Bluesky app fork with some witchin' additions 馃挮
at main 182 lines 5.3 kB view raw
1import {useMemo, useState} from 'react' 2import {View} from 'react-native' 3import {type AppBskyActorDefs, moderateProfile} from '@atproto/api' 4import {msg, Trans} from '@lingui/macro' 5import {useLingui} from '@lingui/react' 6import {differenceInSeconds} from 'date-fns' 7 8import {HITSLOP_10} from '#/lib/constants' 9import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo' 10import {sanitizeDisplayName} from '#/lib/strings/display-names' 11import {useModerationOpts} from '#/state/preferences/moderation-opts' 12import {useSession} from '#/state/session' 13import {atoms as a, useTheme, web} from '#/alf' 14import {Button, ButtonText} from '#/components/Button' 15import * as Dialog from '#/components/Dialog' 16import {useDialogControl} from '#/components/Dialog' 17import {Newskie} from '#/components/icons/Newskie' 18import * as StarterPackCard from '#/components/StarterPack/StarterPackCard' 19import {Text} from '#/components/Typography' 20import {IS_NATIVE} from '#/env' 21 22export function NewskieDialog({ 23 profile, 24 disabled, 25}: { 26 profile: AppBskyActorDefs.ProfileViewDetailed 27 disabled?: boolean 28}) { 29 const {_} = useLingui() 30 const control = useDialogControl() 31 32 const createdAt = profile.createdAt as string | undefined 33 34 const [now] = useState(() => Date.now()) 35 const daysOld = useMemo(() => { 36 if (!createdAt) return Infinity 37 return differenceInSeconds(now, new Date(createdAt)) / 86400 38 }, [createdAt, now]) 39 40 if (!createdAt || daysOld > 7) return null 41 42 return ( 43 <View style={[a.pr_2xs]}> 44 <Button 45 disabled={disabled} 46 label={_( 47 msg`This user is new here. Press for more info about when they joined.`, 48 )} 49 hitSlop={HITSLOP_10} 50 onPress={control.open}> 51 {({hovered, pressed}) => ( 52 <Newskie 53 size="lg" 54 fill="#FFC404" 55 style={{ 56 opacity: hovered || pressed ? 0.5 : 1, 57 }} 58 /> 59 )} 60 </Button> 61 62 <Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}> 63 <Dialog.Handle /> 64 <DialogInner profile={profile} createdAt={createdAt} now={now} /> 65 </Dialog.Outer> 66 </View> 67 ) 68} 69 70function DialogInner({ 71 profile, 72 createdAt, 73 now, 74}: { 75 profile: AppBskyActorDefs.ProfileViewDetailed 76 createdAt: string 77 now: number 78}) { 79 const control = Dialog.useDialogContext() 80 const {_} = useLingui() 81 const t = useTheme() 82 const moderationOpts = useModerationOpts() 83 const {currentAccount} = useSession() 84 const timeAgo = useGetTimeAgo() 85 const isMe = profile.did === currentAccount?.did 86 87 const profileName = useMemo(() => { 88 if (!moderationOpts) return profile.displayName || profile.handle 89 const moderation = moderateProfile(profile, moderationOpts) 90 return sanitizeDisplayName( 91 profile.displayName || profile.handle, 92 moderation.ui('displayName'), 93 ) 94 }, [moderationOpts, profile]) 95 96 const getJoinMessage = () => { 97 const timeAgoString = timeAgo(createdAt, now, {format: 'long'}) 98 99 if (isMe) { 100 if (profile.joinedViaStarterPack) { 101 return _( 102 msg`You joined Bluesky using a starter pack ${timeAgoString} ago`, 103 ) 104 } else { 105 return _(msg`You joined Bluesky ${timeAgoString} ago`) 106 } 107 } else { 108 if (profile.joinedViaStarterPack) { 109 return _( 110 msg`${profileName} joined Bluesky using a starter pack ${timeAgoString} ago`, 111 ) 112 } else { 113 return _(msg`${profileName} joined Bluesky ${timeAgoString} ago`) 114 } 115 } 116 } 117 118 return ( 119 <Dialog.ScrollableInner 120 label={_(msg`New user info dialog`)} 121 style={web({maxWidth: 400})}> 122 <View style={[a.gap_md]}> 123 <View style={[a.align_center]}> 124 <View 125 style={[ 126 { 127 height: 60, 128 width: 64, 129 }, 130 ]}> 131 <Newskie 132 width={64} 133 height={64} 134 fill="#FFC404" 135 style={[a.absolute, a.inset_0]} 136 /> 137 </View> 138 <Text style={[a.font_semi_bold, a.text_xl]}> 139 {isMe ? <Trans>Welcome, friend!</Trans> : <Trans>Say hello!</Trans>} 140 </Text> 141 </View> 142 <Text style={[a.text_md, a.text_center, a.leading_snug]}> 143 {getJoinMessage()} 144 </Text> 145 {profile.joinedViaStarterPack ? ( 146 <StarterPackCard.Link 147 starterPack={profile.joinedViaStarterPack} 148 onPress={() => control.close()}> 149 <View 150 style={[ 151 a.w_full, 152 a.mt_sm, 153 a.p_lg, 154 a.border, 155 a.rounded_sm, 156 t.atoms.border_contrast_low, 157 ]}> 158 <StarterPackCard.Card 159 starterPack={profile.joinedViaStarterPack} 160 /> 161 </View> 162 </StarterPackCard.Link> 163 ) : null} 164 165 {IS_NATIVE && ( 166 <Button 167 label={_(msg`Close`)} 168 color="secondary" 169 size="small" 170 style={[a.mt_sm]} 171 onPress={() => control.close()}> 172 <ButtonText> 173 <Trans>Close</Trans> 174 </ButtonText> 175 </Button> 176 )} 177 </View> 178 179 <Dialog.Close /> 180 </Dialog.ScrollableInner> 181 ) 182}