forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
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}