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
parent
af905947bc
commit
95f8360d19
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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})
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
|
@ -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,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue