mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React, {memo, useCallback} from 'react'
2import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native'
3import {AppBskyActorDefs, ModerationDecision, ModerationUI} from '@atproto/api'
4import {useLingui} from '@lingui/react'
5import {useQueryClient} from '@tanstack/react-query'
6
7import {precacheProfile} from '#/state/queries/profile'
8import {usePalette} from 'lib/hooks/usePalette'
9import {makeProfileLink} from 'lib/routes/links'
10import {forceLTR} from 'lib/strings/bidi'
11import {NON_BREAKING_SPACE} from 'lib/strings/constants'
12import {sanitizeDisplayName} from 'lib/strings/display-names'
13import {sanitizeHandle} from 'lib/strings/handles'
14import {niceDate} from 'lib/strings/time'
15import {TypographyVariant} from 'lib/ThemeContext'
16import {isAndroid} from 'platform/detection'
17import {ProfileHoverCard} from '#/components/ProfileHoverCard'
18import {TextLinkOnWebOnly} from './Link'
19import {Text} from './text/Text'
20import {TimeElapsed} from './TimeElapsed'
21import {PreviewableUserAvatar} from './UserAvatar'
22
23interface PostMetaOpts {
24 author: AppBskyActorDefs.ProfileViewBasic
25 moderation: ModerationDecision | undefined
26 authorHasWarning: boolean
27 postHref: string
28 timestamp: string
29 showAvatar?: boolean
30 avatarModeration?: ModerationUI
31 avatarSize?: number
32 displayNameType?: TypographyVariant
33 displayNameStyle?: StyleProp<TextStyle>
34 onOpenAuthor?: () => void
35 style?: StyleProp<ViewStyle>
36}
37
38let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
39 const {i18n} = useLingui()
40
41 const pal = usePalette('default')
42 const displayName = opts.author.displayName || opts.author.handle
43 const handle = opts.author.handle
44 const profileLink = makeProfileLink(opts.author)
45 const queryClient = useQueryClient()
46 const onOpenAuthor = opts.onOpenAuthor
47 const onBeforePressAuthor = useCallback(() => {
48 precacheProfile(queryClient, opts.author)
49 onOpenAuthor?.()
50 }, [queryClient, opts.author, onOpenAuthor])
51 const onBeforePressPost = useCallback(() => {
52 precacheProfile(queryClient, opts.author)
53 }, [queryClient, opts.author])
54
55 return (
56 <View style={[styles.container, opts.style]}>
57 {opts.showAvatar && (
58 <View style={styles.avatar}>
59 <PreviewableUserAvatar
60 size={opts.avatarSize || 16}
61 profile={opts.author}
62 moderation={opts.avatarModeration}
63 type={opts.author.associated?.labeler ? 'labeler' : 'user'}
64 />
65 </View>
66 )}
67 <ProfileHoverCard inline did={opts.author.did}>
68 <Text
69 numberOfLines={1}
70 style={[styles.maxWidth, pal.textLight, opts.displayNameStyle]}>
71 <TextLinkOnWebOnly
72 type={opts.displayNameType || 'lg-bold'}
73 style={[pal.text]}
74 lineHeight={1.2}
75 disableMismatchWarning
76 text={forceLTR(
77 sanitizeDisplayName(
78 displayName,
79 opts.moderation?.ui('displayName'),
80 ),
81 )}
82 href={profileLink}
83 onBeforePress={onBeforePressAuthor}
84 />
85 <TextLinkOnWebOnly
86 type="md"
87 disableMismatchWarning
88 style={[pal.textLight, {flexShrink: 4}]}
89 text={NON_BREAKING_SPACE + sanitizeHandle(handle, '@')}
90 href={profileLink}
91 onBeforePress={onBeforePressAuthor}
92 anchorNoUnderline
93 />
94 </Text>
95 </ProfileHoverCard>
96 {!isAndroid && (
97 <Text type="md" style={pal.textLight} accessible={false}>
98 ·
99 </Text>
100 )}
101 <TimeElapsed timestamp={opts.timestamp}>
102 {({timeElapsed}) => (
103 <TextLinkOnWebOnly
104 type="md"
105 style={pal.textLight}
106 text={timeElapsed}
107 accessibilityLabel={niceDate(i18n, opts.timestamp)}
108 title={niceDate(i18n, opts.timestamp)}
109 accessibilityHint=""
110 href={opts.postHref}
111 onBeforePress={onBeforePressPost}
112 />
113 )}
114 </TimeElapsed>
115 </View>
116 )
117}
118PostMeta = memo(PostMeta)
119export {PostMeta}
120
121const styles = StyleSheet.create({
122 container: {
123 flexDirection: 'row',
124 alignItems: 'flex-end',
125 paddingBottom: 2,
126 gap: 4,
127 zIndex: 1,
128 flex: 1,
129 },
130 avatar: {
131 alignSelf: 'center',
132 },
133 maxWidth: {
134 flex: isAndroid ? 1 : undefined,
135 flexShrink: isAndroid ? undefined : 1,
136 },
137})