Blah
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
|||||||
|
|
||||||
import 'view/icons'
|
import 'view/icons'
|
||||||
|
|
||||||
|
import {ThemeProvider as Alf} from '#/alf'
|
||||||
import {init as initPersistedState} from '#/state/persisted'
|
import {init as initPersistedState} from '#/state/persisted'
|
||||||
import {listenSessionDropped} from './state/events'
|
import {listenSessionDropped} from './state/events'
|
||||||
import {useColorMode} from 'state/shell'
|
import {useColorMode} from 'state/shell'
|
||||||
@@ -39,6 +40,7 @@ import {
|
|||||||
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
|
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
|
||||||
import * as persisted from '#/state/persisted'
|
import * as persisted from '#/state/persisted'
|
||||||
import {Splash} from '#/Splash'
|
import {Splash} from '#/Splash'
|
||||||
|
import {useColorModeTheme} from '#/alf/util/useColorModeTheme'
|
||||||
|
|
||||||
SplashScreen.preventAutoHideAsync()
|
SplashScreen.preventAutoHideAsync()
|
||||||
|
|
||||||
@@ -46,6 +48,7 @@ function InnerApp() {
|
|||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
const {isInitialLoad, currentAccount} = useSession()
|
const {isInitialLoad, currentAccount} = useSession()
|
||||||
const {resumeSession} = useSessionApi()
|
const {resumeSession} = useSessionApi()
|
||||||
|
const theme = useColorModeTheme(colorMode)
|
||||||
|
|
||||||
// init
|
// init
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -60,6 +63,7 @@ function InnerApp() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
|
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
|
||||||
|
<Alf theme={theme}>
|
||||||
<Splash isReady={!isInitialLoad}>
|
<Splash isReady={!isInitialLoad}>
|
||||||
<React.Fragment
|
<React.Fragment
|
||||||
// Resets the entire tree below when it changes:
|
// Resets the entire tree below when it changes:
|
||||||
@@ -79,6 +83,7 @@ function InnerApp() {
|
|||||||
</LoggedOutViewProvider>
|
</LoggedOutViewProvider>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
</Splash>
|
</Splash>
|
||||||
|
</Alf>
|
||||||
</SafeAreaProvider>
|
</SafeAreaProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,9 +96,9 @@ const sizeVariants: {
|
|||||||
} = {
|
} = {
|
||||||
small: {
|
small: {
|
||||||
pressable: {
|
pressable: {
|
||||||
py: 'm',
|
py: 's',
|
||||||
px: 'l',
|
px: 'm',
|
||||||
radius: 'm',
|
radius: 'round',
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
fontSize: 'm',
|
fontSize: 'm',
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import {Pressable as RNPressable} from 'react-native'
|
||||||
|
import {styled} from '#/alf/system'
|
||||||
|
export const Pressable = styled(RNPressable, {})
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
export * from '#/alf/system'
|
export * from '#/alf/system'
|
||||||
|
export * from '#/alf/util/platform'
|
||||||
export * from '#/alf/components/Box'
|
export * from '#/alf/components/Box'
|
||||||
export * from '#/alf/components/Typography'
|
export * from '#/alf/components/Typography'
|
||||||
export * from '#/alf/components/Button'
|
export * from '#/alf/components/Button'
|
||||||
|
export * from '#/alf/components/Pressable'
|
||||||
|
|||||||
@@ -147,6 +147,8 @@ export const light = createTheme({
|
|||||||
jcc: (_: boolean) => ({justifyContent: 'center'}),
|
jcc: (_: boolean) => ({justifyContent: 'center'}),
|
||||||
/** Shorthand for `justifyContent: 'space-between'` */
|
/** Shorthand for `justifyContent: 'space-between'` */
|
||||||
jcb: (_: boolean) => ({justifyContent: 'space-between'}),
|
jcb: (_: boolean) => ({justifyContent: 'space-between'}),
|
||||||
|
/** Shorthand for `justifyContent: 'flex-end'` */
|
||||||
|
jce: (_: boolean) => ({justifyContent: 'flex-end'}),
|
||||||
/** Shorthand for `position: 'absolute'` */
|
/** Shorthand for `position: 'absolute'` */
|
||||||
abs: (_: boolean) => ({position: 'absolute'}),
|
abs: (_: boolean) => ({position: 'absolute'}),
|
||||||
/** Shorthand for `StyleSheet.absoluteFillObject` */
|
/** Shorthand for `StyleSheet.absoluteFillObject` */
|
||||||
|
|||||||
@@ -18,6 +18,13 @@ export function android(value: any) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function notAndroid(value: any) {
|
||||||
|
return Platform.select({
|
||||||
|
ios: value,
|
||||||
|
web: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function native(value: any) {
|
export function native(value: any) {
|
||||||
return Platform.select({
|
return Platform.select({
|
||||||
native: value,
|
native: value,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import {isWeb} from '#/platform/detection'
|
|||||||
import {listenPostCreated} from '#/state/events'
|
import {listenPostCreated} from '#/state/events'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {STALE} from '#/state/queries'
|
import {STALE} from '#/state/queries'
|
||||||
|
import {Box} from '#/alf'
|
||||||
|
|
||||||
const LOADING_ITEM = {_reactKey: '__loading__'}
|
const LOADING_ITEM = {_reactKey: '__loading__'}
|
||||||
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
|
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
|
||||||
@@ -273,14 +274,14 @@ let Feed = ({
|
|||||||
const offset = Math.max(headerOffset, 32) * (isWeb ? 1 : 2)
|
const offset = Math.max(headerOffset, 32) * (isWeb ? 1 : 2)
|
||||||
|
|
||||||
return isFetchingNextPage ? (
|
return isFetchingNextPage ? (
|
||||||
<View style={[styles.feedFooter]}>
|
<Box pt='xl'>
|
||||||
<ActivityIndicator />
|
<ActivityIndicator />
|
||||||
<View style={{height: offset}} />
|
<Box h={offset} />
|
||||||
</View>
|
</Box>
|
||||||
) : shouldRenderEndOfFeed ? (
|
) : shouldRenderEndOfFeed ? (
|
||||||
<View style={{minHeight: offset}}>{renderEndOfFeed()}</View>
|
<Box minHeight={offset}>{renderEndOfFeed()}</Box>
|
||||||
) : (
|
) : (
|
||||||
<View style={{height: offset}} />
|
<Box h={offset} />
|
||||||
)
|
)
|
||||||
}, [isFetchingNextPage, shouldRenderEndOfFeed, renderEndOfFeed, headerOffset])
|
}, [isFetchingNextPage, shouldRenderEndOfFeed, renderEndOfFeed, headerOffset])
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
} from '@fortawesome/react-native-fontawesome'
|
} from '@fortawesome/react-native-fontawesome'
|
||||||
import {ReasonFeedSource, isReasonFeedSource} from 'lib/api/feed/types'
|
import {ReasonFeedSource, isReasonFeedSource} from 'lib/api/feed/types'
|
||||||
import {Link, TextLinkOnWebOnly, TextLink} from '../util/Link'
|
import {Link, TextLinkOnWebOnly, TextLink} from '../util/Link'
|
||||||
import {Text} from '../util/text/Text'
|
|
||||||
import {UserInfoText} from '../util/UserInfoText'
|
import {UserInfoText} from '../util/UserInfoText'
|
||||||
import {PostMeta} from '../util/PostMeta'
|
import {PostMeta} from '../util/PostMeta'
|
||||||
import {PostCtrls} from '../util/post-ctrls/PostCtrls'
|
import {PostCtrls} from '../util/post-ctrls/PostCtrls'
|
||||||
@@ -34,6 +33,7 @@ import {countLines} from 'lib/strings/helpers'
|
|||||||
import {useComposerControls} from '#/state/shell/composer'
|
import {useComposerControls} from '#/state/shell/composer'
|
||||||
import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
|
import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
|
||||||
import {FeedNameText} from '../util/FeedInfoText'
|
import {FeedNameText} from '../util/FeedInfoText'
|
||||||
|
import {Box, Text, useStyle} from '#/alf'
|
||||||
|
|
||||||
export function FeedItem({
|
export function FeedItem({
|
||||||
post,
|
post,
|
||||||
@@ -130,18 +130,19 @@ let FeedItemInner = ({
|
|||||||
})
|
})
|
||||||
}, [post, record, openComposer])
|
}, [post, record, openComposer])
|
||||||
|
|
||||||
const outerStyles = [
|
const outerStyles = useStyle(React.useMemo(() => ({
|
||||||
styles.outer,
|
bg: 'l0',
|
||||||
pal.view,
|
borderTopWidth: isThreadChild ? 0 : 1,
|
||||||
{
|
paddingLeft: 'm',
|
||||||
borderColor: pal.colors.border,
|
pr: 'l',
|
||||||
|
cursor: 'pointer',
|
||||||
|
overflow: 'hidden',
|
||||||
|
borderColor: 'l2',
|
||||||
paddingBottom:
|
paddingBottom:
|
||||||
isThreadLastChild || (!isThreadChild && !isThreadParent)
|
isThreadLastChild || (!isThreadChild && !isThreadParent)
|
||||||
? 6
|
? 's'
|
||||||
: undefined,
|
: undefined,
|
||||||
},
|
}), []))
|
||||||
isThreadChild ? styles.outerSmallTop : undefined,
|
|
||||||
]
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
@@ -152,8 +153,8 @@ let FeedItemInner = ({
|
|||||||
accessible={false}>
|
accessible={false}>
|
||||||
<PostSandboxWarning />
|
<PostSandboxWarning />
|
||||||
|
|
||||||
<View style={{flexDirection: 'row', gap: 10, paddingLeft: 8}}>
|
<Box row gap='s' pl='s'>
|
||||||
<View style={{width: 52}}>
|
<Box width={52}>
|
||||||
{isThreadChild && (
|
{isThreadChild && (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -166,15 +167,15 @@ let FeedItemInner = ({
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</Box>
|
||||||
|
|
||||||
<View style={{paddingTop: 12, flexShrink: 1}}>
|
<Box pt='m' flexShrink={1}>
|
||||||
{isReasonFeedSource(reason) ? (
|
{isReasonFeedSource(reason) ? (
|
||||||
<Link href={reason.href}>
|
<Link href={reason.href}>
|
||||||
<Text
|
<Text
|
||||||
type="sm-bold"
|
fontSize='s'
|
||||||
style={pal.textLight}
|
fontWeight='bold'
|
||||||
lineHeight={1.2}
|
c='l5'
|
||||||
numberOfLines={1}>
|
numberOfLines={1}>
|
||||||
From{' '}
|
From{' '}
|
||||||
<FeedNameText
|
<FeedNameText
|
||||||
@@ -203,9 +204,9 @@ let FeedItemInner = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Text
|
<Text
|
||||||
type="sm-bold"
|
fontSize='s'
|
||||||
style={pal.textLight}
|
fontWeight='bold'
|
||||||
lineHeight={1.2}
|
c='l5'
|
||||||
numberOfLines={1}>
|
numberOfLines={1}>
|
||||||
Reposted by{' '}
|
Reposted by{' '}
|
||||||
<TextLinkOnWebOnly
|
<TextLinkOnWebOnly
|
||||||
@@ -221,11 +222,11 @@ let FeedItemInner = ({
|
|||||||
</Text>
|
</Text>
|
||||||
</Link>
|
</Link>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</Box>
|
||||||
</View>
|
</Box>
|
||||||
|
|
||||||
<View style={styles.layout}>
|
<Box row gap='m' mt={1}>
|
||||||
<View style={styles.layoutAvi}>
|
<Box pl='s'>
|
||||||
<PreviewableUserAvatar
|
<PreviewableUserAvatar
|
||||||
size={52}
|
size={52}
|
||||||
did={post.author.did}
|
did={post.author.did}
|
||||||
@@ -245,8 +246,8 @@ let FeedItemInner = ({
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</Box>
|
||||||
<View style={styles.layoutContent}>
|
<Box flex={1}>
|
||||||
<PostMeta
|
<PostMeta
|
||||||
author={post.author}
|
author={post.author}
|
||||||
authorHasWarning={!!post.author.labels?.length}
|
authorHasWarning={!!post.author.labels?.length}
|
||||||
@@ -254,7 +255,7 @@ let FeedItemInner = ({
|
|||||||
postHref={href}
|
postHref={href}
|
||||||
/>
|
/>
|
||||||
{!isThreadChild && replyAuthorDid !== '' && (
|
{!isThreadChild && replyAuthorDid !== '' && (
|
||||||
<View style={[s.flexRow, s.mb2, s.alignCenter]}>
|
<Box row aic mb={2}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon="reply"
|
icon="reply"
|
||||||
size={9}
|
size={9}
|
||||||
@@ -264,9 +265,9 @@ let FeedItemInner = ({
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Text
|
<Text
|
||||||
type="md"
|
fontSize='m'
|
||||||
style={[pal.textLight, s.mr2]}
|
c='l4'
|
||||||
lineHeight={1.2}
|
mr={2}
|
||||||
numberOfLines={1}>
|
numberOfLines={1}>
|
||||||
Reply to{' '}
|
Reply to{' '}
|
||||||
<UserInfoText
|
<UserInfoText
|
||||||
@@ -276,7 +277,7 @@ let FeedItemInner = ({
|
|||||||
style={[pal.textLight, s.ml2]}
|
style={[pal.textLight, s.ml2]}
|
||||||
/>
|
/>
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<PostContent
|
<PostContent
|
||||||
moderation={moderation}
|
moderation={moderation}
|
||||||
@@ -285,8 +286,8 @@ let FeedItemInner = ({
|
|||||||
postAuthor={post.author}
|
postAuthor={post.author}
|
||||||
/>
|
/>
|
||||||
<PostCtrls post={post} record={record} onPressReply={onPressReply} />
|
<PostCtrls post={post} record={record} onPressReply={onPressReply} />
|
||||||
</View>
|
</Box>
|
||||||
</View>
|
</Box>
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -320,7 +321,7 @@ let PostContent = ({
|
|||||||
childContainerStyle={styles.contentHiderChild}>
|
childContainerStyle={styles.contentHiderChild}>
|
||||||
<PostAlerts moderation={moderation.content} style={styles.alert} />
|
<PostAlerts moderation={moderation.content} style={styles.alert} />
|
||||||
{richText.text ? (
|
{richText.text ? (
|
||||||
<View style={styles.postTextContainer}>
|
<Box row aic flexWrap='wrap' pb='xs'>
|
||||||
<RichText
|
<RichText
|
||||||
testID="postText"
|
testID="postText"
|
||||||
type="post-text"
|
type="post-text"
|
||||||
@@ -329,7 +330,7 @@ let PostContent = ({
|
|||||||
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
|
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
|
||||||
style={s.flex1}
|
style={s.flex1}
|
||||||
/>
|
/>
|
||||||
</View>
|
</Box>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
{limitLines ? (
|
{limitLines ? (
|
||||||
<TextLink
|
<TextLink
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Pressable, StyleSheet, View} from 'react-native'
|
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {Text} from '../util/text/Text'
|
|
||||||
import {TextLink} from '../util/Link'
|
import {TextLink} from '../util/Link'
|
||||||
import {UserAvatar, UserAvatarType} from '../util/UserAvatar'
|
import {UserAvatar, UserAvatarType} from '../util/UserAvatar'
|
||||||
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
|
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||||
@@ -13,13 +10,14 @@ import {sanitizeHandle} from 'lib/strings/handles'
|
|||||||
import {makeProfileLink} from 'lib/routes/links'
|
import {makeProfileLink} from 'lib/routes/links'
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
import {BACK_HITSLOP} from 'lib/constants'
|
import {BACK_HITSLOP} from 'lib/constants'
|
||||||
import {isNative} from 'platform/detection'
|
|
||||||
import {useLightboxControls, ImagesLightbox} from '#/state/lightbox'
|
import {useLightboxControls, ImagesLightbox} from '#/state/lightbox'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {useSetDrawerOpen} from '#/state/shell'
|
import {useSetDrawerOpen} from '#/state/shell'
|
||||||
import {emitSoftReset} from '#/state/events'
|
import {emitSoftReset} from '#/state/events'
|
||||||
|
|
||||||
|
import { Box, useTokens, web, Pressable, Text } from '#/alf'
|
||||||
|
|
||||||
export function ProfileSubpageHeader({
|
export function ProfileSubpageHeader({
|
||||||
isLoading,
|
isLoading,
|
||||||
href,
|
href,
|
||||||
@@ -48,7 +46,7 @@ export function ProfileSubpageHeader({
|
|||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {isMobile} = useWebMediaQueries()
|
const {isMobile} = useWebMediaQueries()
|
||||||
const {openLightbox} = useLightboxControls()
|
const {openLightbox} = useLightboxControls()
|
||||||
const pal = usePalette('default')
|
const tokens = useTokens()
|
||||||
const canGoBack = navigation.canGoBack()
|
const canGoBack = navigation.canGoBack()
|
||||||
|
|
||||||
const onPressBack = React.useCallback(() => {
|
const onPressBack = React.useCallback(() => {
|
||||||
@@ -72,25 +70,25 @@ export function ProfileSubpageHeader({
|
|||||||
}, [openLightbox, avatar])
|
}, [openLightbox, avatar])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CenteredView style={pal.view}>
|
<CenteredView style={{ backgroundColor: tokens.color.l0 }}>
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
<View
|
<Box
|
||||||
style={[
|
row aic
|
||||||
{
|
pt={web('s')}
|
||||||
flexDirection: 'row',
|
pb='s'
|
||||||
alignItems: 'center',
|
px='m'
|
||||||
borderBottomWidth: 1,
|
borderColor='l3'
|
||||||
paddingTop: isNative ? 0 : 8,
|
borderBottomWidth={1}
|
||||||
paddingBottom: 8,
|
gtMobile={{
|
||||||
paddingHorizontal: isMobile ? 12 : 14,
|
px: 14
|
||||||
},
|
}}>
|
||||||
pal.border,
|
|
||||||
]}>
|
|
||||||
<Pressable
|
<Pressable
|
||||||
testID="headerDrawerBtn"
|
testID="headerDrawerBtn"
|
||||||
onPress={canGoBack ? onPressBack : onPressMenu}
|
onPress={canGoBack ? onPressBack : onPressMenu}
|
||||||
hitSlop={BACK_HITSLOP}
|
hitSlop={BACK_HITSLOP}
|
||||||
style={canGoBack ? styles.backBtn : styles.backBtnWide}
|
w={canGoBack ? 20 : 40}
|
||||||
|
h={30}
|
||||||
|
px={canGoBack ? 0 : 6}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={canGoBack ? 'Back' : 'Menu'}
|
accessibilityLabel={canGoBack ? 'Back' : 'Menu'}
|
||||||
accessibilityHint="">
|
accessibilityHint="">
|
||||||
@@ -98,28 +96,28 @@ export function ProfileSubpageHeader({
|
|||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
size={18}
|
size={18}
|
||||||
icon="angle-left"
|
icon="angle-left"
|
||||||
style={[styles.backIcon, pal.text]}
|
style={{ marginTop: tokens.space.s, color: tokens.color.l5 }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
size={18}
|
size={18}
|
||||||
icon="bars"
|
icon="bars"
|
||||||
style={[styles.backIcon, pal.textLight]}
|
style={{ marginTop: tokens.space.s, color: tokens.color.l5 }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Pressable>
|
</Pressable>
|
||||||
<View style={{flex: 1}} />
|
<Box style={{flex: 1}} />
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<View
|
<Box
|
||||||
style={{
|
row
|
||||||
flexDirection: 'row',
|
gap='m'
|
||||||
alignItems: 'flex-start',
|
pt='m'
|
||||||
gap: 10,
|
pb='s'
|
||||||
paddingTop: 14,
|
px='m'
|
||||||
paddingBottom: 6,
|
gtMobile={{
|
||||||
paddingHorizontal: isMobile ? 12 : 14,
|
px: 14
|
||||||
}}>
|
}}>
|
||||||
<Pressable
|
<Pressable
|
||||||
testID="headerAviButton"
|
testID="headerAviButton"
|
||||||
@@ -130,7 +128,7 @@ export function ProfileSubpageHeader({
|
|||||||
style={{width: 58}}>
|
style={{width: 58}}>
|
||||||
<UserAvatar type={avatarType} size={58} avatar={avatar} />
|
<UserAvatar type={avatarType} size={58} avatar={avatar} />
|
||||||
</Pressable>
|
</Pressable>
|
||||||
<View style={{flex: 1}}>
|
<Box flex={1}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<LoadingPlaceholder
|
<LoadingPlaceholder
|
||||||
width={200}
|
width={200}
|
||||||
@@ -142,7 +140,7 @@ export function ProfileSubpageHeader({
|
|||||||
testID="headerTitle"
|
testID="headerTitle"
|
||||||
type="title-xl"
|
type="title-xl"
|
||||||
href={href}
|
href={href}
|
||||||
style={[pal.text, {fontWeight: 'bold'}]}
|
style={{fontWeight: 'bold', color: tokens.color.l7}}
|
||||||
text={title || ''}
|
text={title || ''}
|
||||||
onPress={emitSoftReset}
|
onPress={emitSoftReset}
|
||||||
numberOfLines={4}
|
numberOfLines={4}
|
||||||
@@ -152,7 +150,7 @@ export function ProfileSubpageHeader({
|
|||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<LoadingPlaceholder width={50} height={8} />
|
<LoadingPlaceholder width={50} height={8} />
|
||||||
) : (
|
) : (
|
||||||
<Text type="xl" style={[pal.textLight]} numberOfLines={1}>
|
<Text fontSize='l' c='l5' numberOfLines={1}>
|
||||||
by{' '}
|
by{' '}
|
||||||
{!creator ? (
|
{!creator ? (
|
||||||
'—'
|
'—'
|
||||||
@@ -162,37 +160,18 @@ export function ProfileSubpageHeader({
|
|||||||
<TextLink
|
<TextLink
|
||||||
text={sanitizeHandle(creator.handle, '@')}
|
text={sanitizeHandle(creator.handle, '@')}
|
||||||
href={makeProfileLink(creator)}
|
href={makeProfileLink(creator)}
|
||||||
style={pal.textLight}
|
style={{ color: tokens.color.l5 }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</Box>
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<View
|
<Box row aic>
|
||||||
style={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
}}>
|
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</View>
|
</Box>
|
||||||
</CenteredView>
|
</CenteredView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
backBtn: {
|
|
||||||
width: 20,
|
|
||||||
height: 30,
|
|
||||||
},
|
|
||||||
backBtnWide: {
|
|
||||||
width: 20,
|
|
||||||
height: 30,
|
|
||||||
paddingHorizontal: 6,
|
|
||||||
},
|
|
||||||
backIcon: {
|
|
||||||
marginTop: 6,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {HeartIcon, HeartIconSolid} from 'lib/icons'
|
|||||||
import {s} from 'lib/styles'
|
import {s} from 'lib/styles'
|
||||||
import {useTheme} from 'lib/ThemeContext'
|
import {useTheme} from 'lib/ThemeContext'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
|
import {Box, useTokens} from '#/alf'
|
||||||
|
|
||||||
export function LoadingPlaceholder({
|
export function LoadingPlaceholder({
|
||||||
width,
|
width,
|
||||||
@@ -42,58 +43,57 @@ export function PostLoadingPlaceholder({
|
|||||||
}: {
|
}: {
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
}) {
|
}) {
|
||||||
const theme = useTheme()
|
const tokens = useTokens()
|
||||||
const pal = usePalette('default')
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.post, pal.view, style]}>
|
<Box bg='l0' row px='m' pt='xl' pb='xs'>
|
||||||
<LoadingPlaceholder
|
<Box
|
||||||
|
bg='l1'
|
||||||
width={52}
|
width={52}
|
||||||
height={52}
|
height={52}
|
||||||
style={[
|
radius='round'
|
||||||
styles.avatar,
|
mr='m'
|
||||||
{
|
ml='s'
|
||||||
position: 'relative',
|
top={-6}
|
||||||
top: -6,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
<View style={[s.flex1]}>
|
<Box flex={1}>
|
||||||
<LoadingPlaceholder width={100} height={6} style={{marginBottom: 10}} />
|
<Box gap='s'>
|
||||||
<LoadingPlaceholder width="95%" height={6} style={{marginBottom: 8}} />
|
<Box bg='l1' width={100} height={6} radius='s' />
|
||||||
<LoadingPlaceholder width="95%" height={6} style={{marginBottom: 8}} />
|
<Box bg='l1' width="95%" height={6} radius='s' />
|
||||||
<LoadingPlaceholder width="80%" height={6} style={{marginBottom: 15}} />
|
<Box bg='l1' width="95%" height={6} radius='s' />
|
||||||
<View style={s.flexRow}>
|
<Box bg='l1' width="80%" height={6} radius='s' mb='s' />
|
||||||
<View style={s.flex1}>
|
</Box>
|
||||||
|
<Box row>
|
||||||
|
<Box column>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
style={{color: theme.palette.default.icon}}
|
style={{color: tokens.color.l2}}
|
||||||
icon={['far', 'comment']}
|
icon={['far', 'comment']}
|
||||||
size={14}
|
size={14}
|
||||||
/>
|
/>
|
||||||
</View>
|
</Box>
|
||||||
<View style={s.flex1}>
|
<Box column>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
style={{color: theme.palette.default.icon}}
|
style={{color: tokens.color.l2}}
|
||||||
icon="retweet"
|
icon="retweet"
|
||||||
size={18}
|
size={18}
|
||||||
/>
|
/>
|
||||||
</View>
|
</Box>
|
||||||
<View style={s.flex1}>
|
<Box column>
|
||||||
<HeartIcon
|
<HeartIcon
|
||||||
style={{color: theme.palette.default.icon} as ViewStyle}
|
style={{color: tokens.color.l2}}
|
||||||
size={17}
|
size={17}
|
||||||
strokeWidth={1.7}
|
strokeWidth={1.7}
|
||||||
/>
|
/>
|
||||||
</View>
|
</Box>
|
||||||
<View style={s.flex1} />
|
<Box column />
|
||||||
</View>
|
</Box>
|
||||||
</View>
|
</Box>
|
||||||
</View>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PostFeedLoadingPlaceholder() {
|
export function PostFeedLoadingPlaceholder() {
|
||||||
return (
|
return (
|
||||||
<View>
|
<Box>
|
||||||
<PostLoadingPlaceholder />
|
<PostLoadingPlaceholder />
|
||||||
<PostLoadingPlaceholder />
|
<PostLoadingPlaceholder />
|
||||||
<PostLoadingPlaceholder />
|
<PostLoadingPlaceholder />
|
||||||
@@ -102,7 +102,7 @@ export function PostFeedLoadingPlaceholder() {
|
|||||||
<PostLoadingPlaceholder />
|
<PostLoadingPlaceholder />
|
||||||
<PostLoadingPlaceholder />
|
<PostLoadingPlaceholder />
|
||||||
<PostLoadingPlaceholder />
|
<PostLoadingPlaceholder />
|
||||||
</View>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, {memo} from 'react'
|
import React, {memo} from 'react'
|
||||||
import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native'
|
import {StyleProp, TextStyle, ViewStyle} from 'react-native'
|
||||||
import {Text} from './text/Text'
|
|
||||||
import {TextLinkOnWebOnly} from './Link'
|
import {TextLinkOnWebOnly} from './Link'
|
||||||
import {niceDate} from 'lib/strings/time'
|
import {niceDate} from 'lib/strings/time'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
@@ -11,6 +10,7 @@ import {sanitizeHandle} from 'lib/strings/handles'
|
|||||||
import {isAndroid} from 'platform/detection'
|
import {isAndroid} from 'platform/detection'
|
||||||
import {TimeElapsed} from './TimeElapsed'
|
import {TimeElapsed} from './TimeElapsed'
|
||||||
import {makeProfileLink} from 'lib/routes/links'
|
import {makeProfileLink} from 'lib/routes/links'
|
||||||
|
import {Box, Text, android, notAndroid} from '#/alf'
|
||||||
|
|
||||||
interface PostMetaOpts {
|
interface PostMetaOpts {
|
||||||
author: {
|
author: {
|
||||||
@@ -35,17 +35,17 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
|
|||||||
const handle = opts.author.handle
|
const handle = opts.author.handle
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, opts.style]}>
|
<Box row aic pb={2} gap='xs' zIndex={1} style={[opts.style]}>
|
||||||
{opts.showAvatar && (
|
{opts.showAvatar && (
|
||||||
<View style={styles.avatar}>
|
<Box alignSelf='center'>
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
avatar={opts.author.avatar}
|
avatar={opts.author.avatar}
|
||||||
size={opts.avatarSize || 16}
|
size={opts.avatarSize || 16}
|
||||||
// TODO moderation
|
// TODO moderation
|
||||||
/>
|
/>
|
||||||
</View>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<View style={styles.maxWidth}>
|
<Box flex={android(1)} maxWidth={notAndroid('80%')}>
|
||||||
<TextLinkOnWebOnly
|
<TextLinkOnWebOnly
|
||||||
type={opts.displayNameType || 'lg-bold'}
|
type={opts.displayNameType || 'lg-bold'}
|
||||||
style={[pal.text, opts.displayNameStyle]}
|
style={[pal.text, opts.displayNameStyle]}
|
||||||
@@ -55,22 +55,21 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
|
|||||||
<>
|
<>
|
||||||
{sanitizeDisplayName(displayName)}
|
{sanitizeDisplayName(displayName)}
|
||||||
<Text
|
<Text
|
||||||
type="md"
|
c='l4'
|
||||||
numberOfLines={1}
|
fontSize='m'
|
||||||
lineHeight={1.2}
|
fontWeight='normal'
|
||||||
style={pal.textLight}>
|
numberOfLines={1}>
|
||||||
{sanitizeHandle(handle, '@')}
|
{sanitizeHandle(handle, '@')}
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
href={makeProfileLink(opts.author)}
|
href={makeProfileLink(opts.author)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</Box>
|
||||||
{!isAndroid && (
|
{!isAndroid && (
|
||||||
<Text
|
<Text
|
||||||
type="md"
|
fontSize='m'
|
||||||
style={pal.textLight}
|
style={pal.textLight}
|
||||||
lineHeight={1.2}
|
|
||||||
accessible={false}>
|
accessible={false}>
|
||||||
·
|
·
|
||||||
</Text>
|
</Text>
|
||||||
@@ -89,26 +88,8 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</TimeElapsed>
|
</TimeElapsed>
|
||||||
</View>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
PostMeta = memo(PostMeta)
|
PostMeta = memo(PostMeta)
|
||||||
export {PostMeta}
|
export {PostMeta}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
paddingBottom: 2,
|
|
||||||
gap: 4,
|
|
||||||
zIndex: 1,
|
|
||||||
flex: 1,
|
|
||||||
},
|
|
||||||
avatar: {
|
|
||||||
alignSelf: 'center',
|
|
||||||
},
|
|
||||||
maxWidth: {
|
|
||||||
flex: isAndroid ? 1 : undefined,
|
|
||||||
maxWidth: !isAndroid ? '80%' : undefined,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, {ComponentProps} from 'react'
|
import React, {ComponentProps} from 'react'
|
||||||
import {StyleSheet, TouchableWithoutFeedback} from 'react-native'
|
import {TouchableWithoutFeedback} from 'react-native'
|
||||||
import LinearGradient from 'react-native-linear-gradient'
|
import LinearGradient from 'react-native-linear-gradient'
|
||||||
import {gradients} from 'lib/styles'
|
import {gradients} from 'lib/styles'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
@@ -8,6 +8,8 @@ import {clamp} from 'lib/numbers'
|
|||||||
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
||||||
import Animated from 'react-native-reanimated'
|
import Animated from 'react-native-reanimated'
|
||||||
|
|
||||||
|
import {useStyles} from '#/alf'
|
||||||
|
|
||||||
export interface FABProps
|
export interface FABProps
|
||||||
extends ComponentProps<typeof TouchableWithoutFeedback> {
|
extends ComponentProps<typeof TouchableWithoutFeedback> {
|
||||||
testID?: string
|
testID?: string
|
||||||
@@ -18,6 +20,26 @@ export function FABInner({testID, icon, ...props}: FABProps) {
|
|||||||
const insets = useSafeAreaInsets()
|
const insets = useSafeAreaInsets()
|
||||||
const {isMobile, isTablet} = useWebMediaQueries()
|
const {isMobile, isTablet} = useWebMediaQueries()
|
||||||
const {fabMinimalShellTransform} = useMinimalShellMode()
|
const {fabMinimalShellTransform} = useMinimalShellMode()
|
||||||
|
const styles = useStyles(React.useMemo(() => ({
|
||||||
|
sizeRegular: {
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
radius: 'round',
|
||||||
|
},
|
||||||
|
sizeLarge: {
|
||||||
|
width: 70,
|
||||||
|
height: 70,
|
||||||
|
radius: 'round',
|
||||||
|
},
|
||||||
|
outer: {
|
||||||
|
position: 'absolute',
|
||||||
|
zIndex: 1,
|
||||||
|
},
|
||||||
|
inner: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
}), []))
|
||||||
|
|
||||||
const size = React.useMemo(() => {
|
const size = React.useMemo(() => {
|
||||||
return isTablet ? styles.sizeLarge : styles.sizeRegular
|
return isTablet ? styles.sizeLarge : styles.sizeRegular
|
||||||
@@ -51,24 +73,3 @@ export function FABInner({testID, icon, ...props}: FABProps) {
|
|||||||
</TouchableWithoutFeedback>
|
</TouchableWithoutFeedback>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
sizeRegular: {
|
|
||||||
width: 60,
|
|
||||||
height: 60,
|
|
||||||
borderRadius: 30,
|
|
||||||
},
|
|
||||||
sizeLarge: {
|
|
||||||
width: 70,
|
|
||||||
height: 70,
|
|
||||||
borderRadius: 35,
|
|
||||||
},
|
|
||||||
outer: {
|
|
||||||
position: 'absolute',
|
|
||||||
zIndex: 1,
|
|
||||||
},
|
|
||||||
inner: {
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
import {TouchableOpacity, View} from 'react-native'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {colors} from 'lib/styles'
|
import {colors} from 'lib/styles'
|
||||||
import {HITSLOP_20} from 'lib/constants'
|
import {HITSLOP_20} from 'lib/constants'
|
||||||
@@ -11,6 +10,8 @@ const AnimatedTouchableOpacity =
|
|||||||
Animated.createAnimatedComponent(TouchableOpacity)
|
Animated.createAnimatedComponent(TouchableOpacity)
|
||||||
import {isWeb} from 'platform/detection'
|
import {isWeb} from 'platform/detection'
|
||||||
|
|
||||||
|
import { useStyles, useTokens } from '#/alf'
|
||||||
|
|
||||||
export function LoadLatestBtn({
|
export function LoadLatestBtn({
|
||||||
onPress,
|
onPress,
|
||||||
label,
|
label,
|
||||||
@@ -20,32 +21,11 @@ export function LoadLatestBtn({
|
|||||||
label: string
|
label: string
|
||||||
showIndicator: boolean
|
showIndicator: boolean
|
||||||
}) {
|
}) {
|
||||||
const pal = usePalette('default')
|
|
||||||
const {isDesktop, isTablet, isMobile} = useWebMediaQueries()
|
const {isDesktop, isTablet, isMobile} = useWebMediaQueries()
|
||||||
const {fabMinimalShellTransform} = useMinimalShellMode()
|
const {fabMinimalShellTransform} = useMinimalShellMode()
|
||||||
|
|
||||||
return (
|
const tokens = useTokens()
|
||||||
<AnimatedTouchableOpacity
|
const styles = useStyles({
|
||||||
style={[
|
|
||||||
styles.loadLatest,
|
|
||||||
isDesktop && styles.loadLatestDesktop,
|
|
||||||
isTablet && styles.loadLatestTablet,
|
|
||||||
pal.borderDark,
|
|
||||||
pal.view,
|
|
||||||
isMobile && fabMinimalShellTransform,
|
|
||||||
]}
|
|
||||||
onPress={onPress}
|
|
||||||
hitSlop={HITSLOP_20}
|
|
||||||
accessibilityRole="button"
|
|
||||||
accessibilityLabel={label}
|
|
||||||
accessibilityHint="">
|
|
||||||
<FontAwesomeIcon icon="angle-up" color={pal.colors.text} size={19} />
|
|
||||||
{showIndicator && <View style={[styles.indicator, pal.borderDark]} />}
|
|
||||||
</AnimatedTouchableOpacity>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
loadLatest: {
|
loadLatest: {
|
||||||
// @ts-ignore 'fixed' is web only -prf
|
// @ts-ignore 'fixed' is web only -prf
|
||||||
position: isWeb ? 'fixed' : 'absolute',
|
position: isWeb ? 'fixed' : 'absolute',
|
||||||
@@ -58,6 +38,8 @@ const styles = StyleSheet.create({
|
|||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
|
borderColor: 'l3',
|
||||||
|
bg: 'l0',
|
||||||
},
|
},
|
||||||
loadLatestTablet: {
|
loadLatestTablet: {
|
||||||
// @ts-ignore web only
|
// @ts-ignore web only
|
||||||
@@ -76,5 +58,25 @@ const styles = StyleSheet.create({
|
|||||||
height: 12,
|
height: 12,
|
||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
|
borderColor: 'l3',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnimatedTouchableOpacity
|
||||||
|
style={[
|
||||||
|
styles.loadLatest,
|
||||||
|
isDesktop && styles.loadLatestDesktop,
|
||||||
|
isTablet && styles.loadLatestTablet,
|
||||||
|
isMobile && fabMinimalShellTransform,
|
||||||
|
]}
|
||||||
|
onPress={onPress}
|
||||||
|
hitSlop={HITSLOP_20}
|
||||||
|
accessibilityRole="button"
|
||||||
|
accessibilityLabel={label}
|
||||||
|
accessibilityHint="">
|
||||||
|
<FontAwesomeIcon icon="angle-up" color={tokens.color.l7} size={19} />
|
||||||
|
{showIndicator && <View style={[styles.indicator]} />}
|
||||||
|
</AnimatedTouchableOpacity>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
ViewStyle,
|
ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {AppBskyFeedDefs, AppBskyFeedPost} from '@atproto/api'
|
import {AppBskyFeedDefs, AppBskyFeedPost} from '@atproto/api'
|
||||||
import {Text} from '../text/Text'
|
|
||||||
import {PostDropdownBtn} from '../forms/PostDropdownBtn'
|
import {PostDropdownBtn} from '../forms/PostDropdownBtn'
|
||||||
import {HeartIcon, HeartIconSolid, CommentBottomArrow} from 'lib/icons'
|
import {HeartIcon, HeartIconSolid, CommentBottomArrow} from 'lib/icons'
|
||||||
import {s} from 'lib/styles'
|
import {s} from 'lib/styles'
|
||||||
@@ -26,6 +25,7 @@ import {
|
|||||||
import {useComposerControls} from '#/state/shell/composer'
|
import {useComposerControls} from '#/state/shell/composer'
|
||||||
import {Shadow} from '#/state/cache/types'
|
import {Shadow} from '#/state/cache/types'
|
||||||
import {useRequireAuth} from '#/state/session'
|
import {useRequireAuth} from '#/state/session'
|
||||||
|
import {Box, Text, Pressable} from '#/alf'
|
||||||
|
|
||||||
let PostCtrls = ({
|
let PostCtrls = ({
|
||||||
big,
|
big,
|
||||||
@@ -129,12 +129,12 @@ let PostCtrls = ({
|
|||||||
])
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.ctrls, style]}>
|
<Box row jcb style={[style]}>
|
||||||
<TouchableOpacity
|
<Pressable
|
||||||
testID="replyBtn"
|
testID="replyBtn"
|
||||||
|
row aic
|
||||||
|
pa={!big ? 'xs' : 0}
|
||||||
style={[
|
style={[
|
||||||
styles.ctrl,
|
|
||||||
!big && styles.ctrlPad,
|
|
||||||
{paddingLeft: 0},
|
{paddingLeft: 0},
|
||||||
post.viewer?.replyDisabled ? {opacity: 0.5} : undefined,
|
post.viewer?.replyDisabled ? {opacity: 0.5} : undefined,
|
||||||
]}
|
]}
|
||||||
@@ -155,11 +155,11 @@ let PostCtrls = ({
|
|||||||
size={big ? 20 : 15}
|
size={big ? 20 : 15}
|
||||||
/>
|
/>
|
||||||
{typeof post.replyCount !== 'undefined' ? (
|
{typeof post.replyCount !== 'undefined' ? (
|
||||||
<Text style={[defaultCtrlColor, s.ml5, s.f15]}>
|
<Text fontSize='s' ml='xs' c='l4'>
|
||||||
{post.replyCount}
|
{post.replyCount}
|
||||||
</Text>
|
</Text>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</TouchableOpacity>
|
</Pressable>
|
||||||
<RepostButton
|
<RepostButton
|
||||||
big={big}
|
big={big}
|
||||||
isReposted={!!post.viewer?.repost}
|
isReposted={!!post.viewer?.repost}
|
||||||
@@ -167,9 +167,10 @@ let PostCtrls = ({
|
|||||||
onRepost={onRepost}
|
onRepost={onRepost}
|
||||||
onQuote={onQuote}
|
onQuote={onQuote}
|
||||||
/>
|
/>
|
||||||
<TouchableOpacity
|
<Pressable
|
||||||
testID="likeBtn"
|
testID="likeBtn"
|
||||||
style={[styles.ctrl, !big && styles.ctrlPad]}
|
row aic
|
||||||
|
pa={!big ? 'xs' : 0}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
requireAuth(() => onPressToggleLike())
|
requireAuth(() => onPressToggleLike())
|
||||||
}}
|
}}
|
||||||
@@ -190,16 +191,15 @@ let PostCtrls = ({
|
|||||||
)}
|
)}
|
||||||
{typeof post.likeCount !== 'undefined' ? (
|
{typeof post.likeCount !== 'undefined' ? (
|
||||||
<Text
|
<Text
|
||||||
testID="likeCount"
|
fontSize='s'
|
||||||
style={
|
ml='xs'
|
||||||
post.viewer?.like
|
c={post.viewer?.like ? 'red' : 'l4'}
|
||||||
? [s.bold, s.likeColor, s.f15, s.ml5]
|
fontWeight={post.viewer?.like ? 'semi' : 'normal'}
|
||||||
: [defaultCtrlColor, s.f15, s.ml5]
|
testID="likeCount">
|
||||||
}>
|
|
||||||
{post.likeCount}
|
{post.likeCount}
|
||||||
</Text>
|
</Text>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</TouchableOpacity>
|
</Pressable>
|
||||||
{big ? undefined : (
|
{big ? undefined : (
|
||||||
<PostDropdownBtn
|
<PostDropdownBtn
|
||||||
testID="postDropdownBtn"
|
testID="postDropdownBtn"
|
||||||
@@ -212,7 +212,7 @@ let PostCtrls = ({
|
|||||||
)}
|
)}
|
||||||
{/* used for adding pad to the right side */}
|
{/* used for adding pad to the right side */}
|
||||||
<View />
|
<View />
|
||||||
</View>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
PostCtrls = memo(PostCtrls)
|
PostCtrls = memo(PostCtrls)
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import {StyleProp, StyleSheet, TouchableOpacity, ViewStyle} from 'react-native'
|
|||||||
import {RepostIcon} from 'lib/icons'
|
import {RepostIcon} from 'lib/icons'
|
||||||
import {s, colors} from 'lib/styles'
|
import {s, colors} from 'lib/styles'
|
||||||
import {useTheme} from 'lib/ThemeContext'
|
import {useTheme} from 'lib/ThemeContext'
|
||||||
import {Text} from '../text/Text'
|
|
||||||
import {pluralize} from 'lib/strings/helpers'
|
import {pluralize} from 'lib/strings/helpers'
|
||||||
import {HITSLOP_10, HITSLOP_20} from 'lib/constants'
|
import {HITSLOP_10, HITSLOP_20} from 'lib/constants'
|
||||||
import {useModalControls} from '#/state/modals'
|
import {useModalControls} from '#/state/modals'
|
||||||
import {useRequireAuth} from '#/state/session'
|
import {useRequireAuth} from '#/state/session'
|
||||||
|
import {Pressable, Text} from '#/alf'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isReposted: boolean
|
isReposted: boolean
|
||||||
@@ -45,12 +45,13 @@ let RepostButton = ({
|
|||||||
}, [onRepost, onQuote, isReposted, openModal])
|
}, [onRepost, onQuote, isReposted, openModal])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<Pressable
|
||||||
|
row aic
|
||||||
|
pa={!big ? 'xs' : 0}
|
||||||
testID="repostBtn"
|
testID="repostBtn"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
requireAuth(() => onPressToggleRepostWrapper())
|
requireAuth(() => onPressToggleRepostWrapper())
|
||||||
}}
|
}}
|
||||||
style={[styles.control, !big && styles.controlPad]}
|
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={`${
|
accessibilityLabel={`${
|
||||||
isReposted ? 'Undo repost' : 'Repost'
|
isReposted ? 'Undo repost' : 'Repost'
|
||||||
@@ -77,7 +78,7 @@ let RepostButton = ({
|
|||||||
{repostCount}
|
{repostCount}
|
||||||
</Text>
|
</Text>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</TouchableOpacity>
|
</Pressable>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
RepostButton = memo(RepostButton)
|
RepostButton = memo(RepostButton)
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import React, {useMemo, useCallback} from 'react'
|
import React, {useMemo, useCallback} from 'react'
|
||||||
import {Dimensions, StyleSheet, View, ActivityIndicator} from 'react-native'
|
import {Dimensions, View, ActivityIndicator} from 'react-native'
|
||||||
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||||
import {useIsFocused, useNavigation} from '@react-navigation/native'
|
import {useIsFocused, useNavigation} from '@react-navigation/native'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
|
||||||
import {HeartIcon, HeartIconSolid} from 'lib/icons'
|
import {HeartIcon, HeartIconSolid} from 'lib/icons'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {CommonNavigatorParams} from 'lib/routes/types'
|
import {CommonNavigatorParams} from 'lib/routes/types'
|
||||||
@@ -15,8 +14,6 @@ import {ProfileSubpageHeader} from 'view/com/profile/ProfileSubpageHeader'
|
|||||||
import {Feed} from 'view/com/posts/Feed'
|
import {Feed} from 'view/com/posts/Feed'
|
||||||
import {TextLink} from 'view/com/util/Link'
|
import {TextLink} from 'view/com/util/Link'
|
||||||
import {ListRef} from 'view/com/util/List'
|
import {ListRef} from 'view/com/util/List'
|
||||||
import {Button} from 'view/com/util/forms/Button'
|
|
||||||
import {Text} from 'view/com/util/text/Text'
|
|
||||||
import {RichText} from 'view/com/util/text/RichText'
|
import {RichText} from 'view/com/util/text/RichText'
|
||||||
import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn'
|
import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn'
|
||||||
import {FAB} from 'view/com/util/fab/FAB'
|
import {FAB} from 'view/com/util/fab/FAB'
|
||||||
@@ -58,6 +55,8 @@ import {useComposerControls} from '#/state/shell/composer'
|
|||||||
import {truncateAndInvalidate} from '#/state/queries/util'
|
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||||
import {isNative} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
|
|
||||||
|
import {Box, Text, Button, Pressable, web, native, useTokens} from '#/alf'
|
||||||
|
|
||||||
const SECTION_TITLES = ['Posts', 'About']
|
const SECTION_TITLES = ['Posts', 'About']
|
||||||
|
|
||||||
interface SectionRef {
|
interface SectionRef {
|
||||||
@@ -68,7 +67,6 @@ type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFeed'>
|
|||||||
export function ProfileFeedScreen(props: Props) {
|
export function ProfileFeedScreen(props: Props) {
|
||||||
const {rkey, name: handleOrDid} = props.route.params
|
const {rkey, name: handleOrDid} = props.route.params
|
||||||
|
|
||||||
const pal = usePalette('default')
|
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
|
|
||||||
@@ -89,27 +87,26 @@ export function ProfileFeedScreen(props: Props) {
|
|||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<CenteredView>
|
<CenteredView>
|
||||||
<View style={[pal.view, pal.border, styles.notFoundContainer]}>
|
<Box bg='l0' borderColor='l4' ma='m' px='l' py='m' radius='s'>
|
||||||
<Text type="title-lg" style={[pal.text, s.mb10]}>
|
<Text fontSize='l' mb='m'>
|
||||||
<Trans>Could not load feed</Trans>
|
<Trans>Could not load feed</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Text type="md" style={[pal.text, s.mb20]}>
|
<Text fontSize='m' mb='xl'>
|
||||||
{error.toString()}
|
{error.toString()}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View style={{flexDirection: 'row'}}>
|
<Box row>
|
||||||
<Button
|
<Button
|
||||||
type="default"
|
|
||||||
accessibilityLabel={_(msg`Go Back`)}
|
accessibilityLabel={_(msg`Go Back`)}
|
||||||
accessibilityHint="Return to previous page"
|
accessibilityHint="Return to previous page"
|
||||||
onPress={onPressBack}
|
onPress={onPressBack}
|
||||||
style={{flexShrink: 1}}>
|
style={{flexShrink: 1}}>
|
||||||
<Text type="button" style={pal.text}>
|
<Text c='l0' fontWeight='bold'>
|
||||||
<Trans>Go Back</Trans>
|
<Trans>Go Back</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</Box>
|
||||||
</View>
|
</Box>
|
||||||
</CenteredView>
|
</CenteredView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -118,9 +115,9 @@ export function ProfileFeedScreen(props: Props) {
|
|||||||
<ProfileFeedScreenIntermediate feedUri={resolvedUri.uri} />
|
<ProfileFeedScreenIntermediate feedUri={resolvedUri.uri} />
|
||||||
) : (
|
) : (
|
||||||
<CenteredView>
|
<CenteredView>
|
||||||
<View style={s.p20}>
|
<Box pa='xl'>
|
||||||
<ActivityIndicator size="large" />
|
<ActivityIndicator size="large" />
|
||||||
</View>
|
</Box>
|
||||||
</CenteredView>
|
</CenteredView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -132,9 +129,9 @@ function ProfileFeedScreenIntermediate({feedUri}: {feedUri: string}) {
|
|||||||
if (!preferences || !info) {
|
if (!preferences || !info) {
|
||||||
return (
|
return (
|
||||||
<CenteredView>
|
<CenteredView>
|
||||||
<View style={s.p20}>
|
<Box pa='xl'>
|
||||||
<ActivityIndicator size="large" />
|
<ActivityIndicator size="large" />
|
||||||
</View>
|
</Box>
|
||||||
</CenteredView>
|
</CenteredView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -155,7 +152,7 @@ export function ProfileFeedScreenInner({
|
|||||||
feedInfo: FeedSourceFeedInfo
|
feedInfo: FeedSourceFeedInfo
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const pal = usePalette('default')
|
const tokens = useTokens()
|
||||||
const {hasSession, currentAccount} = useSession()
|
const {hasSession, currentAccount} = useSession()
|
||||||
const {openModal} = useModalControls()
|
const {openModal} = useModalControls()
|
||||||
const {openComposer} = useComposerControls()
|
const {openComposer} = useComposerControls()
|
||||||
@@ -335,44 +332,47 @@ export function ProfileFeedScreenInner({
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
avatarType="algo">
|
avatarType="algo">
|
||||||
|
<Box row gap='s' jce>
|
||||||
{feedInfo && hasSession && (
|
{feedInfo && hasSession && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
|
size='small'
|
||||||
disabled={isSavePending || isRemovePending}
|
disabled={isSavePending || isRemovePending}
|
||||||
type="default"
|
type="primary"
|
||||||
label={isSaved ? 'Unsave' : 'Save'}
|
|
||||||
onPress={onToggleSaved}
|
onPress={onToggleSaved}
|
||||||
style={styles.btn}
|
>{isSaved ? 'Unsave' : 'Save'}
|
||||||
/>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
size='small'
|
||||||
testID={isPinned ? 'unpinBtn' : 'pinBtn'}
|
testID={isPinned ? 'unpinBtn' : 'pinBtn'}
|
||||||
disabled={isPinPending || isUnpinPending}
|
disabled={isPinPending || isUnpinPending}
|
||||||
type={isPinned ? 'default' : 'inverted'}
|
type={isPinned ? 'secondary' : 'primary'}
|
||||||
label={isPinned ? 'Unpin' : 'Pin to home'}
|
|
||||||
onPress={onTogglePinned}
|
onPress={onTogglePinned}
|
||||||
style={styles.btn}
|
>
|
||||||
/>
|
{isPinned ? 'Unpin' : 'Pin to home'}
|
||||||
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<NativeDropdown
|
<NativeDropdown
|
||||||
testID="headerDropdownBtn"
|
testID="headerDropdownBtn"
|
||||||
items={dropdownItems}
|
items={dropdownItems}
|
||||||
accessibilityLabel={_(msg`More options`)}
|
accessibilityLabel={_(msg`More options`)}
|
||||||
accessibilityHint="">
|
accessibilityHint="">
|
||||||
<View style={[pal.viewLight, styles.btn]}>
|
<Box bg='l1' py='s' px='m' radius='round'>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon="ellipsis"
|
icon="ellipsis"
|
||||||
size={20}
|
size={20}
|
||||||
color={pal.colors.text}
|
color={tokens.color.l4}
|
||||||
/>
|
/>
|
||||||
</View>
|
</Box>
|
||||||
</NativeDropdown>
|
</NativeDropdown>
|
||||||
|
</Box>
|
||||||
</ProfileSubpageHeader>
|
</ProfileSubpageHeader>
|
||||||
)
|
)
|
||||||
}, [
|
}, [
|
||||||
_,
|
_,
|
||||||
hasSession,
|
hasSession,
|
||||||
pal,
|
|
||||||
feedInfo,
|
feedInfo,
|
||||||
isPinned,
|
isPinned,
|
||||||
onTogglePinned,
|
onTogglePinned,
|
||||||
@@ -387,7 +387,7 @@ export function ProfileFeedScreenInner({
|
|||||||
])
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={s.hContentRegion}>
|
<Box h={web('100%')} flex={native(1)}>
|
||||||
<PagerWithHeader
|
<PagerWithHeader
|
||||||
items={SECTION_TITLES}
|
items={SECTION_TITLES}
|
||||||
isHeaderReady={true}
|
isHeaderReady={true}
|
||||||
@@ -431,7 +431,7 @@ export function ProfileFeedScreenInner({
|
|||||||
accessibilityHint=""
|
accessibilityHint=""
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,7 +503,7 @@ function AboutSection({
|
|||||||
scrollElRef: React.MutableRefObject<ScrollView | null>
|
scrollElRef: React.MutableRefObject<ScrollView | null>
|
||||||
isOwner: boolean
|
isOwner: boolean
|
||||||
}) {
|
}) {
|
||||||
const pal = usePalette('default')
|
const tokens = useTokens()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const scrollHandlers = useScrollHandlers()
|
const scrollHandlers = useScrollHandlers()
|
||||||
const onScroll = useAnimatedScrollHandler(scrollHandlers)
|
const onScroll = useAnimatedScrollHandler(scrollHandlers)
|
||||||
@@ -548,52 +548,50 @@ function AboutSection({
|
|||||||
paddingTop: headerHeight,
|
paddingTop: headerHeight,
|
||||||
minHeight: Dimensions.get('window').height * 1.5,
|
minHeight: Dimensions.get('window').height * 1.5,
|
||||||
}}>
|
}}>
|
||||||
<View
|
<Box
|
||||||
style={[
|
borderColor='l2'
|
||||||
{
|
borderTopWidth={1}
|
||||||
borderTopWidth: 1,
|
pa='xl'
|
||||||
paddingVertical: 20,
|
gap='m'
|
||||||
paddingHorizontal: 20,
|
>
|
||||||
gap: 12,
|
|
||||||
},
|
|
||||||
pal.border,
|
|
||||||
]}>
|
|
||||||
{feedInfo.description ? (
|
{feedInfo.description ? (
|
||||||
<RichText
|
<RichText
|
||||||
testID="listDescription"
|
testID="listDescription"
|
||||||
type="lg"
|
type="lg"
|
||||||
style={pal.text}
|
style={{ color: tokens.color.l7 }}
|
||||||
richText={feedInfo.description}
|
richText={feedInfo.description}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Text type="lg" style={[{fontStyle: 'italic'}, pal.textLight]}>
|
<Text fontSize='m' c='l5' fontStyle='italic'>
|
||||||
<Trans>No description</Trans>
|
<Trans>No description</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<View style={{flexDirection: 'row', alignItems: 'center', gap: 10}}>
|
<Box row aic gap='m'>
|
||||||
<Button
|
<Pressable
|
||||||
type="default"
|
|
||||||
testID="toggleLikeBtn"
|
testID="toggleLikeBtn"
|
||||||
accessibilityLabel={_(msg`Like this feed`)}
|
accessibilityLabel={_(msg`Like this feed`)}
|
||||||
accessibilityHint=""
|
accessibilityHint=""
|
||||||
disabled={!hasSession || isLikePending || isUnlikePending}
|
disabled={!hasSession || isLikePending || isUnlikePending}
|
||||||
onPress={onToggleLiked}
|
onPress={onToggleLiked}
|
||||||
style={{paddingHorizontal: 10}}>
|
bg='l1'
|
||||||
|
pa={10}
|
||||||
|
radius='round'
|
||||||
|
>
|
||||||
{isLiked ? (
|
{isLiked ? (
|
||||||
<HeartIconSolid size={19} style={s.likeColor} />
|
<HeartIconSolid size={19} style={s.likeColor} />
|
||||||
) : (
|
) : (
|
||||||
<HeartIcon strokeWidth={3} size={19} style={pal.textLight} />
|
<HeartIcon strokeWidth={3} size={19} style={{ color: tokens.color.l4 }} />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Pressable>
|
||||||
{typeof likeCount === 'number' && (
|
{typeof likeCount === 'number' && (
|
||||||
<TextLink
|
<TextLink
|
||||||
href={makeCustomFeedLink(feedOwnerDid, feedRkey, 'liked-by')}
|
href={makeCustomFeedLink(feedOwnerDid, feedRkey, 'liked-by')}
|
||||||
text={`Liked by ${likeCount} ${pluralize(likeCount, 'user')}`}
|
text={`Liked by ${likeCount} ${pluralize(likeCount, 'user')}`}
|
||||||
style={[pal.textLight, s.semiBold]}
|
style={[{ color: tokens.color.l4, fontWeight: tokens.fontWeight.semi }]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</Box>
|
||||||
<Text type="md" style={[pal.textLight]} numberOfLines={1}>
|
<Text fontSize='m' c='l5' numberOfLines={1}>
|
||||||
Created by{' '}
|
Created by{' '}
|
||||||
{isOwner ? (
|
{isOwner ? (
|
||||||
'you'
|
'you'
|
||||||
@@ -604,29 +602,11 @@ function AboutSection({
|
|||||||
did: feedInfo.creatorDid,
|
did: feedInfo.creatorDid,
|
||||||
handle: feedInfo.creatorHandle,
|
handle: feedInfo.creatorHandle,
|
||||||
})}
|
})}
|
||||||
style={pal.textLight}
|
style={{ color: tokens.color.l4 }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</Box>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
btn: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: 6,
|
|
||||||
paddingVertical: 7,
|
|
||||||
paddingHorizontal: 14,
|
|
||||||
borderRadius: 50,
|
|
||||||
marginLeft: 6,
|
|
||||||
},
|
|
||||||
notFoundContainer: {
|
|
||||||
margin: 10,
|
|
||||||
paddingHorizontal: 18,
|
|
||||||
paddingVertical: 14,
|
|
||||||
borderRadius: 6,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|||||||
Reference in New Issue
Block a user