Split FeedSlice into FlatList rows (#6507)
This commit is contained in:
+116
-27
@@ -42,10 +42,11 @@ import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
|||||||
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
|
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
|
||||||
import {DiscoverFallbackHeader} from './DiscoverFallbackHeader'
|
import {DiscoverFallbackHeader} from './DiscoverFallbackHeader'
|
||||||
import {FeedErrorMessage} from './FeedErrorMessage'
|
import {FeedErrorMessage} from './FeedErrorMessage'
|
||||||
|
import {FeedItem} from './FeedItem'
|
||||||
import {FeedShutdownMsg} from './FeedShutdownMsg'
|
import {FeedShutdownMsg} from './FeedShutdownMsg'
|
||||||
import {FeedSlice} from './FeedSlice'
|
import {ViewFullThread} from './ViewFullThread'
|
||||||
|
|
||||||
type FeedItem =
|
type FeedRow =
|
||||||
| {
|
| {
|
||||||
type: 'loading'
|
type: 'loading'
|
||||||
key: string
|
key: string
|
||||||
@@ -71,6 +72,18 @@ type FeedItem =
|
|||||||
key: string
|
key: string
|
||||||
slice: FeedPostSlice
|
slice: FeedPostSlice
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: 'sliceItem'
|
||||||
|
key: string
|
||||||
|
slice: FeedPostSlice
|
||||||
|
indexInSlice: number
|
||||||
|
showReplyTo: boolean
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'sliceViewFullThread'
|
||||||
|
key: string
|
||||||
|
uri: string
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
type: 'interstitialFeeds'
|
type: 'interstitialFeeds'
|
||||||
key: string
|
key: string
|
||||||
@@ -101,7 +114,7 @@ const followInterstitialType = 'interstitialFollows'
|
|||||||
const progressGuideInterstitialType = 'interstitialProgressGuide'
|
const progressGuideInterstitialType = 'interstitialProgressGuide'
|
||||||
const interstials: Record<
|
const interstials: Record<
|
||||||
'following' | 'discover' | 'profile',
|
'following' | 'discover' | 'profile',
|
||||||
(FeedItem & {
|
(FeedRow & {
|
||||||
type:
|
type:
|
||||||
| 'interstitialFeeds'
|
| 'interstitialFeeds'
|
||||||
| 'interstitialFollows'
|
| 'interstitialFollows'
|
||||||
@@ -139,9 +152,9 @@ const interstials: Record<
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFeedPostSlice(feedItem: FeedItem): FeedPostSlice | null {
|
export function getFeedPostSlice(feedRow: FeedRow): FeedPostSlice | null {
|
||||||
if (feedItem.type === 'slice') {
|
if (feedRow.type === 'sliceItem') {
|
||||||
return feedItem.slice
|
return feedRow.slice
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -303,8 +316,8 @@ let Feed = ({
|
|||||||
}
|
}
|
||||||
}, [pollInterval])
|
}, [pollInterval])
|
||||||
|
|
||||||
const feedItems: FeedItem[] = React.useMemo(() => {
|
const feedItems: FeedRow[] = React.useMemo(() => {
|
||||||
let arr: FeedItem[] = []
|
let arr: FeedRow[] = []
|
||||||
if (KNOWN_SHUTDOWN_FEEDS.includes(feedUri)) {
|
if (KNOWN_SHUTDOWN_FEEDS.includes(feedUri)) {
|
||||||
arr.push({
|
arr.push({
|
||||||
type: 'feedShutdownMsg',
|
type: 'feedShutdownMsg',
|
||||||
@@ -324,13 +337,50 @@ let Feed = ({
|
|||||||
})
|
})
|
||||||
} else if (data) {
|
} else if (data) {
|
||||||
for (const page of data?.pages) {
|
for (const page of data?.pages) {
|
||||||
arr = arr.concat(
|
for (const slice of page.slices) {
|
||||||
page.slices.map(s => ({
|
if (slice.isIncompleteThread && slice.items.length >= 3) {
|
||||||
type: 'slice',
|
const beforeLast = slice.items.length - 2
|
||||||
slice: s,
|
const last = slice.items.length - 1
|
||||||
key: s._reactKey,
|
arr.push({
|
||||||
})),
|
type: 'sliceItem',
|
||||||
)
|
key: slice.items[0]._reactKey,
|
||||||
|
slice: slice,
|
||||||
|
indexInSlice: 0,
|
||||||
|
showReplyTo: false,
|
||||||
|
})
|
||||||
|
arr.push({
|
||||||
|
type: 'sliceViewFullThread',
|
||||||
|
key: slice._reactKey + '-viewFullThread',
|
||||||
|
uri: slice.items[0].uri,
|
||||||
|
})
|
||||||
|
arr.push({
|
||||||
|
type: 'sliceItem',
|
||||||
|
key: slice.items[beforeLast]._reactKey,
|
||||||
|
slice: slice,
|
||||||
|
indexInSlice: beforeLast,
|
||||||
|
showReplyTo:
|
||||||
|
slice.items[beforeLast].parentAuthor?.did !==
|
||||||
|
slice.items[beforeLast].post.author.did,
|
||||||
|
})
|
||||||
|
arr.push({
|
||||||
|
type: 'sliceItem',
|
||||||
|
key: slice.items[last]._reactKey,
|
||||||
|
slice: slice,
|
||||||
|
indexInSlice: last,
|
||||||
|
showReplyTo: false,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < slice.items.length; i++) {
|
||||||
|
arr.push({
|
||||||
|
type: 'sliceItem',
|
||||||
|
key: slice.items[i]._reactKey,
|
||||||
|
slice: slice,
|
||||||
|
indexInSlice: i,
|
||||||
|
showReplyTo: i === 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isError && !isEmpty) {
|
if (isError && !isEmpty) {
|
||||||
@@ -454,10 +504,10 @@ let Feed = ({
|
|||||||
// =
|
// =
|
||||||
|
|
||||||
const renderItem = React.useCallback(
|
const renderItem = React.useCallback(
|
||||||
({item, index}: ListRenderItemInfo<FeedItem>) => {
|
({item: row, index: rowIndex}: ListRenderItemInfo<FeedRow>) => {
|
||||||
if (item.type === 'empty') {
|
if (row.type === 'empty') {
|
||||||
return renderEmptyState()
|
return renderEmptyState()
|
||||||
} else if (item.type === 'error') {
|
} else if (row.type === 'error') {
|
||||||
return (
|
return (
|
||||||
<FeedErrorMessage
|
<FeedErrorMessage
|
||||||
feedDesc={feed}
|
feedDesc={feed}
|
||||||
@@ -466,7 +516,7 @@ let Feed = ({
|
|||||||
savedFeedConfig={savedFeedConfig}
|
savedFeedConfig={savedFeedConfig}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else if (item.type === 'loadMoreError') {
|
} else if (row.type === 'loadMoreError') {
|
||||||
return (
|
return (
|
||||||
<LoadMoreRetryBtn
|
<LoadMoreRetryBtn
|
||||||
label={_(
|
label={_(
|
||||||
@@ -475,25 +525,50 @@ let Feed = ({
|
|||||||
onPress={onPressRetryLoadMore}
|
onPress={onPressRetryLoadMore}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else if (item.type === 'loading') {
|
} else if (row.type === 'loading') {
|
||||||
return <PostFeedLoadingPlaceholder />
|
return <PostFeedLoadingPlaceholder />
|
||||||
} else if (item.type === 'feedShutdownMsg') {
|
} else if (row.type === 'feedShutdownMsg') {
|
||||||
return <FeedShutdownMsg feedUri={feedUri} />
|
return <FeedShutdownMsg feedUri={feedUri} />
|
||||||
} else if (item.type === feedInterstitialType) {
|
} else if (row.type === feedInterstitialType) {
|
||||||
return <SuggestedFeeds />
|
return <SuggestedFeeds />
|
||||||
} else if (item.type === followInterstitialType) {
|
} else if (row.type === followInterstitialType) {
|
||||||
return <SuggestedFollows feed={feed} />
|
return <SuggestedFollows feed={feed} />
|
||||||
} else if (item.type === progressGuideInterstitialType) {
|
} else if (row.type === progressGuideInterstitialType) {
|
||||||
return <ProgressGuide />
|
return <ProgressGuide />
|
||||||
} else if (item.type === 'slice') {
|
} else if (row.type === 'sliceItem') {
|
||||||
if (item.slice.isFallbackMarker) {
|
const slice = row.slice
|
||||||
|
if (slice.isFallbackMarker) {
|
||||||
// HACK
|
// HACK
|
||||||
// tell the user we fell back to discover
|
// tell the user we fell back to discover
|
||||||
// see home.ts (feed api) for more info
|
// see home.ts (feed api) for more info
|
||||||
// -prf
|
// -prf
|
||||||
return <DiscoverFallbackHeader />
|
return <DiscoverFallbackHeader />
|
||||||
}
|
}
|
||||||
return <FeedSlice slice={item.slice} hideTopBorder={index === 0} />
|
const indexInSlice = row.indexInSlice
|
||||||
|
const item = slice.items[indexInSlice]
|
||||||
|
return (
|
||||||
|
<FeedItem
|
||||||
|
post={item.post}
|
||||||
|
record={item.record}
|
||||||
|
reason={indexInSlice === 0 ? slice.reason : undefined}
|
||||||
|
feedContext={slice.feedContext}
|
||||||
|
moderation={item.moderation}
|
||||||
|
parentAuthor={item.parentAuthor}
|
||||||
|
showReplyTo={row.showReplyTo}
|
||||||
|
isThreadParent={isThreadParentAt(slice.items, indexInSlice)}
|
||||||
|
isThreadChild={isThreadChildAt(slice.items, indexInSlice)}
|
||||||
|
isThreadLastChild={
|
||||||
|
isThreadChildAt(slice.items, indexInSlice) &&
|
||||||
|
slice.items.length === indexInSlice + 1
|
||||||
|
}
|
||||||
|
isParentBlocked={item.isParentBlocked}
|
||||||
|
isParentNotFound={item.isParentNotFound}
|
||||||
|
hideTopBorder={rowIndex === 0 && indexInSlice === 0}
|
||||||
|
rootPost={slice.items[0].post}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else if (row.type === 'sliceViewFullThread') {
|
||||||
|
return <ViewFullThread uri={row.uri} />
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -574,3 +649,17 @@ export {Feed}
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
feedFooter: {paddingTop: 20},
|
feedFooter: {paddingTop: 20},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function isThreadParentAt<T>(arr: Array<T>, i: number) {
|
||||||
|
if (arr.length === 1) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return i < arr.length - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function isThreadChildAt<T>(arr: Array<T>, i: number) {
|
||||||
|
if (arr.length === 1) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return i > 0
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,184 +0,0 @@
|
|||||||
import React, {memo} from 'react'
|
|
||||||
import {StyleSheet, View} from 'react-native'
|
|
||||||
import Svg, {Circle, Line} from 'react-native-svg'
|
|
||||||
import {AtUri} from '@atproto/api'
|
|
||||||
import {Trans} from '@lingui/macro'
|
|
||||||
|
|
||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
|
||||||
import {makeProfileLink} from '#/lib/routes/links'
|
|
||||||
import {FeedPostSlice} from '#/state/queries/post-feed'
|
|
||||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
|
||||||
import {SubtleWebHover} from '#/components/SubtleWebHover'
|
|
||||||
import {Link} from '../util/Link'
|
|
||||||
import {Text} from '../util/text/Text'
|
|
||||||
import {FeedItem} from './FeedItem'
|
|
||||||
|
|
||||||
let FeedSlice = ({
|
|
||||||
slice,
|
|
||||||
hideTopBorder,
|
|
||||||
}: {
|
|
||||||
slice: FeedPostSlice
|
|
||||||
hideTopBorder?: boolean
|
|
||||||
}): React.ReactNode => {
|
|
||||||
if (slice.isIncompleteThread && slice.items.length >= 3) {
|
|
||||||
const beforeLast = slice.items.length - 2
|
|
||||||
const last = slice.items.length - 1
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<FeedItem
|
|
||||||
key={slice.items[0]._reactKey}
|
|
||||||
post={slice.items[0].post}
|
|
||||||
record={slice.items[0].record}
|
|
||||||
reason={slice.reason}
|
|
||||||
feedContext={slice.feedContext}
|
|
||||||
parentAuthor={slice.items[0].parentAuthor}
|
|
||||||
showReplyTo={false}
|
|
||||||
moderation={slice.items[0].moderation}
|
|
||||||
isThreadParent={isThreadParentAt(slice.items, 0)}
|
|
||||||
isThreadChild={isThreadChildAt(slice.items, 0)}
|
|
||||||
hideTopBorder={hideTopBorder}
|
|
||||||
isParentBlocked={slice.items[0].isParentBlocked}
|
|
||||||
isParentNotFound={slice.items[0].isParentNotFound}
|
|
||||||
rootPost={slice.items[0].post}
|
|
||||||
/>
|
|
||||||
<ViewFullThread uri={slice.items[0].uri} />
|
|
||||||
<FeedItem
|
|
||||||
key={slice.items[beforeLast]._reactKey}
|
|
||||||
post={slice.items[beforeLast].post}
|
|
||||||
record={slice.items[beforeLast].record}
|
|
||||||
reason={undefined}
|
|
||||||
feedContext={slice.feedContext}
|
|
||||||
parentAuthor={slice.items[beforeLast].parentAuthor}
|
|
||||||
showReplyTo={
|
|
||||||
slice.items[beforeLast].parentAuthor?.did !==
|
|
||||||
slice.items[beforeLast].post.author.did
|
|
||||||
}
|
|
||||||
moderation={slice.items[beforeLast].moderation}
|
|
||||||
isThreadParent={isThreadParentAt(slice.items, beforeLast)}
|
|
||||||
isThreadChild={isThreadChildAt(slice.items, beforeLast)}
|
|
||||||
isParentBlocked={slice.items[beforeLast].isParentBlocked}
|
|
||||||
isParentNotFound={slice.items[beforeLast].isParentNotFound}
|
|
||||||
rootPost={slice.items[0].post}
|
|
||||||
/>
|
|
||||||
<FeedItem
|
|
||||||
key={slice.items[last]._reactKey}
|
|
||||||
post={slice.items[last].post}
|
|
||||||
record={slice.items[last].record}
|
|
||||||
reason={undefined}
|
|
||||||
feedContext={slice.feedContext}
|
|
||||||
parentAuthor={slice.items[last].parentAuthor}
|
|
||||||
showReplyTo={false}
|
|
||||||
moderation={slice.items[last].moderation}
|
|
||||||
isThreadParent={isThreadParentAt(slice.items, last)}
|
|
||||||
isThreadChild={isThreadChildAt(slice.items, last)}
|
|
||||||
isParentBlocked={slice.items[last].isParentBlocked}
|
|
||||||
isParentNotFound={slice.items[last].isParentNotFound}
|
|
||||||
isThreadLastChild
|
|
||||||
rootPost={slice.items[0].post}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{slice.items.map((item, i) => (
|
|
||||||
<FeedItem
|
|
||||||
key={item._reactKey}
|
|
||||||
post={slice.items[i].post}
|
|
||||||
record={slice.items[i].record}
|
|
||||||
reason={i === 0 ? slice.reason : undefined}
|
|
||||||
feedContext={slice.feedContext}
|
|
||||||
moderation={slice.items[i].moderation}
|
|
||||||
parentAuthor={slice.items[i].parentAuthor}
|
|
||||||
showReplyTo={i === 0}
|
|
||||||
isThreadParent={isThreadParentAt(slice.items, i)}
|
|
||||||
isThreadChild={isThreadChildAt(slice.items, i)}
|
|
||||||
isThreadLastChild={
|
|
||||||
isThreadChildAt(slice.items, i) && slice.items.length === i + 1
|
|
||||||
}
|
|
||||||
isParentBlocked={slice.items[i].isParentBlocked}
|
|
||||||
isParentNotFound={slice.items[i].isParentNotFound}
|
|
||||||
hideTopBorder={hideTopBorder && i === 0}
|
|
||||||
rootPost={slice.items[0].post}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
FeedSlice = memo(FeedSlice)
|
|
||||||
export {FeedSlice}
|
|
||||||
|
|
||||||
function ViewFullThread({uri}: {uri: string}) {
|
|
||||||
const {
|
|
||||||
state: hover,
|
|
||||||
onIn: onHoverIn,
|
|
||||||
onOut: onHoverOut,
|
|
||||||
} = useInteractionState()
|
|
||||||
const pal = usePalette('default')
|
|
||||||
const itemHref = React.useMemo(() => {
|
|
||||||
const urip = new AtUri(uri)
|
|
||||||
return makeProfileLink({did: urip.hostname, handle: ''}, 'post', urip.rkey)
|
|
||||||
}, [uri])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
style={[styles.viewFullThread]}
|
|
||||||
href={itemHref}
|
|
||||||
asAnchor
|
|
||||||
noFeedback
|
|
||||||
onPointerEnter={onHoverIn}
|
|
||||||
onPointerLeave={onHoverOut}>
|
|
||||||
<SubtleWebHover
|
|
||||||
hover={hover}
|
|
||||||
// adjust position for visual alignment - the actual box has lots of top padding and not much bottom padding -sfn
|
|
||||||
style={{top: 8, bottom: -5}}
|
|
||||||
/>
|
|
||||||
<View style={styles.viewFullThreadDots}>
|
|
||||||
<Svg width="4" height="40">
|
|
||||||
<Line
|
|
||||||
x1="2"
|
|
||||||
y1="0"
|
|
||||||
x2="2"
|
|
||||||
y2="15"
|
|
||||||
stroke={pal.colors.replyLine}
|
|
||||||
strokeWidth="2"
|
|
||||||
/>
|
|
||||||
<Circle cx="2" cy="22" r="1.5" fill={pal.colors.replyLineDot} />
|
|
||||||
<Circle cx="2" cy="28" r="1.5" fill={pal.colors.replyLineDot} />
|
|
||||||
<Circle cx="2" cy="34" r="1.5" fill={pal.colors.replyLineDot} />
|
|
||||||
</Svg>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Text type="md" style={[pal.link, {paddingTop: 18, paddingBottom: 4}]}>
|
|
||||||
<Trans>View full thread</Trans>
|
|
||||||
</Text>
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
viewFullThread: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
gap: 10,
|
|
||||||
paddingLeft: 18,
|
|
||||||
},
|
|
||||||
viewFullThreadDots: {
|
|
||||||
width: 42,
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
function isThreadParentAt<T>(arr: Array<T>, i: number) {
|
|
||||||
if (arr.length === 1) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return i < arr.length - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function isThreadChildAt<T>(arr: Array<T>, i: number) {
|
|
||||||
if (arr.length === 1) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return i > 0
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import {StyleSheet, View} from 'react-native'
|
||||||
|
import Svg, {Circle, Line} from 'react-native-svg'
|
||||||
|
import {AtUri} from '@atproto/api'
|
||||||
|
import {Trans} from '@lingui/macro'
|
||||||
|
|
||||||
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
|
import {makeProfileLink} from '#/lib/routes/links'
|
||||||
|
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||||
|
import {SubtleWebHover} from '#/components/SubtleWebHover'
|
||||||
|
import {Link} from '../util/Link'
|
||||||
|
import {Text} from '../util/text/Text'
|
||||||
|
|
||||||
|
export function ViewFullThread({uri}: {uri: string}) {
|
||||||
|
const {
|
||||||
|
state: hover,
|
||||||
|
onIn: onHoverIn,
|
||||||
|
onOut: onHoverOut,
|
||||||
|
} = useInteractionState()
|
||||||
|
const pal = usePalette('default')
|
||||||
|
const itemHref = React.useMemo(() => {
|
||||||
|
const urip = new AtUri(uri)
|
||||||
|
return makeProfileLink({did: urip.hostname, handle: ''}, 'post', urip.rkey)
|
||||||
|
}, [uri])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
style={[styles.viewFullThread]}
|
||||||
|
href={itemHref}
|
||||||
|
asAnchor
|
||||||
|
noFeedback
|
||||||
|
onPointerEnter={onHoverIn}
|
||||||
|
onPointerLeave={onHoverOut}>
|
||||||
|
<SubtleWebHover
|
||||||
|
hover={hover}
|
||||||
|
// adjust position for visual alignment - the actual box has lots of top padding and not much bottom padding -sfn
|
||||||
|
style={{top: 8, bottom: -5}}
|
||||||
|
/>
|
||||||
|
<View style={styles.viewFullThreadDots}>
|
||||||
|
<Svg width="4" height="40">
|
||||||
|
<Line
|
||||||
|
x1="2"
|
||||||
|
y1="0"
|
||||||
|
x2="2"
|
||||||
|
y2="15"
|
||||||
|
stroke={pal.colors.replyLine}
|
||||||
|
strokeWidth="2"
|
||||||
|
/>
|
||||||
|
<Circle cx="2" cy="22" r="1.5" fill={pal.colors.replyLineDot} />
|
||||||
|
<Circle cx="2" cy="28" r="1.5" fill={pal.colors.replyLineDot} />
|
||||||
|
<Circle cx="2" cy="34" r="1.5" fill={pal.colors.replyLineDot} />
|
||||||
|
</Svg>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text type="md" style={[pal.link, {paddingTop: 18, paddingBottom: 4}]}>
|
||||||
|
<Trans>View full thread</Trans>
|
||||||
|
</Text>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
viewFullThread: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: 10,
|
||||||
|
paddingLeft: 18,
|
||||||
|
},
|
||||||
|
viewFullThreadDots: {
|
||||||
|
width: 42,
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user