Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
143
fork

Configure Feed

Select the types of activity you want to include in your feed.

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