about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2024-05-24 00:10:13 +0100
committerGitHub <noreply@github.com>2024-05-23 16:10:13 -0700
commitd2c42cf16905a8904dcfbba4825ca5f8abc3f253 (patch)
treeb7a46c68b714b7eaa3c938387617f4174b6c1924
parent406993cf0e5d5fee2bac75aacc528da12c4e0289 (diff)
downloadvoidsky-d2c42cf16905a8904dcfbba4825ca5f8abc3f253.tar.zst
Privileged app passwords (#4200)
* add checkbox to create privileged app password

* add indicator to privileged app pwds to list

* bump api

* oops missed the yarnlock

* adjust modal padding

* lowercase

* one more lowercase

---------

Co-authored-by: Hailey <me@haileyok.com>
-rw-r--r--package.json2
-rw-r--r--src/state/queries/app-passwords.ts5
-rw-r--r--src/view/com/modals/AddAppPasswords.tsx179
-rw-r--r--src/view/screens/AppPasswords.tsx53
-rw-r--r--yarn.lock8
5 files changed, 141 insertions, 106 deletions
diff --git a/package.json b/package.json
index 2680b2d1b..4c79e29f9 100644
--- a/package.json
+++ b/package.json
@@ -49,7 +49,7 @@
     "open-analyzer": "EXPO_PUBLIC_OPEN_ANALYZER=1 yarn build-web"
   },
   "dependencies": {
-    "@atproto/api": "^0.12.11",
+    "@atproto/api": "^0.12.13",
     "@bam.tech/react-native-image-resizer": "^3.0.4",
     "@braintree/sanitize-url": "^6.0.2",
     "@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
diff --git a/src/state/queries/app-passwords.ts b/src/state/queries/app-passwords.ts
index a8f8fba0f..33009a3a4 100644
--- a/src/state/queries/app-passwords.ts
+++ b/src/state/queries/app-passwords.ts
@@ -25,12 +25,13 @@ export function useAppPasswordCreateMutation() {
   return useMutation<
     ComAtprotoServerCreateAppPassword.OutputSchema,
     Error,
-    {name: string}
+    {name: string; privileged: boolean}
   >({
-    mutationFn: async ({name}) => {
+    mutationFn: async ({name, privileged}) => {
       return (
         await getAgent().com.atproto.server.createAppPassword({
           name,
+          privileged,
         })
       ).data
     },
diff --git a/src/view/com/modals/AddAppPasswords.tsx b/src/view/com/modals/AddAppPasswords.tsx
index e6f424ed0..d6df12657 100644
--- a/src/view/com/modals/AddAppPasswords.tsx
+++ b/src/view/com/modals/AddAppPasswords.tsx
@@ -8,20 +8,23 @@ import {
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
+import {usePalette} from '#/lib/hooks/usePalette'
+import {s} from '#/lib/styles'
 import {logger} from '#/logger'
+import {isNative} from '#/platform/detection'
 import {useModalControls} from '#/state/modals'
 import {
   useAppPasswordCreateMutation,
   useAppPasswordsQuery,
 } from '#/state/queries/app-passwords'
-import {usePalette} from 'lib/hooks/usePalette'
-import {s} from 'lib/styles'
-import {isNative} from 'platform/detection'
-import {Button} from '../util/forms/Button'
-import {Text} from '../util/text/Text'
-import * as Toast from '../util/Toast'
+import {Button} from '#/view/com/util/forms/Button'
+import {Text} from '#/view/com/util/text/Text'
+import * as Toast from '#/view/com/util/Toast'
+import {atoms as a} from '#/alf'
+import * as Toggle from '#/components/forms/Toggle'
+import {KeyboardPadding} from '#/components/KeyboardPadding'
 
-export const snapPoints = ['70%']
+export const snapPoints = ['90%']
 
 const shadesOfBlue: string[] = [
   'AliceBlue',
@@ -70,6 +73,7 @@ export function Component({}: {}) {
   )
   const [appPassword, setAppPassword] = useState<string>()
   const [wasCopied, setWasCopied] = useState(false)
+  const [privileged, setPrivileged] = useState(false)
 
   const onCopy = React.useCallback(() => {
     if (appPassword) {
@@ -109,7 +113,7 @@ export function Component({}: {}) {
     }
 
     try {
-      const newPassword = await mutateAppPassword({name})
+      const newPassword = await mutateAppPassword({name, privileged})
       if (newPassword) {
         setAppPassword(newPassword.password)
       } else {
@@ -140,86 +144,98 @@ export function Component({}: {}) {
 
   return (
     <View style={[styles.container, pal.view]} testID="addAppPasswordsModal">
-      <View>
-        {!appPassword ? (
-          <Text type="lg" style={[pal.text]}>
+      {!appPassword ? (
+        <>
+          <View>
+            <Text type="lg" style={[pal.text]}>
+              <Trans>
+                Please enter a unique name for this App Password or use our
+                randomly generated one.
+              </Trans>
+            </Text>
+            <View style={[pal.btn, styles.textInputWrapper]}>
+              <TextInput
+                style={[styles.input, pal.text]}
+                onChangeText={_onChangeText}
+                value={name}
+                placeholder={_(msg`Enter a name for this App Password`)}
+                placeholderTextColor={pal.colors.textLight}
+                autoCorrect={false}
+                autoComplete="off"
+                autoCapitalize="none"
+                autoFocus={true}
+                maxLength={32}
+                selectTextOnFocus={true}
+                blurOnSubmit={true}
+                editable={!isPending}
+                returnKeyType="done"
+                onSubmitEditing={createAppPassword}
+                accessible={true}
+                accessibilityLabel={_(msg`Name`)}
+                accessibilityHint={_(msg`Input name for app password`)}
+              />
+            </View>
+          </View>
+          <Text type="xs" style={[pal.textLight, s.mb10, s.mt2]}>
             <Trans>
-              Please enter a unique name for this App Password or use our
-              randomly generated one.
+              Can only contain letters, numbers, spaces, dashes, and
+              underscores. Must be at least 4 characters long, but no more than
+              32 characters long.
             </Trans>
           </Text>
-        ) : (
-          <Text type="lg" style={[pal.text]}>
-            <Text type="lg-bold" style={[pal.text, s.mr5]}>
-              <Trans>Here is your app password.</Trans>
+          <Toggle.Item
+            type="checkbox"
+            label={_(msg`Allow access to your direct messages`)}
+            value={privileged}
+            onChange={val => setPrivileged(val)}
+            name="privileged"
+            style={a.my_md}>
+            <Toggle.Checkbox />
+            <Toggle.LabelText>
+              <Trans>Allow access to your direct messages</Trans>
+            </Toggle.LabelText>
+          </Toggle.Item>
+        </>
+      ) : (
+        <>
+          <View>
+            <Text type="lg" style={[pal.text]}>
+              <Text type="lg-bold" style={[pal.text, s.mr5]}>
+                <Trans>Here is your app password.</Trans>
+              </Text>
+              <Trans>
+                Use this to sign into the other app along with your handle.
+              </Trans>
             </Text>
+            <TouchableOpacity
+              style={[pal.border, styles.passwordContainer, pal.btn]}
+              onPress={onCopy}
+              accessibilityRole="button"
+              accessibilityLabel={_(msg`Copy`)}
+              accessibilityHint={_(msg`Copies app password`)}>
+              <Text type="2xl-bold" style={[pal.text]}>
+                {appPassword}
+              </Text>
+              {wasCopied ? (
+                <Text style={[pal.textLight]}>
+                  <Trans>Copied</Trans>
+                </Text>
+              ) : (
+                <FontAwesomeIcon
+                  icon={['far', 'clone']}
+                  style={pal.text as FontAwesomeIconStyle}
+                  size={18}
+                />
+              )}
+            </TouchableOpacity>
+          </View>
+          <Text type="lg" style={[pal.textLight, s.mb10]}>
             <Trans>
-              Use this to sign into the other app along with your handle.
+              For security reasons, you won't be able to view this again. If you
+              lose this password, you'll need to generate a new one.
             </Trans>
           </Text>
-        )}
-        {!appPassword ? (
-          <View style={[pal.btn, styles.textInputWrapper]}>
-            <TextInput
-              style={[styles.input, pal.text]}
-              onChangeText={_onChangeText}
-              value={name}
-              placeholder={_(msg`Enter a name for this App Password`)}
-              placeholderTextColor={pal.colors.textLight}
-              autoCorrect={false}
-              autoComplete="off"
-              autoCapitalize="none"
-              autoFocus={true}
-              maxLength={32}
-              selectTextOnFocus={true}
-              blurOnSubmit={true}
-              editable={!isPending}
-              returnKeyType="done"
-              onSubmitEditing={createAppPassword}
-              accessible={true}
-              accessibilityLabel={_(msg`Name`)}
-              accessibilityHint={_(msg`Input name for app password`)}
-            />
-          </View>
-        ) : (
-          <TouchableOpacity
-            style={[pal.border, styles.passwordContainer, pal.btn]}
-            onPress={onCopy}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Copy`)}
-            accessibilityHint={_(msg`Copies app password`)}>
-            <Text type="2xl-bold" style={[pal.text]}>
-              {appPassword}
-            </Text>
-            {wasCopied ? (
-              <Text style={[pal.textLight]}>
-                <Trans>Copied</Trans>
-              </Text>
-            ) : (
-              <FontAwesomeIcon
-                icon={['far', 'clone']}
-                style={pal.text as FontAwesomeIconStyle}
-                size={18}
-              />
-            )}
-          </TouchableOpacity>
-        )}
-      </View>
-      {appPassword ? (
-        <Text type="lg" style={[pal.textLight, s.mb10]}>
-          <Trans>
-            For security reasons, you won't be able to view this again. If you
-            lose this password, you'll need to generate a new one.
-          </Trans>
-        </Text>
-      ) : (
-        <Text type="xs" style={[pal.textLight, s.mb10, s.mt2]}>
-          <Trans>
-            Can only contain letters, numbers, spaces, dashes, and underscores.
-            Must be at least 4 characters long, but no more than 32 characters
-            long.
-          </Trans>
-        </Text>
+        </>
       )}
       <View style={styles.btnContainer}>
         <Button
@@ -230,6 +246,7 @@ export function Component({}: {}) {
           onPress={!appPassword ? createAppPassword : onDone}
         />
       </View>
+      <KeyboardPadding />
     </View>
   )
 }
diff --git a/src/view/screens/AppPasswords.tsx b/src/view/screens/AppPasswords.tsx
index 800216169..65cbb7374 100644
--- a/src/view/screens/AppPasswords.tsx
+++ b/src/view/screens/AppPasswords.tsx
@@ -5,32 +5,34 @@ import {
   TouchableOpacity,
   View,
 } from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {ScrollView} from 'react-native-gesture-handler'
-import {Text} from '../com/util/text/Text'
-import {Button} from '../com/util/forms/Button'
-import * as Toast from '../com/util/Toast'
-import {usePalette} from 'lib/hooks/usePalette'
-import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
-import {NativeStackScreenProps} from '@react-navigation/native-stack'
-import {CommonNavigatorParams} from 'lib/routes/types'
-import {useAnalytics} from 'lib/analytics/analytics'
-import {useFocusEffect} from '@react-navigation/native'
-import {ViewHeader} from '../com/util/ViewHeader'
-import {CenteredView} from 'view/com/util/Views'
-import {Trans, msg} from '@lingui/macro'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import {useSetMinimalShellMode} from '#/state/shell'
+import {useFocusEffect} from '@react-navigation/native'
+import {NativeStackScreenProps} from '@react-navigation/native-stack'
+
+import {useAnalytics} from '#/lib/analytics/analytics'
+import {usePalette} from '#/lib/hooks/usePalette'
+import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
+import {CommonNavigatorParams} from '#/lib/routes/types'
+import {cleanError} from '#/lib/strings/errors'
 import {useModalControls} from '#/state/modals'
 import {useLanguagePrefs} from '#/state/preferences'
 import {
-  useAppPasswordsQuery,
   useAppPasswordDeleteMutation,
+  useAppPasswordsQuery,
 } from '#/state/queries/app-passwords'
-import {ErrorScreen} from '../com/util/error/ErrorScreen'
-import {cleanError} from '#/lib/strings/errors'
-import * as Prompt from '#/components/Prompt'
+import {useSetMinimalShellMode} from '#/state/shell'
+import {ErrorScreen} from '#/view/com/util/error/ErrorScreen'
+import {Button} from '#/view/com/util/forms/Button'
+import {Text} from '#/view/com/util/text/Text'
+import * as Toast from '#/view/com/util/Toast'
+import {ViewHeader} from '#/view/com/util/ViewHeader'
+import {CenteredView} from 'view/com/util/Views'
+import {atoms as a} from '#/alf'
 import {useDialogControl} from '#/components/Dialog'
+import * as Prompt from '#/components/Prompt'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppPasswords'>
 export function AppPasswords({}: Props) {
@@ -135,6 +137,7 @@ export function AppPasswords({}: Props) {
               testID={`appPassword-${i}`}
               name={password.name}
               createdAt={password.createdAt}
+              privileged={password.privileged}
             />
           ))}
           {isTabletOrDesktop && (
@@ -207,10 +210,12 @@ function AppPassword({
   testID,
   name,
   createdAt,
+  privileged,
 }: {
   testID: string
   name: string
   createdAt: string
+  privileged?: boolean
 }) {
   const pal = usePalette('default')
   const {_} = useLingui()
@@ -255,6 +260,18 @@ function AppPassword({
             }).format(new Date(createdAt))}
           </Trans>
         </Text>
+        {privileged && (
+          <View style={[a.flex_row, a.gap_sm, a.align_center, a.mt_xs]}>
+            <FontAwesomeIcon
+              icon="circle-exclamation"
+              color={pal.colors.textLight}
+              size={14}
+            />
+            <Text type="md" style={pal.textLight}>
+              Allows access to direct messages
+            </Text>
+          </View>
+        )}
       </View>
       <FontAwesomeIcon icon={['far', 'trash-can']} style={styles.trashIcon} />
 
diff --git a/yarn.lock b/yarn.lock
index c7dc9e342..96bd60b7f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -34,10 +34,10 @@
     jsonpointer "^5.0.0"
     leven "^3.1.0"
 
-"@atproto/api@^0.12.11":
-  version "0.12.11"
-  resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.12.11.tgz#d117a0c81395153289e99bafa760a05c2836896f"
-  integrity sha512-NABsZ4ZYznWisr1bGuP6Z4X1GTiu5gNrmAQTxWp45M8RX88BFP1PskoG3J42d2iiyQMVBwTdoENTFYzvsKBuQg==
+"@atproto/api@^0.12.13":
+  version "0.12.13"
+  resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.12.13.tgz#269d6c57ea894e23f20b28bd3cbfed944bd28528"
+  integrity sha512-pRSID6w8AUiZJoCxgctMPRTSGVFHq7wphAnxEbRLBP3OQ1g+BRZUcqFw+e+17Pd3wrc8VImjiD4HCWtCJvCx3w==
   dependencies:
     "@atproto/common-web" "^0.3.0"
     "@atproto/lexicon" "^0.4.0"