Merge branch 'consolidate-list-props' into web-layout

This commit is contained in:
Dan Abramov
2023-12-15 04:27:26 +00:00
60 changed files with 1267 additions and 689 deletions
+165
View File
@@ -0,0 +1,165 @@
import React from 'react'
import {StyleSheet, TouchableOpacity, View} from 'react-native'
import {useLingui} from '@lingui/react'
import {Trans, msg} from '@lingui/macro'
import {ScrollView} from '../util/Views'
import {Text} from '../util/text/Text'
import {usePalette} from '#/lib/hooks/usePalette'
import {colors, s} from '#/lib/styles'
import {TextLink} from '../util/Link'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
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="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="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://blueskyweb.xyz"
text={_(msg`Business`)}
style={[styles.footerLink, pal.link]}
/>
<TextLink
type="2xl"
href="https://blueskyweb.xyz/blog"
text={_(msg`Blog`)}
style={[styles.footerLink, pal.link]}
/>
<TextLink
type="2xl"
href="https://blueskyweb.xyz/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: {},
})
+3 -1
View File
@@ -33,7 +33,9 @@ export function LoggedOut({onDismiss}: {onDismiss?: () => void}) {
const {requestedAccountSwitchTo} = useLoggedOutView()
const [screenState, setScreenState] = React.useState<ScreenState>(
requestedAccountSwitchTo
? ScreenState.S_Login
? requestedAccountSwitchTo === 'new'
? ScreenState.S_CreateAccount
: ScreenState.S_Login
: ScreenState.S_LoginOrCreateAccount,
)
const {isMobile} = useWebMediaQueries()
+11 -5
View File
@@ -7,6 +7,8 @@ import {usePalette} from 'lib/hooks/usePalette'
import {CenteredView} from '../util/Views'
import {Trans, msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {Logo} from '#/view/icons/Logo'
import {Logotype} from '#/view/icons/Logotype'
export const SplashScreen = ({
onPressSignin,
@@ -22,11 +24,14 @@ export const SplashScreen = ({
<CenteredView style={[styles.container, pal.view]}>
<ErrorBoundary>
<View style={styles.hero}>
<Text style={[styles.title, pal.link]}>
<Trans>Bluesky</Trans>
</Text>
<Text style={[styles.subtitle, pal.textLight]}>
<Trans>See what's next</Trans>
<Logo width={92} fill="sky" />
<View style={{paddingTop: 40, paddingBottom: 6}}>
<Logotype width={161} />
</View>
<Text type="lg-medium" style={[pal.textLight]}>
<Trans>What's next?</Trans>
</Text>
</View>
<View testID="signinOrCreateAccount" style={styles.btns}>
@@ -65,6 +70,7 @@ const styles = StyleSheet.create({
hero: {
flex: 2,
justifyContent: 'center',
alignItems: 'center',
},
btns: {
paddingBottom: 40,
+10 -10
View File
@@ -10,6 +10,8 @@ import {CenteredView} from '../util/Views'
import {isWeb} from 'platform/detection'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {Trans} from '@lingui/macro'
import {Logo} from '#/view/icons/Logo'
import {Logotype} from '#/view/icons/Logotype'
export const SplashScreen = ({
onDismiss,
@@ -55,14 +57,15 @@ export const SplashScreen = ({
styles.containerInner,
isMobileWeb && styles.containerInnerMobile,
pal.border,
{alignItems: 'center'},
]}>
<ErrorBoundary>
<Text style={isMobileWeb ? styles.titleMobile : styles.title}>
Bluesky
</Text>
<Text style={isMobileWeb ? styles.subtitleMobile : styles.subtitle}>
See what's next
</Text>
<Logo width={92} fill="sky" />
<View style={{paddingTop: 40, paddingBottom: 20}}>
<Logotype width={161} />
</View>
<View testID="signinOrCreateAccount" style={styles.btns}>
<TouchableOpacity
testID="createAccountButton"
@@ -117,8 +120,6 @@ function Footer({styles}: {styles: ReturnType<typeof useStyles>}) {
)
}
const useStyles = () => {
const {isTabletOrMobile} = useWebMediaQueries()
const isMobileWeb = isWeb && isTabletOrMobile
return StyleSheet.create({
container: {
height: '100%',
@@ -161,8 +162,7 @@ const useStyles = () => {
paddingBottom: 30,
},
btns: {
flexDirection: isMobileWeb ? 'column' : 'row',
gap: 20,
gap: 10,
justifyContent: 'center',
paddingBottom: 40,
},
+4 -19
View File
@@ -1,12 +1,5 @@
import React from 'react'
import {
Dimensions,
RefreshControl,
StyleProp,
StyleSheet,
View,
ViewStyle,
} from 'react-native'
import {Dimensions, StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
import {useQueryClient} from '@tanstack/react-query'
import {List, ListRef} from '../util/List'
import {FeedSourceCardLoaded} from './FeedSourceCard'
@@ -180,22 +173,14 @@ export const ProfileFeedgens = React.forwardRef<
data={items}
keyExtractor={(item: any) => item._reactKey || item.uri}
renderItem={renderItemInner}
refreshControl={
<RefreshControl
refreshing={isPTRing}
onRefresh={onRefresh}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
progressViewOffset={headerOffset}
/>
}
refreshing={isPTRing}
onRefresh={onRefresh}
headerOffset={headerOffset}
contentContainerStyle={{
minHeight: Dimensions.get('window').height * 1.5,
}}
style={{paddingTop: headerOffset}}
indicatorStyle={theme.colorScheme === 'dark' ? 'white' : 'black'}
removeClippedSubviews={true}
contentOffset={{x: 0, y: headerOffset * -1}}
// @ts-ignore our .web version only -prf
desktopFixedHeight
onEndReached={onEndReached}
+3 -14
View File
@@ -2,7 +2,6 @@ import React from 'react'
import {
ActivityIndicator,
Dimensions,
RefreshControl,
StyleProp,
View,
ViewStyle,
@@ -15,7 +14,6 @@ import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
import {ProfileCard} from '../profile/ProfileCard'
import {Button} from '../util/forms/Button'
import {useAnalytics} from 'lib/analytics/analytics'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {useListMembersQuery} from '#/state/queries/list-members'
import {logger} from '#/logger'
@@ -51,7 +49,6 @@ export function ListMembers({
headerOffset?: number
desktopFixedHeightOffset?: number
}) {
const pal = usePalette('default')
const {track} = useAnalytics()
const [isRefreshing, setIsRefreshing] = React.useState(false)
const {isMobile} = useWebMediaQueries()
@@ -215,24 +212,16 @@ export function ListMembers({
renderItem={renderItem}
ListHeaderComponent={renderHeader}
ListFooterComponent={Footer}
refreshControl={
<RefreshControl
refreshing={isRefreshing}
onRefresh={onRefresh}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
progressViewOffset={headerOffset}
/>
}
refreshing={isRefreshing}
onRefresh={onRefresh}
headerOffset={headerOffset}
contentContainerStyle={{
minHeight: Dimensions.get('window').height * 1.5,
}}
style={{paddingTop: headerOffset}}
onScrolledDownChange={onScrolledDownChange}
onEndReached={onEndReached}
onEndReachedThreshold={0.6}
removeClippedSubviews={true}
contentOffset={{x: 0, y: headerOffset * -1}}
// @ts-ignore our .web version only -prf
desktopFixedHeight={desktopFixedHeightOffset || true}
/>
+45 -25
View File
@@ -119,31 +119,51 @@ export function MyLists({
[error, onRefresh, renderItem, pal],
)
const FlatListCom = inline ? RNFlatList : List
return (
<View testID={testID} style={style}>
{items.length > 0 && (
<FlatListCom
testID={testID ? `${testID}-flatlist` : undefined}
data={items}
keyExtractor={item => (item.uri ? item.uri : item._reactKey)}
renderItem={renderItemInner}
refreshControl={
<RefreshControl
refreshing={isPTRing}
onRefresh={onRefresh}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
/>
}
contentContainerStyle={[s.contentContainer]}
removeClippedSubviews={true}
// @ts-ignore our .web version only -prf
desktopFixedHeight
/>
)}
</View>
)
if (inline) {
return (
<View testID={testID} style={style}>
{items.length > 0 && (
<RNFlatList
testID={testID ? `${testID}-flatlist` : undefined}
data={items}
keyExtractor={item => (item.uri ? item.uri : item._reactKey)}
renderItem={renderItemInner}
refreshControl={
<RefreshControl
refreshing={isPTRing}
onRefresh={onRefresh}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
/>
}
contentContainerStyle={[s.contentContainer]}
removeClippedSubviews={true}
// @ts-ignore our .web version only -prf
desktopFixedHeight
/>
)}
</View>
)
} else {
return (
<View testID={testID} style={style}>
{items.length > 0 && (
<List
testID={testID ? `${testID}-flatlist` : undefined}
data={items}
keyExtractor={item => (item.uri ? item.uri : item._reactKey)}
renderItem={renderItemInner}
refreshing={isPTRing}
onRefresh={onRefresh}
contentContainerStyle={[s.contentContainer]}
removeClippedSubviews={true}
// @ts-ignore our .web version only -prf
desktopFixedHeight
/>
)}
</View>
)
}
}
const styles = StyleSheet.create({
+4 -19
View File
@@ -1,12 +1,5 @@
import React from 'react'
import {
Dimensions,
RefreshControl,
StyleProp,
StyleSheet,
View,
ViewStyle,
} from 'react-native'
import {Dimensions, StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
import {useQueryClient} from '@tanstack/react-query'
import {List, ListRef} from '../util/List'
import {ListCard} from './ListCard'
@@ -182,22 +175,14 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>(
data={items}
keyExtractor={(item: any) => item._reactKey}
renderItem={renderItemInner}
refreshControl={
<RefreshControl
refreshing={isPTRing}
onRefresh={onRefresh}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
progressViewOffset={headerOffset}
/>
}
refreshing={isPTRing}
onRefresh={onRefresh}
headerOffset={headerOffset}
contentContainerStyle={{
minHeight: Dimensions.get('window').height * 1.5,
}}
style={{paddingTop: headerOffset}}
indicatorStyle={theme.colorScheme === 'dark' ? 'white' : 'black'}
removeClippedSubviews={true}
contentOffset={{x: 0, y: headerOffset * -1}}
// @ts-ignore our .web version only -prf
desktopFixedHeight
onEndReached={onEndReached}
+3 -11
View File
@@ -1,13 +1,12 @@
import React from 'react'
import {CenteredView} from '../util/Views'
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
import {ActivityIndicator, StyleSheet, View} from 'react-native'
import {FeedItem} from './FeedItem'
import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
import {ErrorMessage} from '../util/error/ErrorMessage'
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
import {EmptyState} from '../util/EmptyState'
import {s} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
import {useNotificationFeedQuery} from '#/state/queries/notifications/feed'
import {useUnreadNotificationsApi} from '#/state/queries/notifications/unread'
import {logger} from '#/logger'
@@ -30,7 +29,6 @@ export function Feed({
onScrolledDownChange: (isScrolledDown: boolean) => void
ListHeaderComponent?: () => JSX.Element
}) {
const pal = usePalette('default')
const [isPTRing, setIsPTRing] = React.useState(false)
const moderationOpts = useModerationOpts()
@@ -152,14 +150,8 @@ export function Feed({
renderItem={renderItem}
ListHeaderComponent={ListHeaderComponent}
ListFooterComponent={FeedFooter}
refreshControl={
<RefreshControl
refreshing={isPTRing}
onRefresh={onRefresh}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
/>
}
refreshing={isPTRing}
onRefresh={onRefresh}
onEndReached={onEndReached}
onEndReachedThreshold={0.6}
onScrolledDownChange={onScrolledDownChange}
+5 -8
View File
@@ -3,12 +3,9 @@ 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 {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
import {Link} from '../util/Link'
import {Text} from '../util/text/Text'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
import {s} from 'lib/styles'
import {HITSLOP_10} from 'lib/constants'
import Animated from 'react-native-reanimated'
import {msg} from '@lingui/macro'
@@ -21,17 +18,17 @@ 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'
export function FeedsTabBar(
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
) {
const pal = usePalette('default')
const {isSandbox, hasSession} = useSession()
const {hasSession} = useSession()
const {_} = useLingui()
const setDrawerOpen = useSetDrawerOpen()
const navigation = useNavigation<NavigationProp>()
const {feeds, hasPinnedCustom} = usePinnedFeedsInfos()
const brandBlue = useColorSchemeStyle(s.brandBlue, s.blue3)
const {headerHeight} = useShellLayout()
const {headerMinimalShellTransform} = useMinimalShellMode()
const pinnedDisplayNames = hasSession ? feeds.map(f => f.displayName) : []
@@ -86,9 +83,9 @@ export function FeedsTabBar(
/>
</TouchableOpacity>
</View>
<Text style={[brandBlue, s.bold, styles.title]}>
{isSandbox ? 'SANDBOX' : 'Bluesky'}
</Text>
<View>
<Logo width={30} />
</View>
<View style={[pal.view, {width: 18}]}>
{hasSession && (
<Link
+3 -11
View File
@@ -1,18 +1,16 @@
import React, {useCallback, useMemo, useState} from 'react'
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
import {ActivityIndicator, StyleSheet, View} from 'react-native'
import {AppBskyFeedGetLikes as GetLikes} from '@atproto/api'
import {CenteredView} from '../util/Views'
import {List} from '../util/List'
import {ErrorMessage} from '../util/error/ErrorMessage'
import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
import {usePalette} from 'lib/hooks/usePalette'
import {logger} from '#/logger'
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
import {usePostLikedByQuery} from '#/state/queries/post-liked-by'
import {cleanError} from '#/lib/strings/errors'
export function PostLikedBy({uri}: {uri: string}) {
const pal = usePalette('default')
const [isPTRing, setIsPTRing] = useState(false)
const {
data: resolvedUri,
@@ -88,14 +86,8 @@ export function PostLikedBy({uri}: {uri: string}) {
<List
data={likes}
keyExtractor={item => item.actor.did}
refreshControl={
<RefreshControl
refreshing={isPTRing}
onRefresh={onRefresh}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
/>
}
refreshing={isPTRing}
onRefresh={onRefresh}
onEndReached={onEndReached}
renderItem={renderItem}
initialNumToRender={15}
+3 -11
View File
@@ -1,18 +1,16 @@
import React, {useMemo, useCallback, useState} from 'react'
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
import {ActivityIndicator, StyleSheet, View} from 'react-native'
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
import {CenteredView} from '../util/Views'
import {List} from '../util/List'
import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
import {ErrorMessage} from '../util/error/ErrorMessage'
import {usePalette} from 'lib/hooks/usePalette'
import {logger} from '#/logger'
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
import {usePostRepostedByQuery} from '#/state/queries/post-reposted-by'
import {cleanError} from '#/lib/strings/errors'
export function PostRepostedBy({uri}: {uri: string}) {
const pal = usePalette('default')
const [isPTRing, setIsPTRing] = useState(false)
const {
data: resolvedUri,
@@ -89,14 +87,8 @@ export function PostRepostedBy({uri}: {uri: string}) {
<List
data={repostedBy}
keyExtractor={item => item.did}
refreshControl={
<RefreshControl
refreshing={isPTRing}
onRefresh={onRefresh}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
/>
}
refreshing={isPTRing}
onRefresh={onRefresh}
onEndReached={onEndReached}
renderItem={renderItem}
initialNumToRender={15}
+16 -13
View File
@@ -2,7 +2,6 @@ import React, {useEffect, useRef} from 'react'
import {
ActivityIndicator,
Pressable,
RefreshControl,
StyleSheet,
TouchableOpacity,
View,
@@ -157,7 +156,9 @@ function PostThreadLoaded({
// construct content
const posts = React.useMemo(() => {
let arr = [TOP_COMPONENT].concat(
Array.from(flattenThreadSkeleton(sortThread(thread, threadViewPrefs))),
Array.from(
flattenThreadSkeleton(sortThread(thread, threadViewPrefs), hasSession),
),
)
if (arr.length > maxVisible) {
arr = arr.slice(0, maxVisible).concat([LOAD_MORE])
@@ -166,7 +167,7 @@ function PostThreadLoaded({
arr.push(BOTTOM_COMPONENT)
}
return arr
}, [thread, maxVisible, threadViewPrefs])
}, [thread, maxVisible, threadViewPrefs, hasSession])
/**
* NOTE
@@ -347,14 +348,8 @@ function PostThreadLoaded({
}
keyExtractor={item => item._reactKey}
renderItem={renderItem}
refreshControl={
<RefreshControl
refreshing={isPTRing}
onRefresh={onPTR}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
/>
}
refreshing={isPTRing}
onRefresh={onPTR}
onContentSizeChange={onContentSizeChange}
style={s.hContentRegion}
// @ts-ignore our .web version only -prf
@@ -468,20 +463,24 @@ function isThreadPost(v: unknown): v is ThreadPost {
function* flattenThreadSkeleton(
node: ThreadNode,
hasSession: boolean,
): Generator<YieldedItem, void> {
if (node.type === 'post') {
if (node.parent) {
yield* flattenThreadSkeleton(node.parent)
yield* flattenThreadSkeleton(node.parent, hasSession)
} else if (node.ctx.isParentLoading) {
yield PARENT_SPINNER
}
if (!hasSession && node.ctx.depth > 0 && hasPwiOptOut(node)) {
return
}
yield node
if (node.ctx.isHighlightedPost && !node.post.viewer?.replyDisabled) {
yield REPLY_PROMPT
}
if (node.replies?.length) {
for (const reply of node.replies) {
yield* flattenThreadSkeleton(reply)
yield* flattenThreadSkeleton(reply, hasSession)
}
} else if (node.ctx.isChildLoading) {
yield CHILD_SPINNER
@@ -493,6 +492,10 @@ function* flattenThreadSkeleton(
}
}
function hasPwiOptOut(node: ThreadPost) {
return !!node.post.author.labels?.find(l => l.val === '!no-unauthenticated')
}
function hasBranchingReplies(node: ThreadNode) {
if (node.type !== 'post') {
return false
+3 -14
View File
@@ -3,7 +3,6 @@ import {
ActivityIndicator,
AppState,
Dimensions,
RefreshControl,
StyleProp,
StyleSheet,
View,
@@ -16,7 +15,6 @@ import {FeedErrorMessage} from './FeedErrorMessage'
import {FeedSlice} from './FeedSlice'
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
import {useAnalytics} from 'lib/analytics/analytics'
import {usePalette} from 'lib/hooks/usePalette'
import {useTheme} from 'lib/ThemeContext'
import {logger} from '#/logger'
import {
@@ -70,7 +68,6 @@ let Feed = ({
ListHeaderComponent?: () => JSX.Element
extraData?: any
}): React.ReactNode => {
const pal = usePalette('default')
const theme = useTheme()
const {track} = useAnalytics()
const queryClient = useQueryClient()
@@ -276,25 +273,17 @@ let Feed = ({
renderItem={renderItem}
ListFooterComponent={FeedFooter}
ListHeaderComponent={ListHeaderComponent}
refreshControl={
<RefreshControl
refreshing={isPTRing}
onRefresh={onRefresh}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
progressViewOffset={headerOffset}
/>
}
refreshing={isPTRing}
onRefresh={onRefresh}
headerOffset={headerOffset}
contentContainerStyle={{
minHeight: Dimensions.get('window').height * 1.5,
}}
style={{paddingTop: headerOffset}}
onScrolledDownChange={onScrolledDownChange}
indicatorStyle={theme.colorScheme === 'dark' ? 'white' : 'black'}
onEndReached={onEndReached}
onEndReachedThreshold={2} // number of posts left to trigger load more
removeClippedSubviews={true}
contentOffset={{x: 0, y: headerOffset * -1}}
extraData={extraData}
// @ts-ignore our .web version only -prf
desktopFixedHeight={
+3
View File
@@ -50,6 +50,9 @@ export function ProfileCard({
return null
}
const moderation = moderateProfile(profile, moderationOpts)
if (moderation.account.filter) {
return null
}
return (
<Link
+3 -11
View File
@@ -1,18 +1,16 @@
import React from 'react'
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
import {ActivityIndicator, StyleSheet, View} from 'react-native'
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
import {CenteredView} from '../util/Views'
import {List} from '../util/List'
import {ErrorMessage} from '../util/error/ErrorMessage'
import {ProfileCardWithFollowBtn} from './ProfileCard'
import {usePalette} from 'lib/hooks/usePalette'
import {useProfileFollowersQuery} from '#/state/queries/profile-followers'
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
import {logger} from '#/logger'
import {cleanError} from '#/lib/strings/errors'
export function ProfileFollowers({name}: {name: string}) {
const pal = usePalette('default')
const [isPTRing, setIsPTRing] = React.useState(false)
const {
data: resolvedDid,
@@ -90,14 +88,8 @@ export function ProfileFollowers({name}: {name: string}) {
<List
data={followers}
keyExtractor={item => item.did}
refreshControl={
<RefreshControl
refreshing={isPTRing}
onRefresh={onRefresh}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
/>
}
refreshing={isPTRing}
onRefresh={onRefresh}
onEndReached={onEndReached}
renderItem={renderItem}
initialNumToRender={15}
+3 -11
View File
@@ -1,18 +1,16 @@
import React from 'react'
import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
import {ActivityIndicator, StyleSheet, View} from 'react-native'
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
import {CenteredView} from '../util/Views'
import {List} from '../util/List'
import {ErrorMessage} from '../util/error/ErrorMessage'
import {ProfileCardWithFollowBtn} from './ProfileCard'
import {usePalette} from 'lib/hooks/usePalette'
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
import {logger} from '#/logger'
import {cleanError} from '#/lib/strings/errors'
export function ProfileFollows({name}: {name: string}) {
const pal = usePalette('default')
const [isPTRing, setIsPTRing] = React.useState(false)
const {
data: resolvedDid,
@@ -90,14 +88,8 @@ export function ProfileFollows({name}: {name: string}) {
<List
data={follows}
keyExtractor={item => item.did}
refreshControl={
<RefreshControl
refreshing={isPTRing}
onRefresh={onRefresh}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
/>
}
refreshing={isPTRing}
onRefresh={onRefresh}
onEndReached={onEndReached}
renderItem={renderItem}
initialNumToRender={15}
+42 -3
View File
@@ -1,27 +1,42 @@
import React, {memo, startTransition} from 'react'
import {FlatListProps} from 'react-native'
import {FlatListProps, RefreshControl} from 'react-native'
import {FlatList_INTERNAL} from './Views'
import {addStyle} from 'lib/styles'
import {useScrollHandlers} from '#/lib/ScrollContext'
import {runOnJS, useSharedValue} from 'react-native-reanimated'
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
import {usePalette} from '#/lib/hooks/usePalette'
export type ListMethods = FlatList_INTERNAL
export type ListProps<ItemT> = Omit<
FlatListProps<ItemT>,
'onScroll' // Use ScrollContext instead.
| 'onScroll' // Use ScrollContext instead.
| 'refreshControl' // Pass refreshing and/or onRefresh instead.
| 'contentOffset' // Pass headerOffset instead.
> & {
onScrolledDownChange?: (isScrolledDown: boolean) => void
headerOffset?: number
refreshing?: boolean
onRefresh?: () => void
}
export type ListRef = React.MutableRefObject<FlatList_INTERNAL | null>
const SCROLLED_DOWN_LIMIT = 200
function ListImpl<ItemT>(
{onScrolledDownChange, ...props}: ListProps<ItemT>,
{
onScrolledDownChange,
refreshing,
onRefresh,
headerOffset,
style,
...props
}: ListProps<ItemT>,
ref: React.Ref<ListMethods>,
) {
const isScrolledDown = useSharedValue(false)
const contextScrollHandlers = useScrollHandlers()
const pal = usePalette('default')
function handleScrolledDownChange(didScrollDown: boolean) {
startTransition(() => {
@@ -49,11 +64,35 @@ function ListImpl<ItemT>(
},
})
let refreshControl
if (refreshing !== undefined || onRefresh !== undefined) {
refreshControl = (
<RefreshControl
refreshing={refreshing ?? false}
onRefresh={onRefresh}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
progressViewOffset={headerOffset}
/>
)
}
let contentOffset
if (headerOffset != null) {
style = addStyle(style, {
paddingTop: headerOffset,
})
contentOffset = {x: 0, y: headerOffset * -1}
}
return (
<FlatList_INTERNAL
{...props}
contentOffset={contentOffset}
refreshControl={refreshControl}
onScroll={scrollHandler}
scrollEventThrottle={1}
style={style}
ref={ref}
/>
)
+12 -8
View File
@@ -7,7 +7,7 @@ import {Text} from '../text/Text'
import {ShieldExclamation} from 'lib/icons'
import {describeModerationCause} from 'lib/moderation'
import {useLingui} from '@lingui/react'
import {msg} from '@lingui/macro'
import {msg, Trans} from '@lingui/macro'
import {useModalControls} from '#/state/modals'
import {isPostMediaBlurred} from 'lib/moderation'
@@ -95,13 +95,17 @@ export function ContentHider({
<Text type="md" style={pal.text}>
{desc.name}
</Text>
{!moderation.noOverride && (
<View style={styles.showBtn}>
<Text type="lg" style={pal.link}>
{override ? 'Hide' : 'Show'}
</Text>
</View>
)}
<View style={styles.showBtn}>
<Text type="lg" style={pal.link}>
{moderation.noOverride ? (
<Trans>Learn more</Trans>
) : override ? (
<Trans>Hide</Trans>
) : (
<Trans>Show</Trans>
)}
</Text>
</View>
</Pressable>
{override && <View style={childContainerStyle}>{children}</View>}
</View>
+41 -23
View File
@@ -22,6 +22,7 @@ import {Trans, msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useModalControls} from '#/state/modals'
import {s} from '#/lib/styles'
import {CenteredView} from '../Views'
export function ScreenHider({
testID,
@@ -53,41 +54,58 @@ export function ScreenHider({
)
}
const isNoPwi =
moderation.cause?.type === 'label' &&
moderation.cause?.labelDef.id === '!no-unauthenticated'
const desc = describeModerationCause(moderation.cause, 'account')
return (
<View style={[styles.container, pal.view, containerStyle]}>
<CenteredView
style={[styles.container, pal.view, containerStyle]}
sideBorders>
<View style={styles.iconContainer}>
<View style={[styles.icon, palInverted.view]}>
<FontAwesomeIcon
icon="exclamation"
icon={isNoPwi ? ['far', 'eye-slash'] : 'exclamation'}
style={pal.textInverted as FontAwesomeIconStyle}
size={24}
/>
</View>
</View>
<Text type="title-2xl" style={[styles.title, pal.text]}>
<Trans>Content Warning</Trans>
{isNoPwi ? (
<Trans>Sign-in Required</Trans>
) : (
<Trans>Content Warning</Trans>
)}
</Text>
<Text type="2xl" style={[styles.description, pal.textLight]}>
<Trans>This {screenDescription} has been flagged:</Trans>
<Text type="2xl-medium" style={[pal.text, s.ml5]}>
{desc.name}.
</Text>
<TouchableWithoutFeedback
onPress={() => {
openModal({
name: 'moderation-details',
context: 'account',
moderation,
})
}}
accessibilityRole="button"
accessibilityLabel={_(msg`Learn more about this warning`)}
accessibilityHint="">
<Text type="2xl" style={pal.link}>
<Trans>Learn More</Trans>
</Text>
</TouchableWithoutFeedback>
{isNoPwi ? (
<Trans>
This account has requested that users sign in to view their profile.
</Trans>
) : (
<>
<Trans>This {screenDescription} has been flagged:</Trans>
<Text type="2xl-medium" style={[pal.text, s.ml5]}>
{desc.name}.
</Text>
<TouchableWithoutFeedback
onPress={() => {
openModal({
name: 'moderation-details',
context: 'account',
moderation,
})
}}
accessibilityRole="button"
accessibilityLabel={_(msg`Learn more about this warning`)}
accessibilityHint="">
<Text type="2xl" style={pal.link}>
<Trans>Learn More</Trans>
</Text>
</TouchableWithoutFeedback>
</>
)}{' '}
</Text>
{isMobile && <View style={styles.spacer} />}
<View style={styles.btnContainer}>
@@ -116,7 +134,7 @@ export function ScreenHider({
</Button>
)}
</View>
</View>
</CenteredView>
)
}
+48
View File
@@ -0,0 +1,48 @@
import React from 'react'
import Svg, {
Path,
Defs,
LinearGradient,
Stop,
SvgProps,
PathProps,
} from 'react-native-svg'
import {colors} from '#/lib/styles'
const ratio = 57 / 64
type Props = {
fill?: PathProps['fill']
} & SvgProps
export const Logo = React.forwardRef(function LogoImpl(props: Props, ref) {
const {fill, ...rest} = props
const gradient = fill === 'sky'
const _fill = gradient ? 'url(#sky)' : fill || colors.blue3
// @ts-ignore it's fiiiiine
const size = parseInt(rest.width || 32)
return (
<Svg
fill="none"
// @ts-ignore it's fiiiiine
ref={ref}
viewBox="0 0 64 57"
{...rest}
style={{width: size, height: size * ratio}}>
{gradient && (
<Defs>
<LinearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<Stop offset="0" stopColor="#0A7AFF" stopOpacity="1" />
<Stop offset="1" stopColor="#59B9FF" stopOpacity="1" />
</LinearGradient>
</Defs>
)}
<Path
fill={_fill}
d="M13.873 3.805C21.21 9.332 29.103 20.537 32 26.55v15.882c0-.338-.13.044-.41.867-1.512 4.456-7.418 21.847-20.923 7.944-7.111-7.32-3.819-14.64 9.125-16.85-7.405 1.264-15.73-.825-18.014-9.015C1.12 23.022 0 8.51 0 6.55 0-3.268 8.579-.182 13.873 3.805ZM50.127 3.805C42.79 9.332 34.897 20.537 32 26.55v15.882c0-.338.13.044.41.867 1.512 4.456 7.418 21.847 20.923 7.944 7.111-7.32 3.819-14.64-9.125-16.85 7.405 1.264 15.73-.825 18.014-9.015C62.88 23.022 64 8.51 64 6.55c0-9.818-8.578-6.732-13.873-2.745Z"
/>
</Svg>
)
})
+28
View File
@@ -0,0 +1,28 @@
import React from 'react'
import Svg, {Path, SvgProps, PathProps} from 'react-native-svg'
import {colors} from '#/lib/styles'
const ratio = 17 / 64
export function Logotype({
fill,
...rest
}: {fill?: PathProps['fill']} & SvgProps) {
// @ts-ignore it's fiiiiine
const size = parseInt(rest.width || 32)
return (
<Svg
fill="none"
viewBox="0 0 64 17"
{...rest}
width={size}
height={Number(size) * ratio}>
<Path
fill={fill || colors.black}
d="M8.478 6.252c1.503.538 2.3 1.78 2.3 3.172 0 2.356-1.576 3.785-4.6 3.785H0V0h5.974c2.875 0 4.267 1.466 4.267 3.413 0 1.3-.594 2.245-1.763 2.839Zm-2.69-4.193H2.504v3.45h3.284c1.28 0 1.967-.667 1.967-1.78 0-1.02-.705-1.67-1.967-1.67Zm-3.284 9.072h3.544c1.41 0 2.17-.65 2.17-1.818 0-1.224-.723-1.837-2.17-1.837H2.504v3.655ZM14.251 13.209h-2.337V0h2.337v13.209ZM22.001 8.998V3.636h2.338v9.573h-2.263v-1.392c-.724 1.076-1.726 1.614-3.006 1.614-2.022 0-3.34-1.224-3.34-3.45V3.636h2.338v5.955c0 1.206.594 1.818 1.8 1.818 1.132 0 2.133-.835 2.133-2.411ZM34.979 8.59v.556h-7.161c.167 1.651 1.076 2.467 2.486 2.467 1.076 0 1.8-.463 2.189-1.372h2.244c-.5 1.947-2.17 3.19-4.452 3.19-1.428 0-2.579-.463-3.45-1.372-.872-.91-1.318-2.115-1.318-3.637 0-1.502.427-2.708 1.299-3.636.872-.909 2.004-1.372 3.432-1.372 1.447 0 2.597.482 3.45 1.428.854.946 1.28 2.208 1.28 3.747Zm-4.75-3.358c-1.28 0-2.17.742-2.393 2.281h4.805c-.204-1.391-1.057-2.281-2.411-2.281ZM40.16 13.469c-2.783 0-4.249-1.095-4.379-3.303h2.282c.13 1.188.724 1.633 2.134 1.633 1.261 0 1.892-.39 1.892-1.15 0-.687-.445-1.02-1.874-1.262l-1.094-.185c-2.097-.353-3.136-1.318-3.136-2.894 0-1.8 1.429-2.894 3.97-2.894 2.728 0 4.138 1.075 4.23 3.246h-2.207c-.056-1.169-.742-1.577-2.023-1.577-1.113 0-1.67.371-1.67 1.113 0 .668.483.965 1.596 1.169l1.206.186c2.32.426 3.32 1.28 3.32 2.912 0 1.93-1.557 3.006-4.247 3.006ZM54.667 13.209h-2.671l-2.783-4.453-1.447 1.447v3.006h-2.3V0h2.3v7.606l3.896-3.97h2.783l-3.618 3.618 3.84 5.955ZM60.772 6.048l.78-2.412H64l-3.692 10.352c-.39 1.057-.872 1.818-1.484 2.245-.612.426-1.484.63-2.634.63-.39 0-.724-.018-1.02-.055V14.97h.89c1.057 0 1.577-.65 1.577-1.54 0-.445-.149-1.094-.446-1.929l-2.746-7.866h2.487l.779 2.393c.575 1.8 1.076 3.58 1.521 5.343.408-1.521.928-3.302 1.54-5.324Z"
/>
</Svg>
)
}
+3 -9
View File
@@ -1,5 +1,5 @@
import React from 'react'
import {ActivityIndicator, StyleSheet, View, RefreshControl} from 'react-native'
import {ActivityIndicator, 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'
@@ -487,14 +487,8 @@ export function FeedsScreen(_props: Props) {
keyExtractor={item => item.key}
contentContainerStyle={styles.contentContainer}
renderItem={renderItem}
refreshControl={
<RefreshControl
refreshing={isPTR}
onRefresh={isUserSearching ? undefined : onPullToRefresh}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
/>
}
refreshing={isPTR}
onRefresh={isUserSearching ? undefined : onPullToRefresh}
initialNumToRender={10}
onEndReached={onEndReached}
// @ts-ignore our .web version only -prf
+2 -6
View File
@@ -9,6 +9,7 @@ import {CustomFeedEmptyState} from 'view/com/posts/CustomFeedEmptyState'
import {FeedsTabBar} from '../com/pager/FeedsTabBar'
import {Pager, RenderTabBarFnProps} from 'view/com/pager/Pager'
import {FeedPage} from 'view/com/feeds/FeedPage'
import {HomeLoggedOutCTA} from '../com/auth/HomeLoggedOutCTA'
import {useSetMinimalShellMode, useSetDrawerSwipeDisabled} from '#/state/shell'
import {usePreferencesQuery} from '#/state/queries/preferences'
import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types'
@@ -199,12 +200,7 @@ function HomeScreenReady({
onPageScrollStateChanged={onPageScrollStateChanged}
renderTabBar={renderTabBar}
tabBarPosition="top">
<FeedPage
testID="customFeedPage"
isPageFocused={true}
feed={`feedgen|at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot`}
renderEmptyState={renderCustomFeedEmptyState}
/>
<HomeLoggedOutCTA />
</Pager>
)
}
+1 -1
View File
@@ -153,7 +153,7 @@ function ProfileScreenLoaded({
const isMe = profile.did === currentAccount?.did
const showRepliesTab = hasSession
const showLikesTab = isMe
const showFeedsTab = isMe || extraInfoQuery.data?.hasFeedgens
const showFeedsTab = hasSession && (isMe || extraInfoQuery.data?.hasFeedgens)
const showListsTab = hasSession && (isMe || extraInfoQuery.data?.hasLists)
const sectionTitles = useMemo<string[]>(() => {
return [
+68 -43
View File
@@ -3,7 +3,6 @@ import {
View,
StyleSheet,
ActivityIndicator,
RefreshControl,
TextInput,
Pressable,
Platform,
@@ -185,7 +184,6 @@ type SearchResultSlice =
function SearchScreenPostResults({query}: {query: string}) {
const {_} = useLingui()
const pal = usePalette('default')
const [isPTR, setIsPTR] = React.useState(false)
const {
isFetched,
@@ -254,14 +252,8 @@ function SearchScreenPostResults({query}: {query: string}) {
}
}}
keyExtractor={item => item.key}
refreshControl={
<RefreshControl
refreshing={isPTR}
onRefresh={onPullToRefresh}
tintColor={pal.colors.text}
titleColor={pal.colors.text}
/>
}
refreshing={isPTR}
onRefresh={onPullToRefresh}
onEndReached={onEndReached}
// @ts-ignore web only -prf
desktopFixedHeight
@@ -304,7 +296,8 @@ function SearchScreenUserResults({query}: {query: string}) {
)
}
const SECTIONS = ['Posts', 'Users']
const SECTIONS_LOGGEDOUT = ['Users']
const SECTIONS_LOGGEDIN = ['Posts', 'Users']
export function SearchScreenInner({query}: {query?: string}) {
const pal = usePalette('default')
const setMinimalShellMode = useSetMinimalShellMode()
@@ -320,44 +313,62 @@ export function SearchScreenInner({query}: {query?: string}) {
[setDrawerSwipeDisabled, setMinimalShellMode],
)
if (hasSession) {
return query ? (
<Pager
tabBarPosition="top"
onPageSelected={onPageSelected}
renderTabBar={props => (
<CenteredView sideBorders style={pal.border}>
<TabBar items={SECTIONS_LOGGEDIN} {...props} />
</CenteredView>
)}
initialPage={0}>
<View>
<SearchScreenPostResults query={query} />
</View>
<View>
<SearchScreenUserResults query={query} />
</View>
</Pager>
) : (
<View>
<CenteredView sideBorders style={pal.border}>
<Text
type="title"
style={[
pal.text,
pal.border,
{
display: 'flex',
paddingVertical: 12,
paddingHorizontal: 18,
fontWeight: 'bold',
},
]}>
<Trans>Suggested Follows</Trans>
</Text>
</CenteredView>
<SearchScreenSuggestedFollows />
</View>
)
}
return query ? (
<Pager
tabBarPosition="top"
onPageSelected={onPageSelected}
renderTabBar={props => (
<CenteredView sideBorders style={pal.border}>
<TabBar items={SECTIONS} {...props} />
<TabBar items={SECTIONS_LOGGEDOUT} {...props} />
</CenteredView>
)}
initialPage={0}>
<View>
<SearchScreenPostResults query={query} />
</View>
<View>
<SearchScreenUserResults query={query} />
</View>
</Pager>
) : hasSession ? (
<View>
<CenteredView sideBorders style={pal.border}>
<Text
type="title"
style={[
pal.text,
pal.border,
{
display: 'flex',
paddingVertical: 12,
paddingHorizontal: 18,
fontWeight: 'bold',
},
]}>
<Trans>Suggested Follows</Trans>
</Text>
</CenteredView>
<SearchScreenSuggestedFollows />
</View>
) : (
<CenteredView sideBorders style={pal.border}>
<View
@@ -383,13 +394,27 @@ export function SearchScreenInner({query}: {query?: string}) {
</Text>
)}
<Text
style={[
pal.textLight,
{textAlign: 'center', paddingVertical: 12, paddingHorizontal: 18},
]}>
<Trans>Search for posts and users.</Trans>
</Text>
<View
style={{
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 30,
gap: 15,
}}>
<MagnifyingGlassIcon
strokeWidth={3}
size={isDesktop ? 60 : 60}
style={pal.textLight}
/>
<Text type="xl" style={[pal.textLight, {paddingHorizontal: 18}]}>
{isDesktop ? (
<Trans>Find users with the search tool on the right</Trans>
) : (
<Trans>Find users on Bluesky</Trans>
)}
</Text>
</View>
</View>
</CenteredView>
)
+1 -1
View File
@@ -231,9 +231,9 @@ let DrawerContent = ({}: {}): React.ReactNode => {
onPress={onPressNotifications}
/>
)}
<FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
{hasSession && (
<>
<FeedsMenuItem isActive={isAtFeeds} onPress={onPressMyFeeds} />
<ListsMenuItem onPress={onPressLists} />
<ModerationMenuItem onPress={onPressModeration} />
<ProfileMenuItem
+12 -6
View File
@@ -14,13 +14,19 @@ import {useCloseAllActiveElements} from '#/state/util'
let NavSignupCard = ({}: {}): React.ReactNode => {
const {_} = useLingui()
const pal = usePalette('default')
const {setShowLoggedOut} = useLoggedOutViewControls()
const {requestSwitchToAccount} = useLoggedOutViewControls()
const closeAllActiveElements = useCloseAllActiveElements()
const showLoggedOut = React.useCallback(() => {
const showSignIn = React.useCallback(() => {
closeAllActiveElements()
setShowLoggedOut(true)
}, [setShowLoggedOut, closeAllActiveElements])
requestSwitchToAccount({requestedAccount: 'none'})
}, [requestSwitchToAccount, closeAllActiveElements])
const showCreateAccount = React.useCallback(() => {
closeAllActiveElements()
requestSwitchToAccount({requestedAccount: 'new'})
// setShowLoggedOut(true)
}, [requestSwitchToAccount, closeAllActiveElements])
return (
<View
@@ -39,7 +45,7 @@ let NavSignupCard = ({}: {}): React.ReactNode => {
<View style={{flexDirection: 'row', paddingTop: 12, gap: 8}}>
<Button
onPress={showLoggedOut}
onPress={showCreateAccount}
accessibilityHint={_(msg`Sign up`)}
accessibilityLabel={_(msg`Sign up`)}>
<Text type="md" style={[{color: 'white'}, s.bold]}>
@@ -48,7 +54,7 @@ let NavSignupCard = ({}: {}): React.ReactNode => {
</Button>
<Button
type="default"
onPress={showLoggedOut}
onPress={showSignIn}
accessibilityHint={_(msg`Sign in`)}
accessibilityLabel={_(msg`Sign in`)}>
<Text type="md" style={[pal.text, s.bold]}>
+22 -22
View File
@@ -138,31 +138,31 @@ export function BottomBar({navigation}: BottomTabBarProps) {
accessibilityLabel={_(msg`Search`)}
accessibilityHint=""
/>
<Btn
testID="bottomBarFeedsBtn"
icon={
isAtFeeds ? (
<HashtagIcon
size={24}
style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
strokeWidth={4}
/>
) : (
<HashtagIcon
size={24}
style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
strokeWidth={2.25}
/>
)
}
onPress={onPressFeeds}
accessibilityRole="tab"
accessibilityLabel={_(msg`Feeds`)}
accessibilityHint=""
/>
{hasSession && (
<>
<Btn
testID="bottomBarFeedsBtn"
icon={
isAtFeeds ? (
<HashtagIcon
size={24}
style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
strokeWidth={4}
/>
) : (
<HashtagIcon
size={24}
style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
strokeWidth={2.25}
/>
)
}
onPress={onPressFeeds}
accessibilityRole="tab"
accessibilityLabel={_(msg`Feeds`)}
accessibilityHint=""
/>
<Btn
testID="bottomBarNotificationsBtn"
icon={
+11 -11
View File
@@ -65,20 +65,20 @@ export function BottomBarWeb() {
)
}}
</NavItem>
<NavItem routeName="Feeds" href="/feeds">
{({isActive}) => {
return (
<HashtagIcon
size={22}
style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
strokeWidth={isActive ? 4 : 2.5}
/>
)
}}
</NavItem>
{hasSession && (
<>
<NavItem routeName="Feeds" href="/feeds">
{({isActive}) => {
return (
<HashtagIcon
size={22}
style={[styles.ctrlIcon, pal.text, styles.feedsIcon]}
strokeWidth={isActive ? 4 : 2.5}
/>
)
}}
</NavItem>
<NavItem routeName="Notifications" href="/notifications">
{({isActive}) => {
const Icon = isActive ? BellIconSolid : BellIcon
+18 -19
View File
@@ -314,27 +314,26 @@ export function DesktopLeftNav() {
}
label={_(msg`Search`)}
/>
<NavItem
href="/feeds"
icon={
<HashtagIcon
strokeWidth={2.25}
style={pal.text as FontAwesomeIconStyle}
size={isDesktop ? 24 : 28}
/>
}
iconFilled={
<HashtagIcon
strokeWidth={2.5}
style={pal.text as FontAwesomeIconStyle}
size={isDesktop ? 24 : 28}
/>
}
label={_(msg`Feeds`)}
/>
{hasSession && (
<>
<NavItem
href="/feeds"
icon={
<HashtagIcon
strokeWidth={2.25}
style={pal.text as FontAwesomeIconStyle}
size={isDesktop ? 24 : 28}
/>
}
iconFilled={
<HashtagIcon
strokeWidth={2.5}
style={pal.text as FontAwesomeIconStyle}
size={isDesktop ? 24 : 28}
/>
}
label={_(msg`Feeds`)}
/>
<NavItem
href="/notifications"
count={numUnread}
+6 -12
View File
@@ -19,10 +19,6 @@ import {useTheme} from 'lib/ThemeContext'
import {usePalette} from 'lib/hooks/usePalette'
import {RoutesContainer, TabsNavigator} from '../../Navigation'
import {isStateAtTabRoot} from 'lib/routes/helpers'
import {
SafeAreaProvider,
initialWindowMetrics,
} from 'react-native-safe-area-context'
import {
useIsDrawerOpen,
useSetDrawerOpen,
@@ -107,14 +103,12 @@ export const Shell: React.FC = function ShellImpl() {
const pal = usePalette('default')
const theme = useTheme()
return (
<SafeAreaProvider initialMetrics={initialWindowMetrics} style={pal.view}>
<View testID="mobileShellView" style={[styles.outerContainer, pal.view]}>
<StatusBar style={theme.colorScheme === 'dark' ? 'light' : 'dark'} />
<RoutesContainer>
<ShellInner />
</RoutesContainer>
</View>
</SafeAreaProvider>
<View testID="mobileShellView" style={[styles.outerContainer, pal.view]}>
<StatusBar style={theme.colorScheme === 'dark' ? 'light' : 'dark'} />
<RoutesContainer>
<ShellInner />
</RoutesContainer>
</View>
)
}