···4import {requestMediaLibraryPermissionsAsync} from 'expo-image-picker'
5import {createAssetAsync} from 'expo-media-library'
6import * as Sharing from 'expo-sharing'
7-import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
8import {msg, Trans} from '@lingui/macro'
9import {useLingui} from '@lingui/react'
10···15import {atoms as a} from '#/alf'
16import {Button, ButtonText} from '#/components/Button'
17import * as Dialog from '#/components/Dialog'
18-import {DialogControlProps} from '#/components/Dialog'
19import {Loader} from '#/components/Loader'
20import {QrCode} from '#/components/StarterPack/QrCode'
21import * as bsky from '#/types/bsky'
···55 if (isNative) {
56 const res = await requestMediaLibraryPermissionsAsync()
5758- if (!res) {
59 Toast.show(
60 _(
61 msg`You must grant access to your photo library to save a QR code`,
···155156 return (
157 <Dialog.Outer control={control}>
0158 <Dialog.ScrollableInner
159 label={_(msg`Create a QR code for a starter pack`)}>
160 <View style={[a.flex_1, a.align_center, a.gap_5xl]}>
···197 )}
198 </React.Suspense>
199 </View>
0200 </Dialog.ScrollableInner>
201 </Dialog.Outer>
202 )
···4import {requestMediaLibraryPermissionsAsync} from 'expo-image-picker'
5import {createAssetAsync} from 'expo-media-library'
6import * as Sharing from 'expo-sharing'
7+import {type AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
8import {msg, Trans} from '@lingui/macro'
9import {useLingui} from '@lingui/react'
10···15import {atoms as a} from '#/alf'
16import {Button, ButtonText} from '#/components/Button'
17import * as Dialog from '#/components/Dialog'
18+import {type DialogControlProps} from '#/components/Dialog'
19import {Loader} from '#/components/Loader'
20import {QrCode} from '#/components/StarterPack/QrCode'
21import * as bsky from '#/types/bsky'
···55 if (isNative) {
56 const res = await requestMediaLibraryPermissionsAsync()
5758+ if (!res.granted) {
59 Toast.show(
60 _(
61 msg`You must grant access to your photo library to save a QR code`,
···155156 return (
157 <Dialog.Outer control={control}>
158+ <Dialog.Handle />
159 <Dialog.ScrollableInner
160 label={_(msg`Create a QR code for a starter pack`)}>
161 <View style={[a.flex_1, a.align_center, a.gap_5xl]}>
···198 )}
199 </React.Suspense>
200 </View>
201+ <Dialog.Close />
202 </Dialog.ScrollableInner>
203 </Dialog.Outer>
204 )
+5-23
src/components/StarterPack/ShareDialog.tsx
···1import {View} from 'react-native'
2import {Image} from 'expo-image'
3-import {requestMediaLibraryPermissionsAsync} from 'expo-image-picker'
4import {type AppBskyGraphDefs} from '@atproto/api'
5import {msg, Trans} from '@lingui/macro'
6import {useLingui} from '@lingui/react'
78import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
9-import {saveImageToMediaLibrary} from '#/lib/media/manip'
10import {shareUrl} from '#/lib/sharing'
11import {logEvent} from '#/lib/statsig/statsig'
12import {getStarterPackOgCard} from '#/lib/strings/starter-pack'
13-import {logger} from '#/logger'
14import {isNative, isWeb} from '#/platform/detection'
15-import * as Toast from '#/view/com/util/Toast'
16import {atoms as a, useTheme} from '#/alf'
17import {Button, ButtonText} from '#/components/Button'
18import {type DialogControlProps} from '#/components/Dialog'
···60 control.close()
61 }
6263- const onSave = async () => {
64- const res = await requestMediaLibraryPermissionsAsync()
65-66- if (!res) {
67- Toast.show(
68- _(msg`You must grant access to your photo library to save the image.`),
69- 'xmark',
70- )
71- return
72- }
7374- try {
75- await saveImageToMediaLibrary({uri: imageUrl})
76- Toast.show(_(msg`Image saved`))
77- control.close()
78- } catch (e: unknown) {
79- Toast.show(_(msg`An error occurred while saving the QR code!`), 'xmark')
80- logger.error('Failed to save QR code', {error: e})
81- return
82- }
83 }
8485 return (
···161 </View>
162 </View>
163 )}
0164 </Dialog.ScrollableInner>
165 </>
166 )
···1import {View} from 'react-native'
2import {Image} from 'expo-image'
03import {type AppBskyGraphDefs} from '@atproto/api'
4import {msg, Trans} from '@lingui/macro'
5import {useLingui} from '@lingui/react'
67import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
8+import {useSaveImageToMediaLibrary} from '#/lib/media/save-image'
9import {shareUrl} from '#/lib/sharing'
10import {logEvent} from '#/lib/statsig/statsig'
11import {getStarterPackOgCard} from '#/lib/strings/starter-pack'
012import {isNative, isWeb} from '#/platform/detection'
013import {atoms as a, useTheme} from '#/alf'
14import {Button, ButtonText} from '#/components/Button'
15import {type DialogControlProps} from '#/components/Dialog'
···57 control.close()
58 }
5960+ const saveImageToAlbum = useSaveImageToMediaLibrary()
0000000006162+ const onSave = async () => {
63+ await saveImageToAlbum(imageUrl)
000000064 }
6566 return (
···142 </View>
143 </View>
144 )}
145+ <Dialog.Close />
146 </Dialog.ScrollableInner>
147 </>
148 )