Bluesky app fork with some witchin' additions 馃挮
witchsky.app
bluesky
fork
client
1import {useCallback} from 'react'
2import {View} from 'react-native'
3import {Image} from 'expo-image'
4import {LinearGradient} from 'expo-linear-gradient'
5import {msg} from '@lingui/core/macro'
6import {useLingui} from '@lingui/react'
7import {Trans} from '@lingui/react/macro'
8
9import {atoms as a, useTheme, web} from '#/alf'
10import {Button, ButtonText} from '#/components/Button'
11import * as Dialog from '#/components/Dialog'
12import {useNuxDialogContext} from '#/components/dialogs/nuxs'
13import {Sparkle_Stroke2_Corner0_Rounded as SparkleIcon} from '#/components/icons/Sparkle'
14import {Text} from '#/components/Typography'
15import {IS_E2E, IS_NATIVE, IS_WEB} from '#/env'
16import {createIsEnabledCheck, isExistingUserAsOf} from './utils'
17
18export const enabled = createIsEnabledCheck(props => {
19 return (
20 !IS_E2E &&
21 IS_NATIVE &&
22 isExistingUserAsOf(
23 '2026-02-05T00:00:00.000Z',
24 props.currentProfile.createdAt,
25 )
26 )
27})
28
29export function DraftsAnnouncement() {
30 const t = useTheme()
31 const {_} = useLingui()
32 const nuxDialogs = useNuxDialogContext()
33 const control = Dialog.useDialogControl()
34
35 Dialog.useAutoOpen(control)
36
37 const onClose = useCallback(() => {
38 nuxDialogs.dismissActiveNux()
39 }, [nuxDialogs])
40
41 return (
42 <Dialog.Outer
43 control={control}
44 onClose={onClose}
45 nativeOptions={{preventExpansion: true}}>
46 <Dialog.Handle fill={t.palette.primary_400} />
47
48 <Dialog.ScrollableInner
49 label={_(msg`Introducing drafts`)}
50 style={[web({maxWidth: 440})]}
51 contentContainerStyle={[
52 {
53 paddingTop: 0,
54 paddingLeft: 0,
55 paddingRight: 0,
56 },
57 ]}>
58 <View
59 style={[
60 a.align_center,
61 a.overflow_hidden,
62 {
63 paddingTop: IS_WEB ? 24 : 40,
64 borderTopLeftRadius: a.rounded_md.borderRadius,
65 borderTopRightRadius: a.rounded_md.borderRadius,
66 },
67 ]}>
68 <LinearGradient
69 colors={[t.palette.primary_100, t.palette.primary_200]}
70 locations={[0, 1]}
71 start={{x: 0, y: 0}}
72 end={{x: 0, y: 1}}
73 style={[a.absolute, a.inset_0]}
74 />
75 <View
76 style={[a.flex_row, a.align_center, a.gap_xs, {marginBottom: -12}]}>
77 <SparkleIcon fill={t.palette.primary_800} size="sm" />
78 <Text
79 style={[
80 a.font_semi_bold,
81 {
82 color: t.palette.primary_800,
83 },
84 ]}>
85 <Trans>New Feature</Trans>
86 </Text>
87 </View>
88 <Image
89 accessibilityIgnoresInvertColors
90 source={require('../../../../assets/images/drafts_announcement_nux.webp')}
91 style={[
92 a.w_full,
93 {
94 aspectRatio: 393 / 226,
95 },
96 ]}
97 alt={_(
98 msg({
99 message: `A screenshot of the post composer with a new button next to the post button that says "Drafts", with a rainbow firework effect. Below, the text in the composer reads "Hey, did you hear the news? Bluesky has drafts now!!!".`,
100 comment:
101 'Contains a post that originally appeared in English. Consider translating the post text if it makes sense in your language, and noting that the post was translated from English.',
102 }),
103 )}
104 />
105 </View>
106 <View style={[a.align_center, a.px_xl, a.pt_xl, a.gap_2xl, a.pb_sm]}>
107 <View style={[a.gap_sm, a.align_center]}>
108 <Text
109 style={[
110 a.text_3xl,
111 a.leading_tight,
112 a.font_bold,
113 a.text_center,
114 {
115 fontSize: IS_WEB ? 28 : 32,
116 maxWidth: 300,
117 },
118 ]}>
119 <Trans>Drafts</Trans>
120 </Text>
121 <Text
122 style={[
123 a.text_md,
124 a.leading_snug,
125 a.text_center,
126 {
127 maxWidth: 340,
128 },
129 ]}>
130 <Trans>
131 Not ready to hit post? Keep your best ideas in Drafts until the
132 timing is just right.
133 </Trans>
134 </Text>
135 </View>
136
137 {!IS_WEB && (
138 <Button
139 label={_(msg`Close`)}
140 size="large"
141 color="primary"
142 onPress={() => control.close()}
143 style={[a.w_full]}>
144 <ButtonText>
145 <Trans>Finally!</Trans>
146 </ButtonText>
147 </Button>
148 )}
149 </View>
150
151 <Dialog.Close />
152 </Dialog.ScrollableInner>
153 </Dialog.Outer>
154 )
155}