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