about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/StarterPack/QrCodeDialog.tsx10
-rw-r--r--src/components/StarterPack/ShareDialog.tsx23
-rw-r--r--src/components/StarterPack/Wizard/WizardEditListDialog.tsx2
-rw-r--r--src/components/StarterPack/Wizard/WizardListCard.tsx29
-rw-r--r--src/screens/StarterPack/Wizard/StepFeeds.tsx13
-rw-r--r--src/screens/StarterPack/Wizard/StepProfiles.tsx1
-rw-r--r--src/screens/StarterPack/Wizard/index.tsx38
7 files changed, 56 insertions, 60 deletions
diff --git a/src/components/StarterPack/QrCodeDialog.tsx b/src/components/StarterPack/QrCodeDialog.tsx
index 580c6cc7c..39eb3076d 100644
--- a/src/components/StarterPack/QrCodeDialog.tsx
+++ b/src/components/StarterPack/QrCodeDialog.tsx
@@ -1,16 +1,14 @@
 import React from 'react'
 import {View} from 'react-native'
 import ViewShot from 'react-native-view-shot'
-import * as FS from 'expo-file-system'
 import {requestMediaLibraryPermissionsAsync} from 'expo-image-picker'
+import {createAssetAsync} from 'expo-media-library'
 import * as Sharing from 'expo-sharing'
 import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import {nanoid} from 'nanoid/non-secure'
 
 import {logger} from '#/logger'
-import {saveImageToMediaLibrary} from 'lib/media/manip'
 import {logEvent} from 'lib/statsig/statsig'
 import {isNative, isWeb} from 'platform/detection'
 import * as Toast from '#/view/com/util/Toast'
