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>
zio/stable
Ollie H 2023-05-02 21:00:18 -07:00 committed by GitHub
parent af905947bc
commit 95f8360d19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 78 additions and 26 deletions

View File

@ -49,6 +49,7 @@
"@sentry/react-native": "4.13.0", "@sentry/react-native": "4.13.0",
"@tiptap/core": "^2.0.0-beta.220", "@tiptap/core": "^2.0.0-beta.220",
"@tiptap/extension-document": "^2.0.0-beta.220", "@tiptap/extension-document": "^2.0.0-beta.220",
"@tiptap/extension-hard-break": "^2.0.3",
"@tiptap/extension-history": "^2.0.3", "@tiptap/extension-history": "^2.0.3",
"@tiptap/extension-link": "^2.0.0-beta.220", "@tiptap/extension-link": "^2.0.0-beta.220",
"@tiptap/extension-mention": "^2.0.0-beta.220", "@tiptap/extension-mention": "^2.0.0-beta.220",
@ -58,6 +59,7 @@
"@tiptap/pm": "^2.0.0-beta.220", "@tiptap/pm": "^2.0.0-beta.220",
"@tiptap/react": "^2.0.0-beta.220", "@tiptap/react": "^2.0.0-beta.220",
"@tiptap/suggestion": "^2.0.0-beta.220", "@tiptap/suggestion": "^2.0.0-beta.220",
"@types/node": "^18.16.2",
"@zxing/text-encoding": "^0.9.0", "@zxing/text-encoding": "^0.9.0",
"await-lock": "^2.2.2", "await-lock": "^2.2.2",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",

View File

@ -11,6 +11,7 @@ export interface ConfirmModal {
title: string title: string
message: string | (() => JSX.Element) message: string | (() => JSX.Element)
onPressConfirm: () => void | Promise<void> onPressConfirm: () => void | Promise<void>
onPressCancel?: () => void | Promise<void>
} }
export interface EditProfileModal { export interface EditProfileModal {
@ -86,10 +87,10 @@ export interface ContentFilteringSettingsModal {
export type Modal = export type Modal =
// Account // Account
| AddAppPasswordModal
| ChangeHandleModal | ChangeHandleModal
| DeleteAccountModal | DeleteAccountModal
| EditProfileModal | EditProfileModal
| AddAppPasswordModal
// Curation // Curation
| ContentFilteringSettingsModal | ContentFilteringSettingsModal

View File

@ -88,6 +88,30 @@ export const ComposePost = observer(function ComposePost({
autocompleteView.setup() autocompleteView.setup()
}, [autocompleteView]) }, [autocompleteView])
const onEscape = useCallback(
(e: KeyboardEvent) => {
if (e.key === 'Escape') {
store.shell.openModal({
name: 'confirm',
title: 'Cancel draft',
onPressConfirm: onClose,
onPressCancel: () => {
store.shell.closeModal()
},
message: "Are you sure you'd like to cancel this draft?",
})
}
},
[store.shell, onClose],
)
useEffect(() => {
if (isDesktopWeb) {
window.addEventListener('keydown', onEscape)
return () => window.removeEventListener('keydown', onEscape)
}
}, [onEscape])
const onPressAddLinkCard = useCallback( const onPressAddLinkCard = useCallback(
(uri: string) => { (uri: string) => {
setExtLink({uri, isLoading: true}) setExtLink({uri, isLoading: true})

View File

@ -4,6 +4,7 @@ import {RichText} from '@atproto/api'
import {useEditor, EditorContent, JSONContent} from '@tiptap/react' import {useEditor, EditorContent, JSONContent} from '@tiptap/react'
import {Document} from '@tiptap/extension-document' import {Document} from '@tiptap/extension-document'
import History from '@tiptap/extension-history' import History from '@tiptap/extension-history'
import Hardbreak from '@tiptap/extension-hard-break'
import {Link} from '@tiptap/extension-link' import {Link} from '@tiptap/extension-link'
import {Mention} from '@tiptap/extension-mention' import {Mention} from '@tiptap/extension-mention'
import {Paragraph} from '@tiptap/extension-paragraph' import {Paragraph} from '@tiptap/extension-paragraph'
@ -72,6 +73,7 @@ export const TextInput = React.forwardRef(
}), }),
Text, Text,
History, History,
Hardbreak,
], ],
editorProps: { editorProps: {
attributes: { attributes: {

View File

@ -34,8 +34,8 @@ export function Component({altText}: Props) {
testID="altTextImageSaveBtn" testID="altTextImageSaveBtn"
onPress={onPress} onPress={onPress}
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel="Save" accessibilityLabel="Done"
accessibilityHint="Save alt text"> accessibilityHint="Closes alt text modal">
<LinearGradient <LinearGradient
colors={[gradients.blueLight.start, gradients.blueLight.end]} colors={[gradients.blueLight.start, gradients.blueLight.end]}
start={{x: 0, y: 0}} start={{x: 0, y: 0}}

View File

@ -19,10 +19,12 @@ export function Component({
title, title,
message, message,
onPressConfirm, onPressConfirm,
onPressCancel,
}: { }: {
title: string title: string
message: string | (() => JSX.Element) message: string | (() => JSX.Element)
onPressConfirm: () => void | Promise<void> onPressConfirm: () => void | Promise<void>
onPressCancel?: () => void | Promise<void>
}) { }) {
const pal = usePalette('default') const pal = usePalette('default')
const store = useStores() const store = useStores()
@ -69,12 +71,23 @@ export function Component({
style={[styles.btn]} style={[styles.btn]}
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel="Confirm" accessibilityLabel="Confirm"
// TODO: This needs to be updated so that modal roles are clear; accessibilityHint="">
// Currently there is only one usage for the confirm modal: post deletion
accessibilityHint="Confirms a potentially destructive action">
<Text style={[s.white, s.bold, s.f18]}>Confirm</Text> <Text style={[s.white, s.bold, s.f18]}>Confirm</Text>
</TouchableOpacity> </TouchableOpacity>
)} )}
{onPressCancel === undefined ? null : (
<TouchableOpacity
testID="cancelBtn"
onPress={onPressCancel}
style={[styles.btnCancel, s.mt10]}
accessibilityRole="button"
accessibilityLabel="Cancel"
accessibilityHint="">
<Text type="button-lg" style={pal.textLight}>
Cancel
</Text>
</TouchableOpacity>
)}
</View> </View>
) )
} }
@ -104,4 +117,12 @@ const styles = StyleSheet.create({
marginHorizontal: 44, marginHorizontal: 44,
backgroundColor: colors.blue3, backgroundColor: colors.blue3,
}, },
btnCancel: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 32,
padding: 14,
marginHorizontal: 20,
},
}) })

