Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Samuel Newman
Hailey
and committed by
GitHub
d2c42cf1 406993cf

+141 -106
+1 -1
package.json
··· 49 49 "open-analyzer": "EXPO_PUBLIC_OPEN_ANALYZER=1 yarn build-web" 50 50 }, 51 51 "dependencies": { 52 - "@atproto/api": "^0.12.11", 52 + "@atproto/api": "^0.12.13", 53 53 "@bam.tech/react-native-image-resizer": "^3.0.4", 54 54 "@braintree/sanitize-url": "^6.0.2", 55 55 "@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
+3 -2
src/state/queries/app-passwords.ts
··· 25 25 return useMutation< 26 26 ComAtprotoServerCreateAppPassword.OutputSchema, 27 27 Error, 28 - {name: string} 28 + {name: string; privileged: boolean} 29 29 >({ 30 - mutationFn: async ({name}) => { 30 + mutationFn: async ({name, privileged}) => { 31 31 return ( 32 32 await getAgent().com.atproto.server.createAppPassword({ 33 33 name, 34 + privileged, 34 35 }) 35 36 ).data 36 37 },
+98 -81
src/view/com/modals/AddAppPasswords.tsx
··· 8 8 import {msg, Trans} from '@lingui/macro' 9 9 import {useLingui} from '@lingui/react' 10 10 11 + import {usePalette} from '#/lib/hooks/usePalette' 12 + import {s} from '#/lib/styles' 11 13 import {logger} from '#/logger' 14 + import {isNative} from '#/platform/detection' 12 15 import {useModalControls} from '#/state/modals' 13 16 import { 14 17 useAppPasswordCreateMutation, 15 18 useAppPasswordsQuery, 16 19 } from '#/state/queries/app-passwords' 17 - import {usePalette} from 'lib/hooks/usePalette' 18 - import {s} from 'lib/styles' 19 - import {isNative} from 'platform/detection' 20 - import {Button} from '../util/forms/Button' 21 - import {Text} from '../util/text/Text' 22 - import * as Toast from '../util/Toast' 20 + import {Button} from '#/view/com/util/forms/Button' 21 + import {Text} from '#/view/com/util/text/Text' 22 + import * as Toast from '#/view/com/util/Toast' 23 + import {atoms as a} from '#/alf' 24 + import * as Toggle from '#/components/forms/Toggle' 25 + import {KeyboardPadding} from '#/components/KeyboardPadding' 23 26 24 - export const snapPoints = ['70%'] 27 + export const snapPoints = ['90%'] 25 28 26 29 const shadesOfBlue: string[] = [ 27 30 'AliceBlue', ··· 70 73 ) 71 74 const [appPassword, setAppPassword] = useState<string>() 72 75 const [wasCopied, setWasCopied] = useState(false) 76 + const [privileged, setPrivileged] = useState(false) 73 77 74 78 const onCopy = React.useCallback(() => { 75 79 if (appPassword) { ··· 109 113 } 110 114 111 115 try { 112 - const newPassword = await mutateAppPassword({name}) 116 + const newPassword = await mutateAppPassword({name, privileged}) 113 117 if (newPassword) { 114 118 setAppPassword(newPassword.password) 115 119 } else { ··· 140 144 141 145 return ( 142 146 <View style={[styles.container, pal.view]} testID="addAppPasswordsModal"> 143 - <View> 144 - {!appPassword ? ( 145 - <Text type="lg" style={[pal.text]}> 147 + {!appPassword ? ( 148 + <> 149 + <View> 150 + <Text type="lg" style={[pal.text]}> 151 + <Trans> 152 + Please enter a unique name for this App Password or use our 153 + randomly generated one. 154 + </Trans> 155 + </Text> 156 + <View style={[pal.btn, styles.textInputWrapper]}> 157 + <TextInput 158 + style={[styles.input, pal.text]} 159 + onChangeText={_onChangeText} 160 + value={name} 161 + placeholder={_(msg`Enter a name for this App Password`)} 162 + placeholderTextColor={pal.colors.textLight} 163 + autoCorrect={false} 164 + autoComplete="off" 165 + autoCapitalize="none" 166 + autoFocus={true} 167 + maxLength={32} 168 + selectTextOnFocus={true} 169 + blurOnSubmit={true} 170 + editable={!isPending} 171 + returnKeyType="done" 172 + onSubmitEditing={createAppPassword} 173 + accessible={true} 174 + accessibilityLabel={_(msg`Name`)} 175 + accessibilityHint={_(msg`Input name for app password`)} 176 + /> 177 + </View> 178 + </View> 179 + <Text type="xs" style={[pal.textLight, s.mb10, s.mt2]}> 146 180 <Trans> 147 - Please enter a unique name for this App Password or use our 148 - randomly generated one. 181 + Can only contain letters, numbers, spaces, dashes, and 182 + underscores. Must be at least 4 characters long, but no more than 183 + 32 characters long. 149 184 </Trans> 150 185 </Text> 151 - ) : ( 152 - <Text type="lg" style={[pal.text]}> 153 - <Text type="lg-bold" style={[pal.text, s.mr5]}> 154 - <Trans>Here is your app password.</Trans> 186 + <Toggle.Item 187 + type="checkbox" 188 + label={_(msg`Allow access to your direct messages`)} 189 + value={privileged} 190 + onChange={val => setPrivileged(val)} 191 + name="privileged" 192 + style={a.my_md}> 193 + <Toggle.Checkbox /> 194 + <Toggle.LabelText> 195 + <Trans>Allow access to your direct messages</Trans> 196 + </Toggle.LabelText> 197 + </Toggle.Item> 198 + </> 199 + ) : ( 200 + <> 201 + <View> 202 + <Text type="lg" style={[pal.text]}> 203 + <Text type="lg-bold" style={[pal.text, s.mr5]}> 204 + <Trans>Here is your app password.</Trans> 205 + </Text> 206 + <Trans> 207 + Use this to sign into the other app along with your handle. 208 + </Trans> 155 209 </Text> 210 + <TouchableOpacity 211 + style={[pal.border, styles.passwordContainer, pal.btn]} 212 + onPress={onCopy} 213 + accessibilityRole="button" 214 + accessibilityLabel={_(msg`Copy`)} 215 + accessibilityHint={_(msg`Copies app password`)}> 216 + <Text type="2xl-bold" style={[pal.text]}> 217 + {appPassword} 218 + </Text> 219 + {wasCopied ? ( 220 + <Text style={[pal.textLight]}> 221 + <Trans>Copied</Trans> 222 + </Text> 223 + ) : ( 224 + <FontAwesomeIcon 225 + icon={['far', 'clone']} 226 + style={pal.text as FontAwesomeIconStyle} 227 + size={18} 228 + /> 229 + )} 230 + </TouchableOpacity> 231 + </View> 232 + <Text type="lg" style={[pal.textLight, s.mb10]}> 156 233 <Trans> 157 - Use this to sign into the other app along with your handle. 234 + For security reasons, you won't be able to view this again. If you 235 + lose this password, you'll need to generate a new one. 158 236 </Trans> 159 237 </Text> 160 - )} 161 - {!appPassword ? ( 162 - <View style={[pal.btn, styles.textInputWrapper]}> 163 - <TextInput 164 - style={[styles.input, pal.text]} 165 - onChangeText={_onChangeText} 166 - value={name} 167 - placeholder={_(msg`Enter a name for this App Password`)} 168 - placeholderTextColor={pal.colors.textLight} 169 - autoCorrect={false} 170 - autoComplete="off" 171 - autoCapitalize="none" 172 - autoFocus={true} 173 - maxLength={32} 174 - selectTextOnFocus={true} 175 - blurOnSubmit={true} 176 - editable={!isPending} 177 - returnKeyType="done" 178 - onSubmitEditing={createAppPassword} 179 - accessible={true} 180 - accessibilityLabel={_(msg`Name`)} 181 - accessibilityHint={_(msg`Input name for app password`)} 182 - /> 183 - </View> 184 - ) : ( 185 - <TouchableOpacity 186 - style={[pal.border, styles.passwordContainer, pal.btn]} 187 - onPress={onCopy} 188 - accessibilityRole="button" 189 - accessibilityLabel={_(msg`Copy`)} 190 - accessibilityHint={_(msg`Copies app password`)}> 191 - <Text type="2xl-bold" style={[pal.text]}> 192 - {appPassword} 193 - </Text> 194 - {wasCopied ? ( 195 - <Text style={[pal.textLight]}> 196 - <Trans>Copied</Trans> 197 - </Text> 198 - ) : ( 199 - <FontAwesomeIcon 200 - icon={['far', 'clone']} 201 - style={pal.text as FontAwesomeIconStyle} 202 - size={18} 203 - /> 204 - )} 205 - </TouchableOpacity> 206 - )} 207 - </View> 208 - {appPassword ? ( 209 - <Text type="lg" style={[pal.textLight, s.mb10]}> 210 - <Trans> 211 - For security reasons, you won't be able to view this again. If you 212 - lose this password, you'll need to generate a new one. 213 - </Trans> 214 - </Text> 215 - ) : ( 216 - <Text type="xs" style={[pal.textLight, s.mb10, s.mt2]}> 217 - <Trans> 218 - Can only contain letters, numbers, spaces, dashes, and underscores. 219 - Must be at least 4 characters long, but no more than 32 characters 220 - long. 221 - </Trans> 222 - </Text> 238 + </> 223 239 )} 224 240 <View style={styles.btnContainer}> 225 241 <Button ··· 230 246 onPress={!appPassword ? createAppPassword : onDone} 231 247 /> 232 248 </View> 249 + <KeyboardPadding /> 233 250 </View> 234 251 ) 235 252 }
+35 -18
src/view/screens/AppPasswords.tsx
··· 5 5 TouchableOpacity, 6 6 View, 7 7 } from 'react-native' 8 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 9 8 import {ScrollView} from 'react-native-gesture-handler' 10 - import {Text} from '../com/util/text/Text' 11 - import {Button} from '../com/util/forms/Button' 12 - import * as Toast from '../com/util/Toast' 13 - import {usePalette} from 'lib/hooks/usePalette' 14 - import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 9 + import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 10 + import {msg, Trans} from '@lingui/macro' 11 + import {useLingui} from '@lingui/react' 12 + import {useFocusEffect} from '@react-navigation/native' 15 13 import {NativeStackScreenProps} from '@react-navigation/native-stack' 16 - import {CommonNavigatorParams} from 'lib/routes/types' 17 - import {useAnalytics} from 'lib/analytics/analytics' 18 - import {useFocusEffect} from '@react-navigation/native' 19 - import {ViewHeader} from '../com/util/ViewHeader' 20 - import {CenteredView} from 'view/com/util/Views' 21 - import {Trans, msg} from '@lingui/macro' 22 - import {useLingui} from '@lingui/react' 23 - import {useSetMinimalShellMode} from '#/state/shell' 14 + 15 + import {useAnalytics} from '#/lib/analytics/analytics' 16 + import {usePalette} from '#/lib/hooks/usePalette' 17 + import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 18 + import {CommonNavigatorParams} from '#/lib/routes/types' 19 + import {cleanError} from '#/lib/strings/errors' 24 20 import {useModalControls} from '#/state/modals' 25 21 import {useLanguagePrefs} from '#/state/preferences' 26 22 import { 27 - useAppPasswordsQuery, 28 23 useAppPasswordDeleteMutation, 24 + useAppPasswordsQuery, 29 25 } from '#/state/queries/app-passwords' 30 - import {ErrorScreen} from '../com/util/error/ErrorScreen' 31 - import {cleanError} from '#/lib/strings/errors' 26 + import {useSetMinimalShellMode} from '#/state/shell' 27 + import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' 28 + import {Button} from '#/view/com/util/forms/Button' 29 + import {Text} from '#/view/com/util/text/Text' 30 + import * as Toast from '#/view/com/util/Toast' 31 + import {ViewHeader} from '#/view/com/util/ViewHeader' 32 + import {CenteredView} from 'view/com/util/Views' 33 + import {atoms as a} from '#/alf' 34 + import {useDialogControl} from '#/components/Dialog' 32 35 import * as Prompt from '#/components/Prompt' 33 - import {useDialogControl} from '#/components/Dialog' 34 36 35 37 type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppPasswords'> 36 38 export function AppPasswords({}: Props) { ··· 135 137 testID={`appPassword-${i}`} 136 138 name={password.name} 137 139 createdAt={password.createdAt} 140 + privileged={password.privileged} 138 141 /> 139 142 ))} 140 143 {isTabletOrDesktop && ( ··· 207 210 testID, 208 211 name, 209 212 createdAt, 213 + privileged, 210 214 }: { 211 215 testID: string 212 216 name: string 213 217 createdAt: string 218 + privileged?: boolean 214 219 }) { 215 220 const pal = usePalette('default') 216 221 const {_} = useLingui() ··· 255 260 }).format(new Date(createdAt))} 256 261 </Trans> 257 262 </Text> 263 + {privileged && ( 264 + <View style={[a.flex_row, a.gap_sm, a.align_center, a.mt_xs]}> 265 + <FontAwesomeIcon 266 + icon="circle-exclamation" 267 + color={pal.colors.textLight} 268 + size={14} 269 + /> 270 + <Text type="md" style={pal.textLight}> 271 + Allows access to direct messages 272 + </Text> 273 + </View> 274 + )} 258 275 </View> 259 276 <FontAwesomeIcon icon={['far', 'trash-can']} style={styles.trashIcon} /> 260 277
+4 -4
yarn.lock
··· 34 34 jsonpointer "^5.0.0" 35 35 leven "^3.1.0" 36 36 37 - "@atproto/api@^0.12.11": 38 - version "0.12.11" 39 - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.12.11.tgz#d117a0c81395153289e99bafa760a05c2836896f" 40 - integrity sha512-NABsZ4ZYznWisr1bGuP6Z4X1GTiu5gNrmAQTxWp45M8RX88BFP1PskoG3J42d2iiyQMVBwTdoENTFYzvsKBuQg== 37 + "@atproto/api@^0.12.13": 38 + version "0.12.13" 39 + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.12.13.tgz#269d6c57ea894e23f20b28bd3cbfed944bd28528" 40 + integrity sha512-pRSID6w8AUiZJoCxgctMPRTSGVFHq7wphAnxEbRLBP3OQ1g+BRZUcqFw+e+17Pd3wrc8VImjiD4HCWtCJvCx3w== 41 41 dependencies: 42 42 "@atproto/common-web" "^0.3.0" 43 43 "@atproto/lexicon" "^0.4.0"