diff options
author | Paul Frazee <pfrazee@gmail.com> | 2024-04-12 14:13:13 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-12 14:13:13 -0700 |
commit | ec5c4929c1c5677d22c923193ce04f3d69b72711 (patch) | |
tree | ccc097ea1565ae506e522a76a019bfeb6a63faf3 /src/view | |
parent | 44039c68d678e99f9dc712f1a6dae87aed970ca3 (diff) | |
download | voidsky-ec5c4929c1c5677d22c923193ce04f3d69b72711.tar.zst |
PWI improvements (#3489)
* Enable home and feeds on the PWI * Add global SigninDialog to drive useRequireAuth() * Tweak desktop styles * Make the logo in leftnav PWI a clickable home link * Add label * Improve dialog on web * Fix query key * Go to home after signout from settings screen * Filter out feeds from the discover listing for logged out users which are known to break without auth * Update profile header follow/subscribe to give signin prompt * Show profile feeds tabs on pwi * Add language selector to account creation footer and pwi left nav desktop --------- Co-authored-by: dan <dan.abramov@gmail.com>
Diffstat (limited to 'src/view')
-rw-r--r-- | src/view/com/auth/HomeLoggedOutCTA.tsx | 170 | ||||
-rw-r--r-- | src/view/com/auth/SplashScreen.tsx | 58 | ||||
-rw-r--r-- | src/view/com/auth/SplashScreen.web.tsx | 56 | ||||
-rw-r--r-- | src/view/com/home/HomeHeaderLayout.web.tsx | 63 | ||||
-rw-r--r-- | src/view/com/home/HomeHeaderLayoutMobile.tsx | 44 | ||||
-rw-r--r-- | src/view/screens/Feeds.tsx | 89 | ||||
-rw-r--r-- | src/view/screens/Home.tsx | 9 | ||||
-rw-r--r-- | src/view/screens/Profile.tsx | 3 | ||||
-rw-r--r-- | src/view/screens/Settings/index.tsx | 10 | ||||
-rw-r--r-- | src/view/shell/Drawer.tsx | 56 | ||||
-rw-r--r-- | src/view/shell/NavSignupCard.tsx | 19 | ||||
-rw-r--r-- | src/view/shell/index.tsx | 39 | ||||
-rw-r--r-- | src/view/shell/index.web.tsx | 28 |
13 files changed, 216 insertions, 428 deletions
diff --git a/src/view/com/auth/HomeLoggedOutCTA.tsx b/src/view/com/auth/HomeLoggedOutCTA.tsx deleted file mode 100644 index 4c8c35da7..000000000 --- a/src/view/com/auth/HomeLoggedOutCTA.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import React from 'react' -import {StyleSheet, TouchableOpacity, View} from 'react-native' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' - -import {usePalette} from '#/lib/hooks/usePalette' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' -import {colors, s} from '#/lib/styles' -import {useLoggedOutViewControls} from '#/state/shell/logged-out' -import {TextLink} from '../util/Link' -import {Text} from '../util/text/Text' -import {ScrollView} from '../util/Views' - -export function HomeLoggedOutCTA() { - const pal = usePalette('default') - const {_} = useLingui() - const {isMobile} = useWebMediaQueries() - const {requestSwitchToAccount} = useLoggedOutViewControls() - - const showCreateAccount = React.useCallback(() => { - requestSwitchToAccount({requestedAccount: 'new'}) - }, [requestSwitchToAccount]) - - const showSignIn = React.useCallback(() => { - requestSwitchToAccount({requestedAccount: 'none'}) - }, [requestSwitchToAccount]) - - return ( - <ScrollView style={styles.container} testID="loggedOutCTA"> - <View style={[styles.hero, isMobile && styles.heroMobile]}> - <Text style={[styles.title, pal.link]}> - <Trans>Bluesky</Trans> - </Text> - <Text - style={[ - styles.subtitle, - isMobile && styles.subtitleMobile, - pal.textLight, - ]}> - <Trans>See what's next</Trans> - </Text> - </View> - <View - testID="signinOrCreateAccount" - style={isMobile ? undefined : styles.btnsDesktop}> - <TouchableOpacity - testID="createAccountButton" - style={[ - styles.btn, - isMobile && styles.btnMobile, - {backgroundColor: colors.blue3}, - ]} - onPress={showCreateAccount} - accessibilityRole="button" - accessibilityLabel={_(msg`Create new account`)} - accessibilityHint={_( - msg`Opens flow to create a new Bluesky account`, - )}> - <Text - style={[ - s.white, - styles.btnLabel, - isMobile && styles.btnLabelMobile, - ]}> - <Trans>Create a new account</Trans> - </Text> - </TouchableOpacity> - <TouchableOpacity - testID="signInButton" - style={[styles.btn, isMobile && styles.btnMobile, pal.btn]} - onPress={showSignIn} - accessibilityRole="button" - accessibilityLabel={_(msg`Sign in`)} - accessibilityHint={_( - msg`Opens flow to sign into your existing Bluesky account`, - )}> - <Text - style={[ - pal.text, - styles.btnLabel, - isMobile && styles.btnLabelMobile, - ]}> - <Trans>Sign in</Trans> - </Text> - </TouchableOpacity> - </View> - - <View style={[styles.footer, pal.view, pal.border]}> - <TextLink - type="2xl" - href="https://bsky.social" - text={_(msg`Business`)} - style={[styles.footerLink, pal.link]} - /> - <TextLink - type="2xl" - href="https://bsky.social/about/blog" - text={_(msg`Blog`)} - style={[styles.footerLink, pal.link]} - /> - <TextLink - type="2xl" - href="https://bsky.social/about/join" - text={_(msg`Jobs`)} - style={[styles.footerLink, pal.link]} - /> - </View> - </ScrollView> - ) -} - -const styles = StyleSheet.create({ - container: { - height: '100%', - }, - hero: { - justifyContent: 'center', - paddingTop: 100, - paddingBottom: 30, - }, - heroMobile: { - paddingBottom: 50, - }, - title: { - textAlign: 'center', - fontSize: 68, - fontWeight: 'bold', - }, - subtitle: { - textAlign: 'center', - fontSize: 48, - fontWeight: 'bold', - }, - subtitleMobile: { - fontSize: 42, - }, - btnsDesktop: { - flexDirection: 'row', - justifyContent: 'center', - gap: 20, - marginHorizontal: 20, - }, - btn: { - borderRadius: 32, - width: 230, - paddingVertical: 12, - marginBottom: 20, - }, - btnMobile: { - flex: 1, - width: 'auto', - marginHorizontal: 20, - paddingVertical: 16, - }, - btnLabel: { - textAlign: 'center', - fontSize: 18, - }, - btnLabelMobile: { - textAlign: 'center', - fontSize: 21, - }, - - footer: { - flexDirection: 'row', - gap: 20, - justifyContent: 'center', - }, - footerLink: {}, -}) diff --git a/src/view/com/auth/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx index 763b01dfa..8eac1ab82 100644 --- a/src/view/com/auth/SplashScreen.tsx +++ b/src/view/com/auth/SplashScreen.tsx @@ -1,19 +1,15 @@ import React from 'react' import {View} from 'react-native' -import RNPickerSelect, {PickerSelectProps} from 'react-native-picker-select' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {sanitizeAppLanguageSetting} from '#/locale/helpers' -import {APP_LANGUAGES} from '#/locale/languages' -import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences' import {Logo} from '#/view/icons/Logo' import {Logotype} from '#/view/icons/Logotype' import {ErrorBoundary} from 'view/com/util/ErrorBoundary' import {atoms as a, useTheme} from '#/alf' +import {AppLanguageDropdown} from '#/components/AppLanguageDropdown' import {Button, ButtonText} from '#/components/Button' -import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron' import {Text} from '#/components/Typography' import {CenteredView} from '../util/Views' @@ -27,22 +23,8 @@ export const SplashScreen = ({ const t = useTheme() const {_} = useLingui() - const langPrefs = useLanguagePrefs() - const setLangPrefs = useLanguagePrefsApi() const insets = useSafeAreaInsets() - const sanitizedLang = sanitizeAppLanguageSetting(langPrefs.appLanguage) - - const onChangeAppLanguage = React.useCallback( - (value: Parameters<PickerSelectProps['onValueChange']>[0]) => { - if (!value) return - if (sanitizedLang !== value) { - setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value)) - } - }, - [sanitizedLang, setLangPrefs], - ) - return ( <CenteredView style={[a.h_full, a.flex_1]}> <ErrorBoundary> @@ -99,43 +81,7 @@ export const SplashScreen = ({ a.justify_center, a.align_center, ]}> - <View style={a.relative}> - <RNPickerSelect - placeholder={{}} - value={sanitizedLang} - onValueChange={onChangeAppLanguage} - items={APP_LANGUAGES.filter(l => Boolean(l.code2)).map(l => ({ - label: l.name, - value: l.code2, - key: l.code2, - }))} - useNativeAndroidPickerStyle={false} - style={{ - inputAndroid: { - color: t.atoms.text_contrast_medium.color, - fontSize: 16, - paddingRight: 12 + 4, - }, - inputIOS: { - color: t.atoms.text.color, - fontSize: 16, - paddingRight: 12 + 4, - }, - }} - /> - - <View - style={[ - a.absolute, - a.inset_0, - {left: 'auto'}, - {pointerEvents: 'none'}, - a.align_center, - a.justify_center, - ]}> - <ChevronDown fill={t.atoms.text.color} size="xs" /> - </View> - </View> + <AppLanguageDropdown /> </View> <View style={{height: insets.bottom}} /> </ErrorBoundary> diff --git a/src/view/com/auth/SplashScreen.web.tsx b/src/view/com/auth/SplashScreen.web.tsx index 7a2ee16cf..f905e1e8d 100644 --- a/src/view/com/auth/SplashScreen.web.tsx +++ b/src/view/com/auth/SplashScreen.web.tsx @@ -4,16 +4,13 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {sanitizeAppLanguageSetting} from '#/locale/helpers' -import {APP_LANGUAGES} from '#/locale/languages' -import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {Logo} from '#/view/icons/Logo' import {Logotype} from '#/view/icons/Logotype' import {ErrorBoundary} from 'view/com/util/ErrorBoundary' import {atoms as a, useTheme} from '#/alf' +import {AppLanguageDropdown} from '#/components/AppLanguageDropdown' import {Button, ButtonText} from '#/components/Button' -import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron' import {InlineLinkText} from '#/components/Link' import {Text} from '#/components/Typography' import {CenteredView} from '../util/Views' @@ -131,23 +128,6 @@ export const SplashScreen = ({ function Footer() { const t = useTheme() - const langPrefs = useLanguagePrefs() - const setLangPrefs = useLanguagePrefsApi() - - const sanitizedLang = sanitizeAppLanguageSetting(langPrefs.appLanguage) - - const onChangeAppLanguage = React.useCallback( - (ev: React.ChangeEvent<HTMLSelectElement>) => { - const value = ev.target.value - - if (!value) return - if (sanitizedLang !== value) { - setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value)) - } - }, - [sanitizedLang, setLangPrefs], - ) - return ( <View style={[ @@ -174,39 +154,7 @@ function Footer() { <View style={a.flex_1} /> - <View style={[a.flex_row, a.gap_sm, a.align_center, a.flex_shrink]}> - <Text aria-hidden={true} style={t.atoms.text_contrast_medium}> - {APP_LANGUAGES.find(l => l.code2 === sanitizedLang)?.name} - </Text> - <ChevronDown - fill={t.atoms.text.color} - size="xs" - style={a.flex_shrink} - /> - - <select - value={sanitizedLang} - onChange={onChangeAppLanguage} - style={{ - cursor: 'pointer', - MozAppearance: 'none', - WebkitAppearance: 'none', - appearance: 'none', - position: 'absolute', - inset: 0, - width: '100%', - color: 'transparent', - background: 'transparent', - border: 0, - padding: 0, - }}> - {APP_LANGUAGES.filter(l => Boolean(l.code2)).map(l => ( - <option key={l.code2} value={l.code2}> - {l.name} - </option> - ))} - </select> - </View> + <AppLanguageDropdown /> </View> ) } diff --git a/src/view/com/home/HomeHeaderLayout.web.tsx b/src/view/com/home/HomeHeaderLayout.web.tsx index 9818b56f6..644d4cab6 100644 --- a/src/view/com/home/HomeHeaderLayout.web.tsx +++ b/src/view/com/home/HomeHeaderLayout.web.tsx @@ -1,20 +1,22 @@ import React from 'react' import {StyleSheet, View} from 'react-native' import Animated from 'react-native-reanimated' -import {usePalette} from 'lib/hooks/usePalette' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {HomeHeaderLayoutMobile} from './HomeHeaderLayoutMobile' -import {Logo} from '#/view/icons/Logo' -import {Link} from '../util/Link' import { FontAwesomeIcon, FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' -import {useLingui} from '@lingui/react' import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + import {CogIcon} from '#/lib/icons' -import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' +import {useSession} from '#/state/session' import {useShellLayout} from '#/state/shell/shell-layout' +import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {Logo} from '#/view/icons/Logo' +import {Link} from '../util/Link' +import {HomeHeaderLayoutMobile} from './HomeHeaderLayoutMobile' export function HomeHeaderLayout(props: { children: React.ReactNode @@ -38,32 +40,35 @@ function HomeHeaderLayoutDesktopAndTablet({ const pal = usePalette('default') const {headerMinimalShellTransform} = useMinimalShellMode() const {headerHeight} = useShellLayout() + const {hasSession} = useSession() const {_} = useLingui() return ( <> - <View style={[pal.view, pal.border, styles.bar, styles.topBar]}> - <Link - href="/settings/following-feed" - hitSlop={10} - accessibilityRole="button" - accessibilityLabel={_(msg`Following Feed Preferences`)} - accessibilityHint=""> - <FontAwesomeIcon - icon="sliders" - style={pal.textLight as FontAwesomeIconStyle} - /> - </Link> - <Logo width={28} /> - <Link - href="/settings/saved-feeds" - hitSlop={10} - accessibilityRole="button" - accessibilityLabel={_(msg`Edit Saved Feeds`)} - accessibilityHint={_(msg`Opens screen to edit Saved Feeds`)}> - <CogIcon size={22} strokeWidth={2} style={pal.textLight} /> - </Link> - </View> + {hasSession && ( + <View style={[pal.view, pal.border, styles.bar, styles.topBar]}> + <Link + href="/settings/following-feed" + hitSlop={10} + accessibilityRole="button" + accessibilityLabel={_(msg`Following Feed Preferences`)} + accessibilityHint=""> + <FontAwesomeIcon + icon="sliders" + style={pal.textLight as FontAwesomeIconStyle} + /> + </Link> + <Logo width={28} /> + <Link + href="/settings/saved-feeds" + hitSlop={10} + accessibilityRole="button" + accessibilityLabel={_(msg`Edit Saved Feeds`)} + accessibilityHint={_(msg`Opens screen to edit Saved Feeds`)}> + <CogIcon size={22} strokeWidth={2} style={pal.textLight} /> + </Link> + </View> + )} {tabBarAnchor} <Animated.View onLayout={e => { diff --git a/src/view/com/home/HomeHeaderLayoutMobile.tsx b/src/view/com/home/HomeHeaderLayoutMobile.tsx index d7b7231c6..78fa9af86 100644 --- a/src/view/com/home/HomeHeaderLayoutMobile.tsx +++ b/src/view/com/home/HomeHeaderLayoutMobile.tsx @@ -1,23 +1,24 @@ import React from 'react' import {StyleSheet, TouchableOpacity, View} from 'react-native' -import {usePalette} from 'lib/hooks/usePalette' -import {Link} from '../util/Link' +import Animated from 'react-native-reanimated' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome' -import {HITSLOP_10} from 'lib/constants' -import Animated from 'react-native-reanimated' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' + +import {useSession} from '#/state/session' import {useSetDrawerOpen} from '#/state/shell/drawer-open' import {useShellLayout} from '#/state/shell/shell-layout' +import {HITSLOP_10} from 'lib/constants' +import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' +import {usePalette} from 'lib/hooks/usePalette' import {isWeb} from 'platform/detection' import {Logo} from '#/view/icons/Logo' - -import {IS_DEV} from '#/env' import {atoms} from '#/alf' -import {Link as Link2} from '#/components/Link' import {ColorPalette_Stroke2_Corner0_Rounded as ColorPalette} from '#/components/icons/ColorPalette' +import {Link as Link2} from '#/components/Link' +import {IS_DEV} from '#/env' +import {Link} from '../util/Link' export function HomeHeaderLayoutMobile({ children, @@ -30,6 +31,7 @@ export function HomeHeaderLayoutMobile({ const setDrawerOpen = useSetDrawerOpen() const {headerHeight} = useShellLayout() const {headerMinimalShellTransform} = useMinimalShellMode() + const {hasSession} = useSession() const onPressAvi = React.useCallback(() => { setDrawerOpen(true) @@ -76,18 +78,20 @@ export function HomeHeaderLayoutMobile({ <ColorPalette size="md" /> </Link2> )} - <Link - testID="viewHeaderHomeFeedPrefsBtn" - href="/settings/following-feed" - hitSlop={HITSLOP_10} - accessibilityRole="button" - accessibilityLabel={_(msg`Following Feed Preferences`)} - accessibilityHint=""> - <FontAwesomeIcon - icon="sliders" - style={pal.textLight as FontAwesomeIconStyle} - /> - </Link> + {hasSession && ( + <Link + testID="viewHeaderHomeFeedPrefsBtn" + href="/settings/following-feed" + hitSlop={HITSLOP_10} + accessibilityRole="button" + accessibilityLabel={_(msg`Following Feed Preferences`)} + accessibilityHint=""> + <FontAwesomeIcon + icon="sliders" + style={pal.textLight as FontAwesomeIconStyle} + /> + </Link> + )} </View> </View> {children} diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx index 2e3bf08db..e64ab08df 100644 --- a/src/view/screens/Feeds.tsx +++ b/src/view/screens/Feeds.tsx @@ -1,52 +1,53 @@ import React from 'react' import { ActivityIndicator, - StyleSheet, - View, type FlatList, Pressable, + StyleSheet, + View, } from 'react-native' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome' -import {ViewHeader} from 'view/com/util/ViewHeader' -import {FAB} from 'view/com/util/fab/FAB' -import {Link} from 'view/com/util/Link' -import {NativeStackScreenProps, FeedsTabNavigatorParams} from 'lib/routes/types' -import {usePalette} from 'lib/hooks/usePalette' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {ComposeIcon2, CogIcon, MagnifyingGlassIcon2} from 'lib/icons' -import {s} from 'lib/styles' -import {atoms as a, useTheme} from '#/alf' -import {SearchInput, SearchInputRef} from 'view/com/util/forms/SearchInput' -import {UserAvatar} from 'view/com/util/UserAvatar' -import { - LoadingPlaceholder, - FeedFeedLoadingPlaceholder, -} from 'view/com/util/LoadingPlaceholder' -import {ErrorMessage} from 'view/com/util/error/ErrorMessage' -import debounce from 'lodash.debounce' -import {Text} from 'view/com/util/text/Text' -import {List} from 'view/com/util/List' -import {useFocusEffect} from '@react-navigation/native' -import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard' -import {Trans, msg} from '@lingui/macro' +import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useSetMinimalShellMode} from '#/state/shell' -import {usePreferencesQuery} from '#/state/queries/preferences' +import {useFocusEffect} from '@react-navigation/native' +import debounce from 'lodash.debounce' + +import {isNative, isWeb} from '#/platform/detection' import { + getAvatarTypeFromUri, useFeedSourceInfoQuery, useGetPopularFeedsQuery, useSearchPopularFeedsMutation, - getAvatarTypeFromUri, } from '#/state/queries/feed' -import {cleanError} from 'lib/strings/errors' -import {useComposerControls} from '#/state/shell/composer' +import {usePreferencesQuery} from '#/state/queries/preferences' import {useSession} from '#/state/session' -import {isNative, isWeb} from '#/platform/detection' +import {useSetMinimalShellMode} from '#/state/shell' +import {useComposerControls} from '#/state/shell/composer' import {HITSLOP_10} from 'lib/constants' +import {usePalette} from 'lib/hooks/usePalette' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {CogIcon, ComposeIcon2, MagnifyingGlassIcon2} from 'lib/icons' +import {FeedsTabNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' +import {cleanError} from 'lib/strings/errors' +import {s} from 'lib/styles' +import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard' +import {ErrorMessage} from 'view/com/util/error/ErrorMessage' +import {FAB} from 'view/com/util/fab/FAB' +import {SearchInput, SearchInputRef} from 'view/com/util/forms/SearchInput' +import {Link} from 'view/com/util/Link' +import {List} from 'view/com/util/List' +import { + FeedFeedLoadingPlaceholder, + LoadingPlaceholder, +} from 'view/com/util/LoadingPlaceholder' +import {Text} from 'view/com/util/text/Text' +import {UserAvatar} from 'view/com/util/UserAvatar' +import {ViewHeader} from 'view/com/util/ViewHeader' +import {atoms as a, useTheme} from '#/alf' import {IconCircle} from '#/components/IconCircle' -import {ListSparkle_Stroke2_Corner0_Rounded} from '#/components/icons/ListSparkle' import {ListMagnifyingGlass_Stroke2_Corner0_Rounded} from '#/components/icons/ListMagnifyingGlass' +import {ListSparkle_Stroke2_Corner0_Rounded} from '#/components/icons/ListSparkle' type Props = NativeStackScreenProps<FeedsTabNavigatorParams, 'Feeds'> @@ -100,6 +101,22 @@ type FlatlistSlice = key: string } +// HACK +// the protocol doesn't yet tell us which feeds are personalized +// this list is used to filter out feed recommendations from logged out users +// for the ones we know need it +// -prf +const KNOWN_AUTHED_ONLY_FEEDS = [ + 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/with-friends', // popular with friends, by bsky.app + 'at://did:plc:tenurhgjptubkk5zf5qhi3og/app.bsky.feed.generator/mutuals', // mutuals, by skyfeed + 'at://did:plc:tenurhgjptubkk5zf5qhi3og/app.bsky.feed.generator/only-posts', // only posts, by skyfeed + 'at://did:plc:wzsilnxf24ehtmmc3gssy5bu/app.bsky.feed.generator/mentions', // mentions, by flicknow + 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/bangers', // my bangers, by jaz + 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/mutuals', // mutuals, by bluesky + 'at://did:plc:q6gjnaw2blty4crticxkmujt/app.bsky.feed.generator/my-followers', // followers, by jaz + 'at://did:plc:vpkhqolt662uhesyj6nxm7ys/app.bsky.feed.generator/followpics', // the gram, by why +] + export function FeedsScreen(_props: Props) { const pal = usePalette('default') const {openComposer} = useComposerControls() @@ -299,7 +316,15 @@ export function FeedsScreen(_props: Props) { for (const page of popularFeeds.pages || []) { slices = slices.concat( page.feeds - .filter(feed => !preferences?.feeds?.saved.includes(feed.uri)) + .filter(feed => { + if ( + !hasSession && + KNOWN_AUTHED_ONLY_FEEDS.includes(feed.uri) + ) { + return false + } + return !preferences?.feeds?.saved.includes(feed.uri) + }) .map(feed => ({ key: `popularFeed:${feed.uri}`, type: 'popularFeed', diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 39bdac669..7a2a88265 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -2,6 +2,7 @@ import React from 'react' import {ActivityIndicator, AppState, StyleSheet, View} from 'react-native' import {useFocusEffect} from '@react-navigation/native' +import {PROD_DEFAULT_FEED} from '#/lib/constants' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {useSetTitle} from '#/lib/hooks/useSetTitle' import {logEvent, LogEvents, useGate} from '#/lib/statsig/statsig' @@ -19,7 +20,6 @@ import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager' import {CustomFeedEmptyState} from 'view/com/posts/CustomFeedEmptyState' import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState' import {FollowingEndOfFeed} from 'view/com/posts/FollowingEndOfFeed' -import {HomeLoggedOutCTA} from '../com/auth/HomeLoggedOutCTA' import {HomeHeader} from '../com/home/HomeHeader' type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'> @@ -231,7 +231,12 @@ function HomeScreenReady({ onPageSelected={onPageSelected} onPageScrollStateChanged={onPageScrollStateChanged} renderTabBar={renderTabBar}> - <HomeLoggedOutCTA /> + <FeedPage + testID="customFeedPage" + isPageFocused + feed={`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`} + renderEmptyState={renderCustomFeedEmptyState} + /> </Pager> ) } diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index c391f8050..f71e1330e 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -184,8 +184,7 @@ function ProfileScreenLoaded({ const showRepliesTab = hasSession const showMediaTab = !hasLabeler const showLikesTab = isMe - const showFeedsTab = - hasSession && (isMe || (profile.associated?.feedgens || 0) > 0) + const showFeedsTab = isMe || (profile.associated?.feedgens || 0) > 0 const showListsTab = hasSession && (isMe || (profile.associated?.lists || 0) > 0) diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx index 8a7fa5e71..b97faafad 100644 --- a/src/view/screens/Settings/index.tsx +++ b/src/view/screens/Settings/index.tsx @@ -71,6 +71,7 @@ import {UserAvatar} from 'view/com/util/UserAvatar' import {ScrollView} from 'view/com/util/Views' import {useDialogControl} from '#/components/Dialog' import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings' +import {navigate, resetToTab} from '#/Navigation' import {ExportCarDialog} from './ExportCarDialog' function SettingsAccountCard({account}: {account: SessionAccount}) { @@ -104,7 +105,14 @@ function SettingsAccountCard({account}: {account: SessionAccount}) { <TouchableOpacity testID="signOutBtn" onPress={() => { - logout('Settings') + if (isNative) { + logout('Settings') + resetToTab('HomeTab') + } else { + navigate('Home').then(() => { + logout('Settings') + }) + } }} accessibilityRole="button" accessibilityLabel={_(msg`Sign out`)} diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx index 1bf5647f6..a7342179d 100644 --- a/src/view/shell/Drawer.tsx +++ b/src/view/shell/Drawer.tsx @@ -9,49 +9,49 @@ import { View, ViewStyle, } from 'react-native' -import {useNavigation, StackActions} from '@react-navigation/native' import { FontAwesomeIcon, FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' -import {s, colors} from 'lib/styles' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {StackActions, useNavigation} from '@react-navigation/native' + +import {emitSoftReset} from '#/state/events' +import {useUnreadNotifications} from '#/state/queries/notifications/unread' +import {useProfileQuery} from '#/state/queries/profile' +import {SessionAccount, useSession} from '#/state/session' +import {useSetDrawerOpen} from '#/state/shell' +import {useAnalytics} from 'lib/analytics/analytics' import {FEEDBACK_FORM_URL, HELP_DESK_URL} from 'lib/constants' +import {useNavigationTabState} from 'lib/hooks/useNavigationTabState' +import {usePalette} from 'lib/hooks/usePalette' import { - HomeIcon, - HomeIconSolid, BellIcon, BellIconSolid, - UserIcon, CogIcon, + HandIcon, + HashtagIcon, + HomeIcon, + HomeIconSolid, + ListIcon, MagnifyingGlassIcon2, MagnifyingGlassIcon2Solid, + UserIcon, UserIconSolid, - HashtagIcon, - ListIcon, - HandIcon, } from 'lib/icons' -import {UserAvatar} from 'view/com/util/UserAvatar' -import {Text} from 'view/com/util/text/Text' -import {useTheme} from 'lib/ThemeContext' -import {usePalette} from 'lib/hooks/usePalette' -import {useAnalytics} from 'lib/analytics/analytics' -import {pluralize} from 'lib/strings/helpers' import {getTabState, TabState} from 'lib/routes/helpers' import {NavigationProp} from 'lib/routes/types' -import {useNavigationTabState} from 'lib/hooks/useNavigationTabState' +import {pluralize} from 'lib/strings/helpers' +import {colors, s} from 'lib/styles' +import {useTheme} from 'lib/ThemeContext' import {isWeb} from 'platform/detection' -import {formatCountShortOnly} from 'view/com/util/numeric/format' -import {Trans, msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' -import {useSetDrawerOpen} from '#/state/shell' -import {useSession, SessionAccount} from '#/state/session' -import {useProfileQuery} from '#/state/queries/profile' -import {useUnreadNotifications} from '#/state/queries/notifications/unread' -import {emitSoftReset} from '#/state/events' import {NavSignupCard} from '#/view/shell/NavSignupCard' -import {TextLink} from '../com/util/Link' - +import {formatCountShortOnly} from 'view/com/util/numeric/format' +import {Text} from 'view/com/util/text/Text' +import {UserAvatar} from 'view/com/util/UserAvatar' import {useTheme as useAlfTheme} from '#/alf' +import {TextLink} from '../com/util/Link' let DrawerProfileCard = ({ account, @@ -246,7 +246,11 @@ let DrawerContent = ({}: {}): React.ReactNode => { <SettingsMenuItem onPress={onPressSettings} /> </> ) : ( - <SearchMenuItem isActive={isAtSearch} onPress={onPressSearch} /> + <> + <HomeMenuItem isActive={isAtHome} onPress={onPressHome} /> + <FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} /> + <SearchMenuItem isActive={isAtSearch} onPress={onPressSearch} /> + </> )} <View style={styles.smallSpacer} /> diff --git a/src/view/shell/NavSignupCard.tsx b/src/view/shell/NavSignupCard.tsx index 83d141498..aa807f0cc 100644 --- a/src/view/shell/NavSignupCard.tsx +++ b/src/view/shell/NavSignupCard.tsx @@ -3,13 +3,16 @@ import {View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {s} from 'lib/styles' -import {usePalette} from 'lib/hooks/usePalette' -import {Text} from '#/view/com/util/text/Text' -import {Button} from '#/view/com/util/forms/Button' import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useCloseAllActiveElements} from '#/state/util' +import {usePalette} from 'lib/hooks/usePalette' +import {s} from 'lib/styles' +import {Button} from '#/view/com/util/forms/Button' +import {Text} from '#/view/com/util/text/Text' import {Logo} from '#/view/icons/Logo' +import {atoms as a} from '#/alf' +import {AppLanguageDropdown} from '#/components/AppLanguageDropdown' +import {Link} from '#/components/Link' let NavSignupCard = ({}: {}): React.ReactNode => { const {_} = useLingui() @@ -35,7 +38,9 @@ let NavSignupCard = ({}: {}): React.ReactNode => { paddingTop: 6, marginBottom: 24, }}> - <Logo width={48} /> + <Link to="/" label="Bluesky - Home"> + <Logo width={48} /> + </Link> <View style={{paddingTop: 18}}> <Text type="md-bold" style={[pal.text]}> @@ -62,6 +67,10 @@ let NavSignupCard = ({}: {}): React.ReactNode => { </Text> </Button> </View> + + <View style={[a.pt_2xl, a.w_full]}> + <AppLanguageDropdown /> + </View> </View> ) } diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx index f29183095..c554112ed 100644 --- a/src/view/shell/index.tsx +++ b/src/view/shell/index.tsx @@ -1,37 +1,39 @@ import React from 'react' -import {StatusBar} from 'expo-status-bar' import { + BackHandler, DimensionValue, StyleSheet, useWindowDimensions, View, - BackHandler, } from 'react-native' -import {useSafeAreaInsets} from 'react-native-safe-area-context' import {Drawer} from 'react-native-drawer-layout' +import Animated from 'react-native-reanimated' +import {useSafeAreaInsets} from 'react-native-safe-area-context' +import {StatusBar} from 'expo-status-bar' import {useNavigationState} from '@react-navigation/native' -import {ModalsContainer} from 'view/com/modals/Modal' -import {Lightbox} from 'view/com/lightbox/Lightbox' -import {ErrorBoundary} from 'view/com/util/ErrorBoundary' -import {DrawerContent} from './Drawer' -import {Composer} from './Composer' -import {useTheme} from 'lib/ThemeContext' -import {usePalette} from 'lib/hooks/usePalette' -import {RoutesContainer, TabsNavigator} from '../../Navigation' -import {isStateAtTabRoot} from 'lib/routes/helpers' + +import {useSession} from '#/state/session' import { useIsDrawerOpen, - useSetDrawerOpen, useIsDrawerSwipeDisabled, + useSetDrawerOpen, } from '#/state/shell' -import {isAndroid} from 'platform/detection' -import {useSession} from '#/state/session' import {useCloseAnyActiveElement} from '#/state/util' +import {usePalette} from 'lib/hooks/usePalette' import * as notifications from 'lib/notifications/notifications' -import {Outlet as PortalOutlet} from '#/components/Portal' -import {MutedWordsDialog} from '#/components/dialogs/MutedWords' +import {isStateAtTabRoot} from 'lib/routes/helpers' +import {useTheme} from 'lib/ThemeContext' +import {isAndroid} from 'platform/detection' import {useDialogStateContext} from 'state/dialogs' -import Animated from 'react-native-reanimated' +import {Lightbox} from 'view/com/lightbox/Lightbox' +import {ModalsContainer} from 'view/com/modals/Modal' +import {ErrorBoundary} from 'view/com/util/ErrorBoundary' +import {MutedWordsDialog} from '#/components/dialogs/MutedWords' +import {SigninDialog} from '#/components/dialogs/Signin' +import {Outlet as PortalOutlet} from '#/components/Portal' +import {RoutesContainer, TabsNavigator} from '../../Navigation' +import {Composer} from './Composer' +import {DrawerContent} from './Drawer' function ShellInner() { const isDrawerOpen = useIsDrawerOpen() @@ -101,6 +103,7 @@ function ShellInner() { <Composer winHeight={winDim.height} /> <ModalsContainer /> <MutedWordsDialog /> + <SigninDialog /> <Lightbox /> <PortalOutlet /> </> diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx index 02993ac46..51fb4a0a1 100644 --- a/src/view/shell/index.web.tsx +++ b/src/view/shell/index.web.tsx @@ -1,24 +1,25 @@ import React, {useEffect} from 'react' -import {View, StyleSheet, TouchableOpacity} from 'react-native' -import {useNavigation} from '@react-navigation/native' +import {StyleSheet, TouchableOpacity, View} from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useNavigation} from '@react-navigation/native' -import {ErrorBoundary} from '../com/util/ErrorBoundary' +import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock' +import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell' +import {useCloseAllActiveElements} from '#/state/util' +import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle' +import {NavigationProp} from 'lib/routes/types' +import {colors, s} from 'lib/styles' +import {MutedWordsDialog} from '#/components/dialogs/MutedWords' +import {SigninDialog} from '#/components/dialogs/Signin' +import {Outlet as PortalOutlet} from '#/components/Portal' +import {useWebMediaQueries} from '../../lib/hooks/useWebMediaQueries' +import {FlatNavigator, RoutesContainer} from '../../Navigation' import {Lightbox} from '../com/lightbox/Lightbox' import {ModalsContainer} from '../com/modals/Modal' +import {ErrorBoundary} from '../com/util/ErrorBoundary' import {Composer} from './Composer.web' -import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle' -import {s, colors} from 'lib/styles' -import {RoutesContainer, FlatNavigator} from '../../Navigation' import {DrawerContent} from './Drawer' -import {useWebMediaQueries} from '../../lib/hooks/useWebMediaQueries' -import {NavigationProp} from 'lib/routes/types' -import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell' -import {useCloseAllActiveElements} from '#/state/util' -import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock' -import {Outlet as PortalOutlet} from '#/components/Portal' -import {MutedWordsDialog} from '#/components/dialogs/MutedWords' function ShellInner() { const isDrawerOpen = useIsDrawerOpen() @@ -45,6 +46,7 @@ function ShellInner() { <Composer winHeight={0} /> <ModalsContainer /> <MutedWordsDialog /> + <SigninDialog /> <Lightbox /> <PortalOutlet /> |