Bluesky app fork with some witchin' additions 💫 witchsky.app
bluesky fork client

Add keyboard shortcuts: new, escape, and hard break (#552)

* Add keyboard shortcuts: new, escape, and hard break

* Add preferences modal

* Remove code accidentally re-added due to rebase

* Fix incorrect copy and lint

* Put stuff back so diffs are clearer

* Re-add invite codes to settings

* Address comments

* Tune the copy

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>

authored by

Ollie H
Paul Frazee
and committed by
GitHub
95f8360d af905947

+78 -26
+2
package.json
··· 49 49 "@sentry/react-native": "4.13.0", 50 50 "@tiptap/core": "^2.0.0-beta.220", 51 51 "@tiptap/extension-document": "^2.0.0-beta.220", 52 + "@tiptap/extension-hard-break": "^2.0.3", 52 53 "@tiptap/extension-history": "^2.0.3", 53 54 "@tiptap/extension-link": "^2.0.0-beta.220", 54 55 "@tiptap/extension-mention": "^2.0.0-beta.220", ··· 58 59 "@tiptap/pm": "^2.0.0-beta.220", 59 60 "@tiptap/react": "^2.0.0-beta.220", 60 61 "@tiptap/suggestion": "^2.0.0-beta.220", 62 + "@types/node": "^18.16.2", 61 63 "@zxing/text-encoding": "^0.9.0", 62 64 "await-lock": "^2.2.2", 63 65 "base64-js": "^1.5.1",
+2 -1
src/state/models/ui/shell.ts
··· 11 11 title: string 12 12 message: string | (() => JSX.Element) 13 13 onPressConfirm: () => void | Promise<void> 14 + onPressCancel?: () => void | Promise<void> 14 15 } 15 16 16 17 export interface EditProfileModal { ··· 86 87 87 88 export type Modal = 88 89 // Account 90 + | AddAppPasswordModal 89 91 | ChangeHandleModal 90 92 | DeleteAccountModal 91 93 | EditProfileModal 92 - | AddAppPasswordModal 93 94 94 95 // Curation 95 96 | ContentFilteringSettingsModal
+24
src/view/com/composer/Composer.tsx
··· 88 88 autocompleteView.setup() 89 89 }, [autocompleteView]) 90 90 91 + const onEscape = useCallback( 92 + (e: KeyboardEvent) => { 93 + if (e.key === 'Escape') { 94 + store.shell.openModal({ 95 + name: 'confirm', 96 + title: 'Cancel draft', 97 + onPressConfirm: onClose, 98 + onPressCancel: () => { 99 + store.shell.closeModal() 100 + }, 101 + message: "Are you sure you'd like to cancel this draft?", 102 + }) 103 + } 104 + }, 105 + [store.shell, onClose], 106 + ) 107 + 108 + useEffect(() => { 109 + if (isDesktopWeb) { 110 + window.addEventListener('keydown', onEscape) 111 + return () => window.removeEventListener('keydown', onEscape) 112 + } 113 + }, [onEscape]) 114 + 91 115 const onPressAddLinkCard = useCallback( 92 116 (uri: string) => { 93 117 setExtLink({uri, isLoading: true})
+2
src/view/com/composer/text-input/TextInput.web.tsx
··· 4 4 import {useEditor, EditorContent, JSONContent} from '@tiptap/react' 5 5 import {Document} from '@tiptap/extension-document' 6 6 import History from '@tiptap/extension-history' 7 + import Hardbreak from '@tiptap/extension-hard-break' 7 8 import {Link} from '@tiptap/extension-link' 8 9 import {Mention} from '@tiptap/extension-mention' 9 10 import {Paragraph} from '@tiptap/extension-paragraph' ··· 72 73 }), 73 74 Text, 74 75 History, 76 + Hardbreak, 75 77 ], 76 78 editorProps: { 77 79 attributes: {
+2 -2
src/view/com/modals/AltImageRead.tsx
··· 34 34 testID="altTextImageSaveBtn" 35 35 onPress={onPress} 36 36 accessibilityRole="button" 37 - accessibilityLabel="Save" 38 - accessibilityHint="Save alt text"> 37 + accessibilityLabel="Done" 38 + accessibilityHint="Closes alt text modal"> 39 39 <LinearGradient 40 40 colors={[gradients.blueLight.start, gradients.blueLight.end]} 41 41 start={{x: 0, y: 0}}
+24 -3
src/view/com/modals/Confirm.tsx
··· 19 19 title, 20 20 message, 21 21 onPressConfirm, 22 + onPressCancel, 22 23 }: { 23 24 title: string 24 25 message: string | (() => JSX.Element) 25 26 onPressConfirm: () => void | Promise<void> 27 + onPressCancel?: () => void | Promise<void> 26 28 }) { 27 29 const pal = usePalette('default') 28 30 const store = useStores() ··· 69 71 style={[styles.btn]} 70 72 accessibilityRole="button" 71 73 accessibilityLabel="Confirm" 72 - // TODO: This needs to be updated so that modal roles are clear; 73 - // Currently there is only one usage for the confirm modal: post deletion 74 - accessibilityHint="Confirms a potentially destructive action"> 74 + accessibilityHint=""> 75 75 <Text style={[s.white, s.bold, s.f18]}>Confirm</Text> 76 76 </TouchableOpacity> 77 77 )} 78 + {onPressCancel === undefined ? null : ( 79 + <TouchableOpacity 80 + testID="cancelBtn" 81 + onPress={onPressCancel} 82 + style={[styles.btnCancel, s.mt10]} 83 + accessibilityRole="button" 84 + accessibilityLabel="Cancel" 85 + accessibilityHint=""> 86 + <Text type="button-lg" style={pal.textLight}> 87 + Cancel 88 + </Text> 89 + </TouchableOpacity> 90 + )} 78 91 </View> 79 92 ) 80 93 } ··· 103 116 marginTop: 22, 104 117 marginHorizontal: 44, 105 118 backgroundColor: colors.blue3, 119 + }, 120 + btnCancel: { 121 + flexDirection: 'row', 122 + alignItems: 'center', 123 + justifyContent: 'center', 124 + borderRadius: 32, 125 + padding: 14, 126 + marginHorizontal: 20, 106 127 }, 107 128 })
+6 -4
src/view/com/util/forms/ToggleButton.tsx
··· 142 142 ]} 143 143 /> 144 144 </View> 145 - <Text type="button" style={[labelStyle, styles.label]}> 146 - {label} 147 - </Text> 145 + {label === '' ? null : ( 146 + <Text type="button" style={[labelStyle, styles.label]}> 147 + {label} 148 + </Text> 149 + )} 148 150 </View> 149 151 </Button> 150 152 ) ··· 154 156 outer: { 155 157 flexDirection: 'row', 156 158 alignItems: 'center', 159 + gap: 10, 157 160 }, 158 161 circle: { 159 162 width: 42, ··· 161 164 borderRadius: 15, 162 165 padding: 4, 163 166 borderWidth: 1, 164 - marginRight: 10, 165 167 }, 166 168 circleFill: { 167 169 width: 16,
+6 -16
src/view/screens/Settings.tsx
··· 34 34 import {AccountData} from 'state/models/session' 35 35 import {useAnalytics} from 'lib/analytics' 36 36 import {NavigationProp} from 'lib/routes/types' 37 - import {pluralize} from 'lib/strings/helpers' 38 37 import {isDesktopWeb} from 'platform/detection' 38 + import {pluralize} from 'lib/strings/helpers' 39 39 40 40 type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'> 41 41 export const SettingsScreen = withAuthRequired( ··· 54 54 light: {color: colors.blue3}, 55 55 dark: {color: colors.blue2}, 56 56 }) 57 + 57 58 const dangerBg = useCustomPalette<ViewStyle>({ 58 59 light: {backgroundColor: colors.red1}, 59 60 dark: {backgroundColor: colors.red7}, ··· 140 141 }, [store]) 141 142 142 143 return ( 143 - <View style={s.hContentRegion} testID="settingsScreen"> 144 + <View style={[s.hContentRegion]} testID="settingsScreen"> 144 145 <ViewHeader title="Settings" /> 145 146 <ScrollView 146 - style={s.hContentRegion} 147 + style={[s.hContentRegion]} 147 148 contentContainerStyle={!isDesktopWeb && pal.viewLight} 148 149 scrollIndicatorInsets={{right: 1}}> 149 - <View style={styles.spacer20} /> 150 150 <View style={[s.flexRow, styles.heading]}> 151 151 <Text type="xl-bold" style={pal.text}> 152 152 Signed in as ··· 161 161 <Link 162 162 href={`/profile/${store.me.handle}`} 163 163 title="Your profile" 164 - noFeedback 165 - accessibilityLabel={`Signed in as ${store.me.handle}`} 166 - accessibilityHint="Double tap to sign out"> 164 + noFeedback> 167 165 <View style={[pal.view, styles.linkCard]}> 168 166 <View style={styles.avi}> 169 167 <UserAvatar size={40} avatar={store.me.avatar} /> ··· 231 229 Add account 232 230 </Text> 233 231 </TouchableOpacity> 234 - 235 232 <View style={styles.spacer20} /> 236 - 237 233 <Text type="xl-bold" style={[pal.text, styles.heading]}> 238 234 Invite a friend 239 235 </Text> ··· 301 297 Blocked accounts 302 298 </Text> 303 299 </Link> 304 - 305 300 <View style={styles.spacer20} /> 306 - 307 301 <Text type="xl-bold" style={[pal.text, styles.heading]}> 308 302 Advanced 309 303 </Text> ··· 338 332 Change my handle 339 333 </Text> 340 334 </TouchableOpacity> 341 - 342 335 <View style={styles.spacer20} /> 343 - 344 336 <Text type="xl-bold" style={[pal.text, styles.heading]}> 345 337 Danger zone 346 338 </Text> ··· 355 347 <FontAwesomeIcon 356 348 icon={['far', 'trash-can']} 357 349 style={dangerText as FontAwesomeIconStyle} 358 - size={21} 350 + size={18} 359 351 /> 360 352 </View> 361 353 <Text type="lg" style={dangerText}> 362 354 Delete my account 363 355 </Text> 364 356 </TouchableOpacity> 365 - 366 357 <View style={styles.spacer20} /> 367 - 368 358 <Text type="xl-bold" style={[pal.text, styles.heading]}> 369 359 Developer tools 370 360 </Text>
+10
yarn.lock
··· 4433 4433 dependencies: 4434 4434 tippy.js "^6.3.7" 4435 4435 4436 + "@tiptap/extension-hard-break@^2.0.3": 4437 + version "2.0.3" 4438 + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.0.3.tgz#aa7805d825e5244bdccc508da18c781e231b2859" 4439 + integrity sha512-RCln6ARn16jvKTjhkcAD5KzYXYS0xRMc0/LrHeV8TKdCd4Yd0YYHe0PU4F9gAgAfPQn7Dgt4uTVJLN11ICl8sQ== 4440 + 4436 4441 "@tiptap/extension-history@^2.0.3": 4437 4442 version "2.0.3" 4438 4443 resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.0.3.tgz#8936c15aa46f2ddeada1c3d9abe2888d58d08c30" ··· 4819 4824 version "18.16.3" 4820 4825 resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.3.tgz#6bda7819aae6ea0b386ebc5b24bdf602f1b42b01" 4821 4826 integrity sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q== 4827 + 4828 + "@types/node@^18.16.2": 4829 + version "18.16.2" 4830 + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.2.tgz#2f610ea71034b3971c312192377f8a7178eb57f1" 4831 + integrity sha512-GQW/JL/5Fz/0I8RpeBG9lKp0+aNcXEaVL71c0D2Q0QHDTFvlYKT7an0onCUXj85anv7b4/WesqdfchLc0jtsCg== 4822 4832 4823 4833 "@types/object.omit@^3.0.0": 4824 4834 version "3.0.0"