diff options
Diffstat (limited to 'src/view')
20 files changed, 327 insertions, 348 deletions
diff --git a/src/view/com/auth/create/CreateAccount.tsx b/src/view/com/auth/create/CreateAccount.tsx index 8aefffa6d..d193802fe 100644 --- a/src/view/com/auth/create/CreateAccount.tsx +++ b/src/view/com/auth/create/CreateAccount.tsx @@ -23,7 +23,7 @@ import {Step3} from './Step3' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {TextLink} from '../../util/Link' import {getAgent} from 'state/session' -import {createFullHandle} from 'lib/strings/handles' +import {createFullHandle, validateHandle} from 'lib/strings/handles' export function CreateAccount({onPressBack}: {onPressBack: () => void}) { const {screen} = useAnalytics() @@ -78,6 +78,10 @@ export function CreateAccount({onPressBack}: {onPressBack: () => void}) { } if (uiState.step === 2) { + if (!validateHandle(uiState.handle, uiState.userDomain).overall) { + return + } + uiDispatch({type: 'set-processing', value: true}) try { const res = await getAgent().resolveHandle({ diff --git a/src/view/com/auth/create/Step2.tsx b/src/view/com/auth/create/Step2.tsx index 87d414bb9..a38920309 100644 --- a/src/view/com/auth/create/Step2.tsx +++ b/src/view/com/auth/create/Step2.tsx @@ -1,15 +1,22 @@ import React from 'react' -import {StyleSheet, View} from 'react-native' +import {View} from 'react-native' import {CreateAccountState, CreateAccountDispatch} from './state' import {Text} from 'view/com/util/text/Text' import {StepHeader} from './StepHeader' import {s} from 'lib/styles' import {TextInput} from '../util/TextInput' -import {createFullHandle} from 'lib/strings/handles' +import { + createFullHandle, + IsValidHandle, + validateHandle, +} from 'lib/strings/handles' import {usePalette} from 'lib/hooks/usePalette' -import {ErrorMessage} from 'view/com/util/error/ErrorMessage' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {atoms as a, useTheme} from '#/alf' +import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' +import {TimesLarge_Stroke2_Corner0_Rounded as Times} from '#/components/icons/Times' +import {useFocusEffect} from '@react-navigation/native' /** STEP 3: Your user handle * @field User handle @@ -23,41 +30,111 @@ export function Step2({ }) { const pal = usePalette('default') const {_} = useLingui() + const t = useTheme() + + const [validCheck, setValidCheck] = React.useState<IsValidHandle>({ + handleChars: false, + frontLength: false, + totalLength: true, + overall: false, + }) + + useFocusEffect( + React.useCallback(() => { + setValidCheck(validateHandle(uiState.handle, uiState.userDomain)) + + // Disabling this, because we only want to run this when we focus the screen + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []), + ) + + const onHandleChange = React.useCallback( + (value: string) => { + if (uiState.error) { + uiDispatch({type: 'set-error', value: ''}) + } + + setValidCheck(validateHandle(value, uiState.userDomain)) + uiDispatch({type: 'set-handle', value}) + }, + [uiDispatch, uiState.error, uiState.userDomain], + ) + return ( <View> <StepHeader uiState={uiState} title={_(msg`Your user handle`)} /> - {uiState.error ? ( - <ErrorMessage message={uiState.error} style={styles.error} /> - ) : undefined} <View style={s.pb10}> - <TextInput - testID="handleInput" - icon="at" - placeholder="e.g. alice" - value={uiState.handle} - editable - autoFocus - autoComplete="off" - autoCorrect={false} - onChange={value => uiDispatch({type: 'set-handle', value})} - // TODO: Add explicit text label - accessibilityLabel={_(msg`User handle`)} - accessibilityHint={_(msg`Input your user handle`)} - /> - <Text type="lg" style={[pal.text, s.pl5, s.pt10]}> - <Trans>Your full handle will be</Trans>{' '} - <Text type="lg-bold" style={pal.text}> - @{createFullHandle(uiState.handle, uiState.userDomain)} + <View style={s.mb20}> + <TextInput + testID="handleInput" + icon="at" + placeholder="e.g. alice" + value={uiState.handle} + editable + autoFocus + autoComplete="off" + autoCorrect={false} + onChange={onHandleChange} + // TODO: Add explicit text label + accessibilityLabel={_(msg`User handle`)} + accessibilityHint={_(msg`Input your user handle`)} + /> + <Text type="lg" style={[pal.text, s.pl5, s.pt10]}> + <Trans>Your full handle will be</Trans>{' '} + <Text type="lg-bold" style={pal.text}> + @{createFullHandle(uiState.handle, uiState.userDomain)} + </Text> </Text> - </Text> + </View> + <View + style={[ + a.w_full, + a.rounded_sm, + a.border, + a.p_md, + a.gap_sm, + t.atoms.border_contrast_low, + ]}> + {uiState.error ? ( + <View style={[a.w_full, a.flex_row, a.align_center, a.gap_sm]}> + <IsValidIcon valid={false} /> + <Text style={[t.atoms.text, a.text_md, a.flex]}> + {uiState.error} + </Text> + </View> + ) : undefined} + <View style={[a.w_full, a.flex_row, a.align_center, a.gap_sm]}> + <IsValidIcon valid={validCheck.handleChars} /> + <Text style={[t.atoms.text, a.text_md, a.flex]}> + <Trans>May only contain letters and numbers</Trans> + </Text> + </View> + <View style={[a.w_full, a.flex_row, a.align_center, a.gap_sm]}> + <IsValidIcon + valid={validCheck.frontLength && validCheck.totalLength} + /> + {!validCheck.totalLength ? ( + <Text style={[t.atoms.text]}> + <Trans>May not be longer than 253 characters</Trans> + </Text> + ) : ( + <Text style={[t.atoms.text, a.text_md]}> + <Trans>Must be at least 3 characters</Trans> + </Text> + )} + </View> + </View> </View> </View> ) } -const styles = StyleSheet.create({ - error: { - borderRadius: 6, - marginBottom: 10, - }, -}) +function IsValidIcon({valid}: {valid: boolean}) { + const t = useTheme() + + if (!valid) { + return <Check size="md" style={{color: t.palette.negative_500}} /> + } + + return <Times size="md" style={{color: t.palette.positive_700}} /> +} diff --git a/src/view/com/auth/create/state.ts b/src/view/com/auth/create/state.ts index 68cfaceec..7a727ec0b 100644 --- a/src/view/com/auth/create/state.ts +++ b/src/view/com/auth/create/state.ts @@ -8,7 +8,7 @@ import {msg} from '@lingui/macro' import * as EmailValidator from 'email-validator' import {getAge} from 'lib/strings/time' import {logger} from '#/logger' -import {createFullHandle} from '#/lib/strings/handles' +import {createFullHandle, validateHandle} from '#/lib/strings/handles' import {cleanError} from '#/lib/strings/errors' import {useOnboardingDispatch} from '#/state/shell/onboarding' import {useSessionApi} from '#/state/session' @@ -282,7 +282,8 @@ function compute(state: CreateAccountState): CreateAccountState { !!state.email && !!state.password } else if (state.step === 2) { - canNext = !!state.handle + canNext = + !!state.handle && validateHandle(state.handle, state.userDomain).overall } else if (state.step === 3) { // Step 3 will automatically redirect as soon as the captcha completes canNext = false diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx index d8da569b1..60814e837 100644 --- a/src/view/com/feeds/FeedPage.tsx +++ b/src/view/com/feeds/FeedPage.tsx @@ -138,7 +138,7 @@ export function FeedPage({ {hasSession && ( <TextLink type="title-lg" - href="/settings/home-feed" + href="/settings/following-feed" style={{fontWeight: 'bold'}} accessibilityLabel={_(msg`Feed Preferences`)} accessibilityHint="" diff --git a/src/view/com/home/HomeHeader.tsx b/src/view/com/home/HomeHeader.tsx new file mode 100644 index 000000000..5ffa31f39 --- /dev/null +++ b/src/view/com/home/HomeHeader.tsx @@ -0,0 +1,71 @@ +import React from 'react' +import {RenderTabBarFnProps} from 'view/com/pager/Pager' +import {HomeHeaderLayout} from './HomeHeaderLayout' +import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {usePinnedFeedsInfos} from '#/state/queries/feed' +import {useNavigation} from '@react-navigation/native' +import {NavigationProp} from 'lib/routes/types' +import {isWeb} from 'platform/detection' +import {TabBar} from '../pager/TabBar' +import {usePalette} from '#/lib/hooks/usePalette' + +export function HomeHeader( + props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void}, +) { + const {isDesktop} = useWebMediaQueries() + if (isDesktop) { + return null + } + return <HomeHeaderInner {...props} /> +} + +export function HomeHeaderInner( + props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void}, +) { + const navigation = useNavigation<NavigationProp>() + const {feeds, hasPinnedCustom} = usePinnedFeedsInfos() + const pal = usePalette('default') + + const items = React.useMemo(() => { + const pinnedNames = feeds.map(f => f.displayName) + + if (!hasPinnedCustom) { + return pinnedNames.concat('Feeds ✨') + } + return pinnedNames + }, [hasPinnedCustom, feeds]) + + const onPressFeedsLink = React.useCallback(() => { + if (isWeb) { + navigation.navigate('Feeds') + } else { + navigation.navigate('FeedsTab') + navigation.popToTop() + } + }, [navigation]) + + const onSelect = React.useCallback( + (index: number) => { + if (!hasPinnedCustom && index === items.length - 1) { + onPressFeedsLink() + } else if (props.onSelect) { + props.onSelect(index) + } + }, + [items.length, onPressFeedsLink, props, hasPinnedCustom], + ) + + return ( + <HomeHeaderLayout> + <TabBar + key={items.join(',')} + onPressSelected={props.onPressSelected} + selectedPage={props.selectedPage} + onSelect={onSelect} + testID={props.testID} + items={items} + indicatorColor={pal.colors.link} + /> + </HomeHeaderLayout> + ) +} diff --git a/src/view/com/home/HomeHeaderLayout.tsx b/src/view/com/home/HomeHeaderLayout.tsx new file mode 100644 index 000000000..70bf064d4 --- /dev/null +++ b/src/view/com/home/HomeHeaderLayout.tsx @@ -0,0 +1 @@ +export {HomeHeaderLayoutMobile as HomeHeaderLayout} from './HomeHeaderLayoutMobile' diff --git a/src/view/com/home/HomeHeaderLayout.web.tsx b/src/view/com/home/HomeHeaderLayout.web.tsx new file mode 100644 index 000000000..47cb00235 --- /dev/null +++ b/src/view/com/home/HomeHeaderLayout.web.tsx @@ -0,0 +1,50 @@ +import React from 'react' +import {StyleSheet} 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 {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' +import {useShellLayout} from '#/state/shell/shell-layout' + +export function HomeHeaderLayout({children}: {children: React.ReactNode}) { + const {isMobile} = useWebMediaQueries() + if (isMobile) { + return <HomeHeaderLayoutMobile>{children}</HomeHeaderLayoutMobile> + } else { + return <HomeHeaderLayoutTablet>{children}</HomeHeaderLayoutTablet> + } +} + +function HomeHeaderLayoutTablet({children}: {children: React.ReactNode}) { + const pal = usePalette('default') + const {headerMinimalShellTransform} = useMinimalShellMode() + const {headerHeight} = useShellLayout() + + return ( + // @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf + <Animated.View + style={[pal.view, pal.border, styles.tabBar, headerMinimalShellTransform]} + onLayout={e => { + headerHeight.value = e.nativeEvent.layout.height + }}> + {children} + </Animated.View> + ) +} + +const styles = StyleSheet.create({ + tabBar: { + // @ts-ignore Web only + position: 'sticky', + zIndex: 1, + // @ts-ignore Web only -prf + left: 'calc(50% - 300px)', + width: 600, + top: 0, + flexDirection: 'row', + alignItems: 'center', + borderLeftWidth: 1, + borderRightWidth: 1, + }, +}) diff --git a/src/view/com/pager/FeedsTabBarMobile.tsx b/src/view/com/home/HomeHeaderLayoutMobile.tsx index 4eba241ae..6c4b911f0 100644 --- a/src/view/com/pager/FeedsTabBarMobile.tsx +++ b/src/view/com/home/HomeHeaderLayoutMobile.tsx @@ -1,7 +1,5 @@ import React from 'react' import {StyleSheet, TouchableOpacity, View} from 'react-native' -import {TabBar} from 'view/com/pager/TabBar' -import {RenderTabBarFnProps} from 'view/com/pager/Pager' import {usePalette} from 'lib/hooks/usePalette' import {Link} from '../util/Link' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' @@ -13,11 +11,7 @@ import {useLingui} from '@lingui/react' import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' import {useSetDrawerOpen} from '#/state/shell/drawer-open' import {useShellLayout} from '#/state/shell/shell-layout' -import {useSession} from '#/state/session' -import {usePinnedFeedsInfos} from '#/state/queries/feed' import {isWeb} from 'platform/detection' -import {useNavigation} from '@react-navigation/native' -import {NavigationProp} from 'lib/routes/types' import {Logo} from '#/view/icons/Logo' import {IS_DEV} from '#/env' @@ -25,49 +19,17 @@ import {atoms} from '#/alf' import {Link as Link2} from '#/components/Link' import {ColorPalette_Stroke2_Corner0_Rounded as ColorPalette} from '#/components/icons/ColorPalette' -export function FeedsTabBar( - props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void}, -) { +export function HomeHeaderLayoutMobile({ + children, +}: { + children: React.ReactNode +}) { const pal = usePalette('default') - const {hasSession} = useSession() const {_} = useLingui() const setDrawerOpen = useSetDrawerOpen() - const navigation = useNavigation<NavigationProp>() - const {feeds, hasPinnedCustom} = usePinnedFeedsInfos() const {headerHeight} = useShellLayout() const {headerMinimalShellTransform} = useMinimalShellMode() - const items = React.useMemo(() => { - if (!hasSession) return [] - - const pinnedNames = feeds.map(f => f.displayName) - - if (!hasPinnedCustom) { - return pinnedNames.concat('Feeds ✨') - } - return pinnedNames - }, [hasSession, hasPinnedCustom, feeds]) - - const onPressFeedsLink = React.useCallback(() => { - if (isWeb) { - navigation.navigate('Feeds') - } else { - navigation.navigate('FeedsTab') - navigation.popToTop() - } - }, [navigation]) - - const onSelect = React.useCallback( - (index: number) => { - if (hasSession && !hasPinnedCustom && index === items.length - 1) { - onPressFeedsLink() - } else if (props.onSelect) { - props.onSelect(index) - } - }, - [items.length, onPressFeedsLink, props, hasSession, hasPinnedCustom], - ) - const onPressAvi = React.useCallback(() => { setDrawerOpen(true) }, [setDrawerOpen]) @@ -113,35 +75,21 @@ export function FeedsTabBar( <ColorPalette size="md" /> </Link2> )} - - {hasSession && ( - <Link - testID="viewHeaderHomeFeedPrefsBtn" - href="/settings/home-feed" - hitSlop={HITSLOP_10} - accessibilityRole="button" - accessibilityLabel={_(msg`Home Feed Preferences`)} - accessibilityHint=""> - <FontAwesomeIcon - icon="sliders" - style={pal.textLight as FontAwesomeIconStyle} - /> - </Link> - )} + <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> - - {items.length > 0 && ( - <TabBar - key={items.join(',')} - onPressSelected={props.onPressSelected} - selectedPage={props.selectedPage} - onSelect={onSelect} - testID={props.testID} - items={items} - indicatorColor={pal.colors.link} - /> - )} + {children} </Animated.View> ) } diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx index 003ad61ba..414f98a61 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx @@ -37,6 +37,7 @@ type Props = { onTap: () => void onZoom: (isZoomed: boolean) => void isScrollViewBeingDragged: boolean + showControls: boolean } const ImageItem = ({ imageSrc, diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx index cf4ba71df..383490f4f 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.ios.tsx @@ -37,11 +37,18 @@ type Props = { onTap: () => void onZoom: (scaled: boolean) => void isScrollViewBeingDragged: boolean + showControls: boolean } const AnimatedImage = Animated.createAnimatedComponent(Image) -const ImageItem = ({imageSrc, onTap, onZoom, onRequestClose}: Props) => { +const ImageItem = ({ + imageSrc, + onTap, + onZoom, + onRequestClose, + showControls, +}: Props) => { const scrollViewRef = useAnimatedRef<Animated.ScrollView>() const translationY = useSharedValue(0) const [loaded, setLoaded] = useState(false) @@ -144,7 +151,7 @@ const ImageItem = ({imageSrc, onTap, onZoom, onRequestClose}: Props) => { accessibilityLabel={imageSrc.alt} accessibilityHint="" onLoad={() => setLoaded(true)} - enableLiveTextInteraction={!scaled} + enableLiveTextInteraction={showControls && !scaled} /> </Animated.ScrollView> </GestureDetector> diff --git a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx index 16688b820..08b99bf9e 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx @@ -10,6 +10,7 @@ type Props = { onTap: () => void onZoom: (scaled: boolean) => void isScrollViewBeingDragged: boolean + showControls: boolean } const ImageItem = (_props: Props) => { diff --git a/src/view/com/lightbox/ImageViewing/index.tsx b/src/view/com/lightbox/ImageViewing/index.tsx index b6835793d..ff8fdb86d 100644 --- a/src/view/com/lightbox/ImageViewing/index.tsx +++ b/src/view/com/lightbox/ImageViewing/index.tsx @@ -122,6 +122,7 @@ function ImageViewing({ imageSrc={imageSrc} onRequestClose={onRequestClose} isScrollViewBeingDragged={isDragging} + showControls={showControls} /> </View> ))} diff --git a/src/view/com/pager/FeedsTabBar.tsx b/src/view/com/pager/FeedsTabBar.tsx deleted file mode 100644 index aa0ba7b24..000000000 --- a/src/view/com/pager/FeedsTabBar.tsx +++ /dev/null @@ -1 +0,0 @@ -export * from './FeedsTabBarMobile' diff --git a/src/view/com/pager/FeedsTabBar.web.tsx b/src/view/com/pager/FeedsTabBar.web.tsx deleted file mode 100644 index fb52b913a..000000000 --- a/src/view/com/pager/FeedsTabBar.web.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import React from 'react' -import {View, StyleSheet} from 'react-native' -import Animated from 'react-native-reanimated' -import {TabBar} from 'view/com/pager/TabBar' -import {RenderTabBarFnProps} from 'view/com/pager/Pager' -import {usePalette} from 'lib/hooks/usePalette' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {FeedsTabBar as FeedsTabBarMobile} from './FeedsTabBarMobile' -import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' -import {useShellLayout} from '#/state/shell/shell-layout' -import {usePinnedFeedsInfos} from '#/state/queries/feed' -import {useSession} from '#/state/session' -import {TextLink} from '#/view/com/util/Link' -import {CenteredView} from '../util/Views' -import {isWeb} from 'platform/detection' -import {useNavigation} from '@react-navigation/native' -import {NavigationProp} from 'lib/routes/types' - -export function FeedsTabBar( - props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void}, -) { - const {isMobile, isTablet} = useWebMediaQueries() - const {hasSession} = useSession() - - if (isMobile) { - return <FeedsTabBarMobile {...props} /> - } else if (isTablet) { - if (hasSession) { - return <FeedsTabBarTablet {...props} /> - } else { - return <FeedsTabBarPublic /> - } - } else { - return null - } -} - -function FeedsTabBarPublic() { - const pal = usePalette('default') - - return ( - <CenteredView sideBorders> - <View - style={[ - pal.view, - { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingHorizontal: 18, - paddingVertical: 12, - }, - ]}> - <TextLink - type="title-lg" - href="/" - style={[pal.text, {fontWeight: 'bold'}]} - text="Bluesky " - /> - </View> - </CenteredView> - ) -} - -function FeedsTabBarTablet( - props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void}, -) { - const {feeds, hasPinnedCustom} = usePinnedFeedsInfos() - const pal = usePalette('default') - const {hasSession} = useSession() - const navigation = useNavigation<NavigationProp>() - const {headerMinimalShellTransform} = useMinimalShellMode() - const {headerHeight} = useShellLayout() - - const items = React.useMemo(() => { - if (!hasSession) return [] - - const pinnedNames = feeds.map(f => f.displayName) - - if (!hasPinnedCustom) { - return pinnedNames.concat('Feeds ✨') - } - return pinnedNames - }, [hasSession, hasPinnedCustom, feeds]) - - const onPressDiscoverFeeds = React.useCallback(() => { - if (isWeb) { - navigation.navigate('Feeds') - } else { - navigation.navigate('FeedsTab') - navigation.popToTop() - } - }, [navigation]) - - const onSelect = React.useCallback( - (index: number) => { - if (hasSession && !hasPinnedCustom && index === items.length - 1) { - onPressDiscoverFeeds() - } else if (props.onSelect) { - props.onSelect(index) - } - }, - [items.length, onPressDiscoverFeeds, props, hasSession, hasPinnedCustom], - ) - - return ( - // @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf - <Animated.View - style={[pal.view, pal.border, styles.tabBar, headerMinimalShellTransform]} - onLayout={e => { - headerHeight.value = e.nativeEvent.layout.height - }}> - <TabBar - key={items.join(',')} - {...props} - onSelect={onSelect} - items={items} - indicatorColor={pal.colors.link} - /> - </Animated.View> - ) -} - -const styles = StyleSheet.create({ - tabBar: { - // @ts-ignore Web only - position: 'sticky', - zIndex: 1, - // @ts-ignore Web only -prf - left: 'calc(50% - 300px)', - width: 600, - top: 0, - flexDirection: 'row', - alignItems: 'center', - borderLeftWidth: 1, - borderRightWidth: 1, - }, -}) diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 2ef1b1447..ebd739839 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -449,7 +449,7 @@ let PostThreadItemLoaded = ({ styles.replyLine, { flexGrow: 1, - backgroundColor: pal.colors.border, + backgroundColor: pal.colors.replyLine, marginBottom: 4, }, ]} @@ -487,7 +487,7 @@ let PostThreadItemLoaded = ({ styles.replyLine, { flexGrow: 1, - backgroundColor: pal.colors.border, + backgroundColor: pal.colors.replyLine, marginTop: 4, }, ]} diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx index e56c88d2c..1dfb687df 100644 --- a/src/view/com/util/forms/PostDropdownBtn.tsx +++ b/src/view/com/util/forms/PostDropdownBtn.tsx @@ -2,6 +2,7 @@ import React, {memo} from 'react' import {StyleProp, View, ViewStyle} from 'react-native' import Clipboard from '@react-native-clipboard/clipboard' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {useNavigation} from '@react-navigation/native' import { AppBskyActorDefs, AppBskyFeedPost, @@ -19,6 +20,8 @@ import * as Toast from '../Toast' import {EventStopper} from '../EventStopper' import {useModalControls} from '#/state/modals' import {makeProfileLink} from '#/lib/routes/links' +import {CommonNavigatorParams} from '#/lib/routes/types' +import {getCurrentRoute} from 'lib/routes/helpers' import {getTranslatorLink} from '#/locale/helpers' import {usePostDeleteMutation} from '#/state/queries/post' import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads' @@ -63,6 +66,7 @@ let PostDropdownBtn = ({ const hiddenPosts = useHiddenPosts() const {hidePost} = useHiddenPostsApi() const openLink = useOpenLink() + const navigation = useNavigation() const rootUri = record.reply?.root?.uri || postUri const isThreadMuted = mutedThreads.includes(rootUri) @@ -82,13 +86,38 @@ let PostDropdownBtn = ({ postDeleteMutation.mutateAsync({uri: postUri}).then( () => { Toast.show(_(msg`Post deleted`)) + + const route = getCurrentRoute(navigation.getState()) + if (route.name === 'PostThread') { + const params = route.params as CommonNavigatorParams['PostThread'] + if ( + currentAccount && + isAuthor && + (params.name === currentAccount.handle || + params.name === currentAccount.did) + ) { + const currentHref = makeProfileLink(postAuthor, 'post', params.rkey) + if (currentHref === href && navigation.canGoBack()) { + navigation.goBack() + } + } + } }, e => { logger.error('Failed to delete post', {message: e}) Toast.show(_(msg`Failed to delete post, please try again`)) }, ) - }, [postUri, postDeleteMutation, _]) + }, [ + navigation, + postUri, + postDeleteMutation, + postAuthor, + currentAccount, + isAuthor, + href, + _, + ]) const onToggleThreadMute = React.useCallback(() => { try { diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index cb2abf1bc..856c237f6 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -6,7 +6,7 @@ import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed' import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState' import {FollowingEndOfFeed} from 'view/com/posts/FollowingEndOfFeed' import {CustomFeedEmptyState} from 'view/com/posts/CustomFeedEmptyState' -import {FeedsTabBar} from '../com/pager/FeedsTabBar' +import {HomeHeader} from '../com/home/HomeHeader' import {Pager, RenderTabBarFnProps, PagerRef} from 'view/com/pager/Pager' import {FeedPage} from 'view/com/feeds/FeedPage' import {HomeLoggedOutCTA} from '../com/auth/HomeLoggedOutCTA' @@ -118,7 +118,7 @@ function HomeScreenReady({ const renderTabBar = React.useCallback( (props: RenderTabBarFnProps) => { return ( - <FeedsTabBar + <HomeHeader key="FEEDS_TAB_BAR" selectedPage={props.selectedPage} onSelect={props.onSelect} diff --git a/src/view/screens/PreferencesHomeFeed.tsx b/src/view/screens/PreferencesFollowingFeed.tsx index 7ad870937..b4acbcd44 100644 --- a/src/view/screens/PreferencesHomeFeed.tsx +++ b/src/view/screens/PreferencesFollowingFeed.tsx @@ -78,9 +78,9 @@ function RepliesThresholdInput({ type Props = NativeStackScreenProps< CommonNavigatorParams, - 'PreferencesHomeFeed' + 'PreferencesFollowingFeed' > -export function PreferencesHomeFeed({navigation}: Props) { +export function PreferencesFollowingFeed({navigation}: Props) { const pal = usePalette('default') const {_} = useLingui() const {isTabletOrDesktop} = useWebMediaQueries() @@ -101,14 +101,14 @@ export function PreferencesHomeFeed({navigation}: Props) { styles.container, isTabletOrDesktop && styles.desktopContainer, ]}> - <ViewHeader title={_(msg`Home Feed Preferences`)} showOnDesktop /> + <ViewHeader title={_(msg`Following Feed Preferences`)} showOnDesktop /> <View style={[ styles.titleSection, isTabletOrDesktop && {paddingTop: 20, paddingBottom: 20}, ]}> <Text type="xl" style={[pal.textLight, styles.description]}> - <Trans>Fine-tune the content you see on your home screen.</Trans> + <Trans>Fine-tune the content you see on your Following feed.</Trans> </Text> </View> @@ -260,7 +260,7 @@ export function PreferencesHomeFeed({navigation}: Props) { <Text style={[pal.text, s.pb10]}> <Trans> Set this setting to "Yes" to show samples of your saved feeds in - your following feed. This is an experimental feature. + your Following feed. This is an experimental feature. </Trans> </Text> <ToggleButton diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx index 9abf0f2bd..00b507a99 100644 --- a/src/view/screens/Settings/index.tsx +++ b/src/view/screens/Settings/index.tsx @@ -241,8 +241,8 @@ export function SettingsScreen({}: Props) { Toast.show(_(msg`Copied build version to clipboard`)) }, [_]) - const openHomeFeedPreferences = React.useCallback(() => { - navigation.navigate('PreferencesHomeFeed') + const openFollowingFeedPreferences = React.useCallback(() => { + navigation.navigate('PreferencesFollowingFeed') }, [navigation]) const openThreadsPreferences = React.useCallback(() => { @@ -529,7 +529,7 @@ export function SettingsScreen({}: Props) { pal.view, isSwitchingAccounts && styles.dimmed, ]} - onPress={openHomeFeedPreferences} + onPress={openFollowingFeedPreferences} accessibilityRole="button" accessibilityHint="" accessibilityLabel={_(msg`Opens the home feed preferences`)}> @@ -540,7 +540,7 @@ export function SettingsScreen({}: Props) { /> </View> <Text type="lg" style={pal.text}> - <Trans>Home Feed Preferences</Trans> + <Trans>Following Feed Preferences</Trans> </Text> </TouchableOpacity> <TouchableOpacity diff --git a/src/view/screens/Storybook/Palette.tsx b/src/view/screens/Storybook/Palette.tsx index b521fe860..900754681 100644 --- a/src/view/screens/Storybook/Palette.tsx +++ b/src/view/screens/Storybook/Palette.tsx @@ -2,99 +2,26 @@ import React from 'react' import {View} from 'react-native' import * as tokens from '#/alf/tokens' -import {atoms as a} from '#/alf' +import {atoms as a, useTheme} from '#/alf' export function Palette() { + const t = useTheme() return ( <View style={[a.gap_md]}> <View style={[a.flex_row, a.gap_md]}> - <View - style={[a.flex_1, {height: 60, backgroundColor: tokens.color.gray_0}]} - /> - <View - style={[ - a.flex_1, - {height: 60, backgroundColor: tokens.color.gray_25}, - ]} - /> - <View - style={[ - a.flex_1, - {height: 60, backgroundColor: tokens.color.gray_50}, - ]} - /> - <View - style={[ - a.flex_1, - {height: 60, backgroundColor: tokens.color.gray_100}, - ]} - /> - <View - style={[ - a.flex_1, - {height: 60, backgroundColor: tokens.color.gray_200}, - ]} - /> - <View - style={[ - a.flex_1, - {height: 60, backgroundColor: tokens.color.gray_300}, - ]} - /> - <View - style={[ - a.flex_1, - {height: 60, backgroundColor: tokens.color.gray_400}, - ]} - /> - <View - style={[ - a.flex_1, - {height: 60, backgroundColor: tokens.color.gray_500}, - ]} - /> - <View - style={[ - a.flex_1, - {height: 60, backgroundColor: tokens.color.gray_600}, - ]} - /> - <View - style={[ - a.flex_1, - {height: 60, backgroundColor: tokens.color.gray_700}, - ]} - /> - <View - style={[ - a.flex_1, - {height: 60, backgroundColor: tokens.color.gray_800}, - ]} - /> - <View - style={[ - a.flex_1, - {height: 60, backgroundColor: tokens.color.gray_900}, - ]} - /> - <View - style={[ - a.flex_1, - {height: 60, backgroundColor: tokens.color.gray_950}, - ]} - /> - <View - style={[ - a.flex_1, - {height: 60, backgroundColor: tokens.color.gray_975}, - ]} - /> - <View - style={[ - a.flex_1, - {height: 60, backgroundColor: tokens.color.gray_1000}, - ]} - /> + <View style={[a.flex_1, t.atoms.bg_contrast_25, {height: 60}]} /> + <View style={[a.flex_1, t.atoms.bg_contrast_50, {height: 60}]} /> + <View style={[a.flex_1, t.atoms.bg_contrast_100, {height: 60}]} /> + <View style={[a.flex_1, t.atoms.bg_contrast_200, {height: 60}]} /> + <View style={[a.flex_1, t.atoms.bg_contrast_300, {height: 60}]} /> + <View style={[a.flex_1, t.atoms.bg_contrast_400, {height: 60}]} /> + <View style={[a.flex_1, t.atoms.bg_contrast_500, {height: 60}]} /> + <View style={[a.flex_1, t.atoms.bg_contrast_600, {height: 60}]} /> + <View style={[a.flex_1, t.atoms.bg_contrast_700, {height: 60}]} /> + <View style={[a.flex_1, t.atoms.bg_contrast_800, {height: 60}]} /> + <View style={[a.flex_1, t.atoms.bg_contrast_900, {height: 60}]} /> + <View style={[a.flex_1, t.atoms.bg_contrast_950, {height: 60}]} /> + <View style={[a.flex_1, t.atoms.bg_contrast_975, {height: 60}]} /> </View> <View style={[a.flex_row, a.gap_md]}> |