mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at verify-code 4.4 kB view raw
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 &middot; 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})