View File

@ -142,9 +142,11 @@ export function ToggleButton({
]} ]}
/> />
</View> </View>
<Text type="button" style={[labelStyle, styles.label]}> {label === '' ? null : (
{label} <Text type="button" style={[labelStyle, styles.label]}>
</Text> {label}
</Text>
)}
</View> </View>
</Button> </Button>
) )
@ -154,6 +156,7 @@ const styles = StyleSheet.create({
outer: { outer: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
gap: 10,
}, },
circle: { circle: {
width: 42, width: 42,
@ -161,7 +164,6 @@ const styles = StyleSheet.create({
borderRadius: 15, borderRadius: 15,
padding: 4, padding: 4,
borderWidth: 1, borderWidth: 1,
marginRight: 10,
}, },
circleFill: { circleFill: {
width: 16, width: 16,

View File

@ -34,8 +34,8 @@ import {useCustomPalette} from 'lib/hooks/useCustomPalette'
import {AccountData} from 'state/models/session' import {AccountData} from 'state/models/session'
import {useAnalytics} from 'lib/analytics' import {useAnalytics} from 'lib/analytics'
import {NavigationProp} from 'lib/routes/types' import {NavigationProp} from 'lib/routes/types'
import {pluralize} from 'lib/strings/helpers'
import {isDesktopWeb} from 'platform/detection' import {isDesktopWeb} from 'platform/detection'
import {pluralize} from 'lib/strings/helpers'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'> type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'>
export const SettingsScreen = withAuthRequired( export const SettingsScreen = withAuthRequired(
@ -54,6 +54,7 @@ export const SettingsScreen = withAuthRequired(
light: {color: colors.blue3}, light: {color: colors.blue3},
dark: {color: colors.blue2}, dark: {color: colors.blue2},
}) })
const dangerBg = useCustomPalette<ViewStyle>({ const dangerBg = useCustomPalette<ViewStyle>({
light: {backgroundColor: colors.red1}, light: {backgroundColor: colors.red1},
dark: {backgroundColor: colors.red7}, dark: {backgroundColor: colors.red7},
@ -140,13 +141,12 @@ export const SettingsScreen = withAuthRequired(
}, [store]) }, [store])
return ( return (
<View style={s.hContentRegion} testID="settingsScreen"> <View style={[s.hContentRegion]} testID="settingsScreen">
<ViewHeader title="Settings" /> <ViewHeader title="Settings" />
<ScrollView <ScrollView
style={s.hContentRegion} style={[s.hContentRegion]}
contentContainerStyle={!isDesktopWeb && pal.viewLight} contentContainerStyle={!isDesktopWeb && pal.viewLight}
scrollIndicatorInsets={{right: 1}}> scrollIndicatorInsets={{right: 1}}>
<View style={styles.spacer20} />
<View style={[s.flexRow, styles.heading]}> <View style={[s.flexRow, styles.heading]}>
<Text type="xl-bold" style={pal.text}> <Text type="xl-bold" style={pal.text}>
Signed in as Signed in as
@ -161,9 +161,7 @@ export const SettingsScreen = withAuthRequired(
<Link <Link
href={`/profile/${store.me.handle}`} href={`/profile/${store.me.handle}`}
title="Your profile" title="Your profile"
noFeedback noFeedback>
accessibilityLabel={`Signed in as ${store.me.handle}`}
accessibilityHint="Double tap to sign out">
<View style={[pal.view, styles.linkCard]}> <View style={[pal.view, styles.linkCard]}>
<View style={styles.avi}> <View style={styles.avi}>
<UserAvatar size={40} avatar={store.me.avatar} /> <UserAvatar size={40} avatar={store.me.avatar} />
@ -231,9 +229,7 @@ export const SettingsScreen = withAuthRequired(
Add account Add account
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
<View style={styles.spacer20} /> <View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}> <Text type="xl-bold" style={[pal.text, styles.heading]}>
Invite a friend Invite a friend
</Text> </Text>
@ -301,9 +297,7 @@ export const SettingsScreen = withAuthRequired(
Blocked accounts Blocked accounts
</Text> </Text>
</Link> </Link>
<View style={styles.spacer20} /> <View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}> <Text type="xl-bold" style={[pal.text, styles.heading]}>
Advanced Advanced
</Text> </Text>
@ -338,9 +332,7 @@ export const SettingsScreen = withAuthRequired(
Change my handle Change my handle
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
<View style={styles.spacer20} /> <View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}> <Text type="xl-bold" style={[pal.text, styles.heading]}>
Danger zone Danger zone
</Text> </Text>
@ -355,16 +347,14 @@ export const SettingsScreen = withAuthRequired(
<FontAwesomeIcon <FontAwesomeIcon
icon={['far', 'trash-can']} icon={['far', 'trash-can']}
style={dangerText as FontAwesomeIconStyle} style={dangerText as FontAwesomeIconStyle}
size={21} size={18}
/> />
</View> </View>
<Text type="lg" style={dangerText}> <Text type="lg" style={dangerText}>
Delete my account Delete my account
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
<View style={styles.spacer20} /> <View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}> <Text type="xl-bold" style={[pal.text, styles.heading]}>
Developer tools Developer tools
</Text> </Text>

View File

@ -4433,6 +4433,11 @@
dependencies: dependencies:
tippy.js "^6.3.7" tippy.js "^6.3.7"
"@tiptap/extension-hard-break@^2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.0.3.tgz#aa7805d825e5244bdccc508da18c781e231b2859"
integrity sha512-RCln6ARn16jvKTjhkcAD5KzYXYS0xRMc0/LrHeV8TKdCd4Yd0YYHe0PU4F9gAgAfPQn7Dgt4uTVJLN11ICl8sQ==
"@tiptap/extension-history@^2.0.3": "@tiptap/extension-history@^2.0.3":
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.0.3.tgz#8936c15aa46f2ddeada1c3d9abe2888d58d08c30" resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.0.3.tgz#8936c15aa46f2ddeada1c3d9abe2888d58d08c30"
@ -4820,6 +4825,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.3.tgz#6bda7819aae6ea0b386ebc5b24bdf602f1b42b01" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.3.tgz#6bda7819aae6ea0b386ebc5b24bdf602f1b42b01"
integrity sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q== integrity sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q==
"@types/node@^18.16.2":
version "18.16.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.2.tgz#2f610ea71034b3971c312192377f8a7178eb57f1"
integrity sha512-GQW/JL/5Fz/0I8RpeBG9lKp0+aNcXEaVL71c0D2Q0QHDTFvlYKT7an0onCUXj85anv7b4/WesqdfchLc0jtsCg==
"@types/object.omit@^3.0.0": "@types/object.omit@^3.0.0":
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/object.omit/-/object.omit-3.0.0.tgz#0d31e1208eac8fe2ad5c9499a1016a8273bbfafc" resolved "https://registry.yarnpkg.com/@types/object.omit/-/object.omit-3.0.0.tgz#0d31e1208eac8fe2ad5c9499a1016a8273bbfafc"