@@ -65,13 +63,9 @@ export function QrCodeDialog({
           return
         }
 
-        const filename = `${FS.documentDirectory}/${nanoid(12)}.png`
-
         // Incase of a FS failure, don't crash the app
         try {
-          await FS.copyAsync({from: uri, to: filename})
-          await saveImageToMediaLibrary({uri: filename})
-          await FS.deleteAsync(filename)
+          await createAssetAsync(`file://${uri}`)
         } catch (e: unknown) {
           Toast.show(_(msg`An error occurred while saving the QR code!`))
           logger.error('Failed to save QR code', {error: e})
diff --git a/src/components/StarterPack/ShareDialog.tsx b/src/components/StarterPack/ShareDialog.tsx
index 23fa10fb3..61e238081 100644
--- a/src/components/StarterPack/ShareDialog.tsx
+++ b/src/components/StarterPack/ShareDialog.tsx
@@ -1,12 +1,10 @@
 import React from 'react'
 import {View} from 'react-native'
-import * as FS from 'expo-file-system'
 import {Image} from 'expo-image'
 import {requestMediaLibraryPermissionsAsync} from 'expo-image-picker'
 import {AppBskyGraphDefs} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import {nanoid} from 'nanoid/non-secure'
 
 import {logger} from '#/logger'
 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
@@ -72,19 +70,8 @@ function ShareDialogInner({
       return
     }
 
-    const cachePath = await Image.getCachePathAsync(imageUrl)
-    const filename = `${FS.documentDirectory}/${nanoid(12)}.png`
-
-    if (!cachePath) {
-      Toast.show(_(msg`An error occurred while saving the image.`))
-      return
-    }
-
     try {
-      await FS.copyAsync({from: cachePath, to: filename})
-      await saveImageToMediaLibrary({uri: filename})
-      await FS.deleteAsync(filename)
-
+      await saveImageToMediaLibrary({uri: imageUrl})
       Toast.show(_(msg`Image saved to your camera roll!`))
       control.close()
     } catch (e: unknown) {
@@ -133,18 +120,18 @@ function ShareDialogInner({
                 isWeb && [a.gap_sm, a.flex_row_reverse, {marginLeft: 'auto'}],
               ]}>
               <Button
-                label="Share link"
+                label={isWeb ? _(msg`Copy link`) : _(msg`Share link`)}
                 variant="solid"
                 color="secondary"
                 size="small"
                 style={[isWeb && a.self_center]}
                 onPress={onShareLink}>
                 <ButtonText>
-                  {isWeb ? <Trans>Copy Link</Trans> : <Trans>Share Link</Trans>}
+                  {isWeb ? <Trans>Copy Link</Trans> : <Trans>Share link</Trans>}
                 </ButtonText>
               </Button>
               <Button
-                label="Create QR code"
+                label={_(msg`Share QR code`)}
                 variant="solid"
                 color="secondary"
                 size="small"
@@ -155,7 +142,7 @@ function ShareDialogInner({
                   })
                 }}>
                 <ButtonText>
-                  <Trans>Create QR code</Trans>
+                  <Trans>Share QR code</Trans>
                 </ButtonText>
               </Button>
               {isNative && (
diff --git a/src/components/StarterPack/Wizard/WizardEditListDialog.tsx b/src/components/StarterPack/Wizard/WizardEditListDialog.tsx
index bf250ac35..cf755e1bc 100644
--- a/src/components/StarterPack/Wizard/WizardEditListDialog.tsx
+++ b/src/components/StarterPack/Wizard/WizardEditListDialog.tsx
@@ -58,6 +58,7 @@ export function WizardEditListDialog({
     state.currentStep === 'Profiles' ? (
       <WizardProfileCard
         profile={item}
+        btnType="remove"
         state={state}
         dispatch={dispatch}
         moderationOpts={moderationOpts}
@@ -65,6 +66,7 @@ export function WizardEditListDialog({
     ) : (
       <WizardFeedCard
         generator={item}
+        btnType="remove"
         state={state}
         dispatch={dispatch}
         moderationOpts={moderationOpts}
diff --git a/src/components/StarterPack/Wizard/WizardListCard.tsx b/src/components/StarterPack/Wizard/WizardListCard.tsx
index f1332011d..aa1b2cf9b 100644
--- a/src/components/StarterPack/Wizard/WizardListCard.tsx
+++ b/src/components/StarterPack/Wizard/WizardListCard.tsx
@@ -9,7 +9,7 @@ import {
   ModerationUI,
 } from '@atproto/api'
 import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
-import {msg} from '@lingui/macro'
+import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
 import {DISCOVER_FEED_URI} from 'lib/constants'
@@ -19,12 +19,14 @@ import {useSession} from 'state/session'
 import {UserAvatar} from 'view/com/util/UserAvatar'
 import {WizardAction, WizardState} from '#/screens/StarterPack/Wizard/State'
 import {atoms as a, useTheme} from '#/alf'
+import {Button, ButtonText} from '#/components/Button'
 import * as Toggle from '#/components/forms/Toggle'
 import {Checkbox} from '#/components/forms/Toggle'
 import {Text} from '#/components/Typography'
 
 function WizardListCard({
   type,
+  btnType,
   displayName,
   subtitle,
   onPress,
@@ -34,6 +36,7 @@ function WizardListCard({
   moderationUi,
 }: {
   type: 'user' | 'algo'
+  btnType: 'checkbox' | 'remove'
   profile?: AppBskyActorDefs.ProfileViewBasic
   feed?: AppBskyFeedDefs.GeneratorView
   displayName: string
@@ -56,7 +59,7 @@ function WizardListCard({
           : _(msg`Add ${displayName} to starter pack`)
       }
       value={included}
-      disabled={disabled}
+      disabled={btnType === 'remove' || disabled}
       onChange={onPress}
       style={[
         a.flex_row,
@@ -85,17 +88,33 @@ function WizardListCard({
           {subtitle}
         </Text>
       </View>
-      <Checkbox />
+      {btnType === 'checkbox' ? (
+        <Checkbox />
+      ) : !disabled ? (
+        <Button
+          label={_(msg`Remove`)}
+          variant="solid"
+          color="secondary"
+          size="xsmall"
+          style={[a.self_center, {marginLeft: 'auto'}]}
+          onPress={onPress}>
+          <ButtonText>
+            <Trans>Remove</Trans>
+          </ButtonText>
+        </Button>
+      ) : null}
     </Toggle.Item>
   )
 }
 
 export function WizardProfileCard({
+  btnType,
   state,
   dispatch,
   profile,
   moderationOpts,
 }: {
+  btnType: 'checkbox' | 'remove'
   state: WizardState
   dispatch: (action: WizardAction) => void
   profile: AppBskyActorDefs.ProfileViewBasic
@@ -127,6 +146,7 @@ export function WizardProfileCard({
   return (
     <WizardListCard
       type="user"
+      btnType={btnType}
       displayName={displayName}
       subtitle={`@${sanitizeHandle(profile.handle)}`}
       onPress={onPress}
@@ -139,11 +159,13 @@ export function WizardProfileCard({
 }
 
 export function WizardFeedCard({
+  btnType,
   generator,
   state,
   dispatch,
   moderationOpts,
 }: {
+  btnType: 'checkbox' | 'remove'
   generator: GeneratorView
   state: WizardState
   dispatch: (action: WizardAction) => void
@@ -170,6 +192,7 @@ export function WizardFeedCard({
   return (
     <WizardListCard
       type="algo"
+      btnType={btnType}
       displayName={sanitizeDisplayName(generator.displayName)}
       subtitle={`Feed by @${sanitizeHandle(generator.creator.handle)}`}
       onPress={onPress}
diff --git a/src/screens/StarterPack/Wizard/StepFeeds.tsx b/src/screens/StarterPack/Wizard/StepFeeds.tsx
index 6752a95db..fbd8e7389 100644
--- a/src/screens/StarterPack/Wizard/StepFeeds.tsx
+++ b/src/screens/StarterPack/Wizard/StepFeeds.tsx
@@ -40,12 +40,14 @@ export function StepFeeds({moderationOpts}: {moderationOpts: ModerationOpts}) {
   const {data: popularFeedsPages, fetchNextPage} = useGetPopularFeedsQuery({
     limit: 30,
   })
-  const popularFeeds =
-    popularFeedsPages?.pages
-      .flatMap(page => page.feeds)
-      .filter(f => !savedFeeds?.some(sf => sf?.uri === f.uri)) ?? []
+  const popularFeeds = popularFeedsPages?.pages.flatMap(p => p.feeds) ?? []
 
-  const suggestedFeeds = savedFeeds?.concat(popularFeeds)
+  const suggestedFeeds =
+    savedFeeds.length === 0
+      ? popularFeeds
+      : savedFeeds.concat(
+          popularFeeds.filter(f => !savedFeeds.some(sf => sf.uri === f.uri)),
+        )
 
   const {data: searchedFeeds, isLoading: isLoadingSearch} =
     useSearchPopularFeedsQuery({q: throttledQuery})
@@ -56,6 +58,7 @@ export function StepFeeds({moderationOpts}: {moderationOpts: ModerationOpts}) {
     return (
       <WizardFeedCard
         generator={item}
+        btnType="checkbox"
         state={state}
         dispatch={dispatch}
         moderationOpts={moderationOpts}
diff --git a/src/screens/StarterPack/Wizard/StepProfiles.tsx b/src/screens/StarterPack/Wizard/StepProfiles.tsx
index 8fe7f52fe..f21b01d40 100644
--- a/src/screens/StarterPack/Wizard/StepProfiles.tsx
+++ b/src/screens/StarterPack/Wizard/StepProfiles.tsx
@@ -45,6 +45,7 @@ export function StepProfiles({
     return (
       <WizardProfileCard
         profile={item}
+        btnType="checkbox"
         state={state}
         dispatch={dispatch}
         moderationOpts={moderationOpts}
diff --git a/src/screens/StarterPack/Wizard/index.tsx b/src/screens/StarterPack/Wizard/index.tsx
index 7cee86f84..fd16fd20a 100644
--- a/src/screens/StarterPack/Wizard/index.tsx
+++ b/src/screens/StarterPack/Wizard/index.tsx
@@ -21,6 +21,7 @@ import {NativeStackScreenProps} from '@react-navigation/native-stack'
 
 import {logger} from '#/logger'
 import {HITSLOP_10} from 'lib/constants'
+import {createSanitizedDisplayName} from 'lib/moderation/create-sanitized-display-name'
 import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types'
 import {logEvent} from 'lib/statsig/statsig'
 import {sanitizeDisplayName} from 'lib/strings/display-names'
@@ -170,15 +171,7 @@ function WizardInner({
   )
 
   const getDefaultName = () => {
-    let displayName
-    if (
-      currentProfile?.displayName != null &&
-      currentProfile?.displayName !== ''
-    ) {
-      displayName = sanitizeDisplayName(currentProfile.displayName)
-    } else {
-      displayName = sanitizeHandle(currentProfile!.handle)
-    }
+    const displayName = createSanitizedDisplayName(currentProfile!, true)
     return _(msg`${displayName}'s Starter Pack`).slice(0, 50)
   }
 
@@ -191,16 +184,12 @@ function WizardInner({
       nextBtn: _(msg`Next`),
     },
     Profiles: {
-      header: _(msg`People`),
+      header: _(msg`Choose People`),
       nextBtn: _(msg`Next`),
-      subtitle: _(
-        msg`Add people to your starter pack that you think others will enjoy following`,
-      ),
     },
     Feeds: {
-      header: _(msg`Feeds`),
+      header: _(msg`Choose Feeds`),
       nextBtn: state.feeds.length === 0 ? _(msg`Skip`) : _(msg`Finish`),
-      subtitle: _(msg`Some subtitle`),
     },
   }
   const currUiStrings = wizardUiStrings[state.currentStep]
@@ -254,8 +243,8 @@ function WizardInner({
     dispatch({type: 'SetProcessing', processing: true})
     if (currentStarterPack && currentListItems) {
       editStarterPack({
-        name: state.name ?? getDefaultName(),
-        description: state.description,
+        name: state.name?.trim() || getDefaultName(),
+        description: state.description?.trim(),
         descriptionFacets: [],
         profiles: state.profiles,
         feeds: state.feeds,
@@ -264,8 +253,8 @@ function WizardInner({
       })
     } else {
       createStarterPack({
-        name: state.name ?? getDefaultName(),
-        description: state.description,
+        name: state.name?.trim() || getDefaultName(),
+        description: state.description?.trim(),
         descriptionFacets: [],
         profiles: state.profiles,
         feeds: state.feeds,
@@ -483,13 +472,10 @@ function Footer({
             </Trans>
           ) : items.length === 2 ? (
             <Trans>
-              <Text style={[a.font_bold, textStyles]}>
-                {getName(items[initialNamesIndex])}{' '}
-              </Text>
-              and
+              <Text style={[a.font_bold, textStyles]}>You</Text> and
               <Text> </Text>
               <Text style={[a.font_bold, textStyles]}>
-                {getName(items[state.currentStep === 'Profiles' ? 0 : 1])}{' '}
+                {getName(items[initialNamesIndex])}{' '}
               </Text>
               are included in your starter pack
             </Trans>
@@ -579,9 +565,9 @@ function Footer({
 
 function getName(item: AppBskyActorDefs.ProfileViewBasic | GeneratorView) {
   if (typeof item.displayName === 'string') {
-    return enforceLen(sanitizeDisplayName(item.displayName), 16, true)
+    return enforceLen(sanitizeDisplayName(item.displayName), 28, true)
   } else if (typeof item.handle === 'string') {
-    return enforceLen(sanitizeHandle(item.handle), 16, true)
+    return enforceLen(sanitizeHandle(item.handle), 28, true)
   }
   return ''
 }