tangled
alpha
login
or
join now
quilling.dev
/
social-app
7
fork
atom
An ATproto social media client -- with an independent Appview.
7
fork
atom
overview
issues
pulls
pipelines
Disable avi
Eric Bailey
2 years ago
f7db14f3
eaf00816
+91
-44
4 changed files
expand all
collapse all
unified
split
assets
icons
download_stroke2_corner0_rounded.svg
src
components
Dialog
index.tsx
dialogs
nudges
TenMillion.tsx
icons
Download.tsx
+1
assets/icons/download_stroke2_corner0_rounded.svg
···
0
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M12 3a1 1 0 0 1 1 1v8.086l1.793-1.793a1 1 0 1 1 1.414 1.414l-3.5 3.5a1 1 0 0 1-1.414 0l-3.5-3.5a1 1 0 1 1 1.414-1.414L11 12.086V4a1 1 0 0 1 1-1ZM4 14a1 1 0 0 1 1 1v4h14v-4a1 1 0 1 1 2 0v5a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-5a1 1 0 0 1 1-1Z" clip-rule="evenodd"/></svg>
+1
-1
src/components/Dialog/index.tsx
···
256
borderTopLeftRadius: 40,
257
borderTopRightRadius: 40,
258
},
259
-
flatten(style),
260
]}
261
contentContainerStyle={a.pb_4xl}
262
ref={ref}>
···
256
borderTopLeftRadius: 40,
257
borderTopRightRadius: 40,
258
},
259
+
style,
260
]}
261
contentContainerStyle={a.pb_4xl}
262
ref={ref}>
+84
-43
src/components/dialogs/nudges/TenMillion.tsx
···
7
import {useLingui} from '@lingui/react'
8
9
import {getCanvas} from '#/lib/canvas'
0
10
import {sanitizeDisplayName} from '#/lib/strings/display-names'
11
import {sanitizeHandle} from '#/lib/strings/handles'
12
-
import {isNative} from '#/platform/detection'
13
import {useModerationOpts} from '#/state/preferences/moderation-opts'
14
import {useProfileQuery} from '#/state/queries/profile'
15
import {useSession} from '#/state/session'
16
import {useComposerControls} from 'state/shell'
17
import {formatCount} from '#/view/com/util/numeric/format'
18
-
import {UserAvatar} from '#/view/com/util/UserAvatar'
19
import {Logomark} from '#/view/icons/Logomark'
20
import {
21
atoms as a,
···
30
import {Divider} from '#/components/Divider'
31
import {GradientFill} from '#/components/GradientFill'
32
import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
0
33
import {Image_Stroke2_Corner0_Rounded as ImageIcon} from '#/components/icons/Image'
34
import {Loader} from '#/components/Loader'
35
import {Text} from '#/components/Typography'
···
88
const isLoadingData = isProfileLoading || !moderation || !profile
89
const isLoadingImage = !uri
90
91
-
const userNumber = 56738 // TODO
92
93
-
const captureInProgress = React.useRef(false)
94
-
const imageRef = React.useRef<ViewShot>(null)
95
-
96
-
const share = () => {
97
if (uri) {
98
controls.tenMillion.close(() => {
99
setTimeout(() => {
···
112
}
113
}
114
115
-
const onCanvasReady = async () => {
116
-
if (
117
-
imageRef.current &&
118
-
imageRef.current.capture &&
119
-
!captureInProgress.current
120
-
) {
121
-
captureInProgress.current = true
122
-
const uri = await imageRef.current.capture()
123
-
setUri(uri)
124
}
125
}
126
···
137
}
138
}
139
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
140
const canvas = isLoadingData ? null : (
141
<View
142
style={[
···
160
options={{width: WIDTH, height: HEIGHT}}
161
style={[a.absolute, a.inset_0]}>
162
<View
0
0
163
style={[
164
a.absolute,
165
a.inset_0,
···
170
bottom: -1,
171
left: -1,
172
right: -1,
173
-
paddingVertical: 32,
174
paddingHorizontal: 48,
175
},
176
]}>
···
208
<View
209
style={[
210
{
211
-
paddingBottom: 48,
212
},
213
]}>
214
<Text
···
216
a.text_md,
217
a.font_bold,
218
a.text_center,
219
-
a.pb_xs,
220
lightTheme.atoms.text_contrast_medium,
221
]}>
222
<Trans>
···
224
</Trans>{' '}
225
🎉
226
</Text>
227
-
<Text
228
-
style={[
229
-
a.relative,
230
-
a.text_center,
231
-
{
232
-
fontStyle: 'italic',
233
-
fontSize: getFontSize(userNumber),
234
-
fontWeight: '900',
235
-
letterSpacing: -2,
236
-
},
237
-
]}>
238
<Text
239
style={[
240
a.absolute,
···
242
color: t.palette.primary_500,
243
fontSize: 32,
244
left: -18,
245
-
top: 8,
0
246
},
247
]}>
248
#
249
</Text>
250
-
{i18n.number(userNumber)}
251
-
</Text>
0
0
0
0
0
0
0
0
0
0
0
0
0
252
</View>
253
{/* End centered content */}
254
···
264
},
265
]}>
266
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
0
267
<UserAvatar
268
size={36}
269
avatar={profile.avatar}
270
moderation={moderation.ui('avatar')}
271
onLoad={onCanvasReady}
272
/>
0
273
<View style={[a.gap_2xs, a.flex_1]}>
274
-
<Text style={[a.text_sm, a.font_bold]}>
275
{sanitizeDisplayName(
276
profile.displayName ||
277
sanitizeHandle(profile.handle),
···
283
style={[
284
a.text_sm,
285
a.font_semibold,
0
0
286
lightTheme.atoms.text_contrast_medium,
287
]}>
288
{sanitizeHandle(profile.handle, '@')}
···
293
style={[
294
a.text_sm,
295
a.font_semibold,
0
0
296
lightTheme.atoms.text_contrast_low,
297
]}>
298
-
{i18n.date(profile.createdAt, {
299
-
dateStyle: 'long',
300
-
})}
0
0
0
301
</Text>
302
)}
303
</View>
···
315
316
return (
317
<Dialog.Outer control={controls.tenMillion}>
318
-
<Dialog.Handle />
319
-
320
<Dialog.ScrollableInner
321
label={_(msg`Ten Million`)}
322
style={[
323
{
324
padding: 0,
0
325
},
326
]}>
327
<View
···
355
<Text
356
style={[
357
a.text_5xl,
0
358
a.pb_lg,
359
{
360
fontWeight: '900',
···
387
</Text>
388
389
<Button
390
-
label={_(msg`Share image externally`)}
0
0
0
0
0
391
size="large"
392
variant="solid"
393
color="secondary"
394
shape="square"
395
-
onPress={download}>
396
-
<ButtonIcon icon={Share} />
397
</Button>
398
<Button
0
399
label={_(msg`Share image in post`)}
400
size="large"
401
variant="solid"
402
color="primary"
403
-
onPress={share}>
404
<ButtonText>{_(msg`Share post`)}</ButtonText>
405
<ButtonIcon position="right" icon={ImageIcon} />
406
</Button>
···
7
import {useLingui} from '@lingui/react'
8
9
import {getCanvas} from '#/lib/canvas'
10
+
import {shareUrl} from '#/lib/sharing'
11
import {sanitizeDisplayName} from '#/lib/strings/display-names'
12
import {sanitizeHandle} from '#/lib/strings/handles'
13
+
import {isAndroid, isNative, isWeb} from '#/platform/detection'
14
import {useModerationOpts} from '#/state/preferences/moderation-opts'
15
import {useProfileQuery} from '#/state/queries/profile'
16
import {useSession} from '#/state/session'
17
import {useComposerControls} from 'state/shell'
18
import {formatCount} from '#/view/com/util/numeric/format'
19
+
// import {UserAvatar} from '#/view/com/util/UserAvatar'
20
import {Logomark} from '#/view/icons/Logomark'
21
import {
22
atoms as a,
···
31
import {Divider} from '#/components/Divider'
32
import {GradientFill} from '#/components/GradientFill'
33
import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
34
+
import {Download_Stroke2_Corner0_Rounded as Download} from '#/components/icons/Download'
35
import {Image_Stroke2_Corner0_Rounded as ImageIcon} from '#/components/icons/Image'
36
import {Loader} from '#/components/Loader'
37
import {Text} from '#/components/Typography'
···
90
const isLoadingData = isProfileLoading || !moderation || !profile
91
const isLoadingImage = !uri
92
93
+
const userNumber = 10_000_000 // TODO
94
95
+
const sharePost = () => {
0
0
0
96
if (uri) {
97
controls.tenMillion.close(() => {
98
setTimeout(() => {
···
111
}
112
}
113
114
+
const onNativeShare = () => {
115
+
if (uri) {
116
+
controls.tenMillion.close(() => {
117
+
shareUrl(uri)
118
+
})
0
0
0
0
119
}
120
}
121
···
132
}
133
}
134
135
+
const imageRef = React.useRef<ViewShot>(null)
136
+
// const captureInProgress = React.useRef(false)
137
+
// const [cavasRelayout, setCanvasRelayout] = React.useState('key')
138
+
// const onCanvasReady = async () => {
139
+
// if (
140
+
// imageRef.current &&
141
+
// imageRef.current.capture &&
142
+
// !captureInProgress.current
143
+
// ) {
144
+
// captureInProgress.current = true
145
+
// setCanvasRelayout('updated')
146
+
// }
147
+
// }
148
+
const onCanvasLayout = async () => {
149
+
if (
150
+
imageRef.current &&
151
+
imageRef.current.capture // &&
152
+
// cavasRelayout === 'updated'
153
+
) {
154
+
console.log('LAYOUT')
155
+
const uri = await imageRef.current.capture()
156
+
setUri(uri)
157
+
}
158
+
}
159
+
160
const canvas = isLoadingData ? null : (
161
<View
162
style={[
···
180
options={{width: WIDTH, height: HEIGHT}}
181
style={[a.absolute, a.inset_0]}>
182
<View
183
+
// key={cavasRelayout}
184
+
onLayout={onCanvasLayout}
185
style={[
186
a.absolute,
187
a.inset_0,
···
192
bottom: -1,
193
left: -1,
194
right: -1,
195
+
paddingVertical: 48,
196
paddingHorizontal: 48,
197
},
198
]}>
···
230
<View
231
style={[
232
{
233
+
paddingBottom: 32,
234
},
235
]}>
236
<Text
···
238
a.text_md,
239
a.font_bold,
240
a.text_center,
241
+
a.pb_md,
242
lightTheme.atoms.text_contrast_medium,
243
]}>
244
<Trans>
···
246
</Trans>{' '}
247
🎉
248
</Text>
249
+
<View>
0
0
0
0
0
0
0
0
0
0
250
<Text
251
style={[
252
a.absolute,
···
254
color: t.palette.primary_500,
255
fontSize: 32,
256
left: -18,
257
+
top: isAndroid ? -8 : isWeb ? 5 : -5,
258
+
fontWeight: '900',
259
},
260
]}>
261
#
262
</Text>
263
+
<Text
264
+
style={[
265
+
a.relative,
266
+
a.text_center,
267
+
{
268
+
fontStyle: 'italic',
269
+
fontSize: getFontSize(userNumber),
270
+
lineHeight: getFontSize(userNumber),
271
+
fontWeight: '900',
272
+
letterSpacing: -2,
273
+
},
274
+
]}>
275
+
{i18n.number(userNumber)}
276
+
</Text>
277
+
</View>
278
</View>
279
{/* End centered content */}
280
···
290
},
291
]}>
292
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
293
+
{/*
294
<UserAvatar
295
size={36}
296
avatar={profile.avatar}
297
moderation={moderation.ui('avatar')}
298
onLoad={onCanvasReady}
299
/>
300
+
*/}
301
<View style={[a.gap_2xs, a.flex_1]}>
302
+
<Text style={[a.text_sm, a.font_bold, a.leading_tight]}>
303
{sanitizeDisplayName(
304
profile.displayName ||
305
sanitizeHandle(profile.handle),
···
311
style={[
312
a.text_sm,
313
a.font_semibold,
314
+
,
315
+
a.leading_tight,
316
lightTheme.atoms.text_contrast_medium,
317
]}>
318
{sanitizeHandle(profile.handle, '@')}
···
323
style={[
324
a.text_sm,
325
a.font_semibold,
326
+
,
327
+
a.leading_tight,
328
lightTheme.atoms.text_contrast_low,
329
]}>
330
+
<Trans>
331
+
Joined{' '}
332
+
{i18n.date(profile.createdAt, {
333
+
dateStyle: 'long',
334
+
})}
335
+
</Trans>
336
</Text>
337
)}
338
</View>
···
350
351
return (
352
<Dialog.Outer control={controls.tenMillion}>
0
0
353
<Dialog.ScrollableInner
354
label={_(msg`Ten Million`)}
355
style={[
356
{
357
padding: 0,
358
+
paddingTop: 0,
359
},
360
]}>
361
<View
···
389
<Text
390
style={[
391
a.text_5xl,
392
+
a.leading_tight,
393
a.pb_lg,
394
{
395
fontWeight: '900',
···
422
</Text>
423
424
<Button
425
+
disabled={isLoadingImage}
426
+
label={
427
+
isNative
428
+
? _(msg`Share image externally`)
429
+
: _(msg`Download image`)
430
+
}
431
size="large"
432
variant="solid"
433
color="secondary"
434
shape="square"
435
+
onPress={isNative ? onNativeShare : download}>
436
+
<ButtonIcon icon={isNative ? Share : Download} />
437
</Button>
438
<Button
439
+
disabled={isLoadingImage}
440
label={_(msg`Share image in post`)}
441
size="large"
442
variant="solid"
443
color="primary"
444
+
onPress={sharePost}>
445
<ButtonText>{_(msg`Share post`)}</ButtonText>
446
<ButtonIcon position="right" icon={ImageIcon} />
447
</Button>
+5
src/components/icons/Download.tsx
···
0
0
0
0
0
···
1
+
import {createSinglePathSVG} from './TEMPLATE'
2
+
3
+
export const Download_Stroke2_Corner0_Rounded = createSinglePathSVG({
4
+
path: 'M12 3a1 1 0 0 1 1 1v8.086l1.793-1.793a1 1 0 1 1 1.414 1.414l-3.5 3.5a1 1 0 0 1-1.414 0l-3.5-3.5a1 1 0 1 1 1.414-1.414L11 12.086V4a1 1 0 0 1 1-1ZM4 14a1 1 0 0 1 1 1v4h14v-4a1 1 0 1 1 2 0v5a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-5a1 1 0 0 1 1-1Z',
5
+
})