diff options
Diffstat (limited to 'src/view/com/util/forms')
-rw-r--r-- | src/view/com/util/forms/Button.tsx | 4 | ||||
-rw-r--r-- | src/view/com/util/forms/DropdownButton.tsx | 5 | ||||
-rw-r--r-- | src/view/com/util/forms/PostDropdownBtn.tsx | 153 | ||||
-rw-r--r-- | src/view/com/util/forms/SearchInput.tsx | 7 |
4 files changed, 115 insertions, 54 deletions
diff --git a/src/view/com/util/forms/Button.tsx b/src/view/com/util/forms/Button.tsx index 270d98317..8f24f8288 100644 --- a/src/view/com/util/forms/Button.tsx +++ b/src/view/com/util/forms/Button.tsx @@ -52,6 +52,7 @@ export function Button({ accessibilityLabelledBy, onAccessibilityEscape, withLoading = false, + disabled = false, }: React.PropsWithChildren<{ type?: ButtonType label?: string @@ -65,6 +66,7 @@ export function Button({ accessibilityLabelledBy?: string onAccessibilityEscape?: () => void withLoading?: boolean + disabled?: boolean }>) { const theme = useTheme() const typeOuterStyle = choose<ViewStyle, Record<ButtonType, ViewStyle>>( @@ -198,7 +200,7 @@ export function Button({ <Pressable style={getStyle} onPress={onPressWrapped} - disabled={isLoading} + disabled={disabled || isLoading} testID={testID} accessibilityRole="button" accessibilityLabel={accessibilityLabel} diff --git a/src/view/com/util/forms/DropdownButton.tsx b/src/view/com/util/forms/DropdownButton.tsx index 1bed60b5d..ad8f50f5e 100644 --- a/src/view/com/util/forms/DropdownButton.tsx +++ b/src/view/com/util/forms/DropdownButton.tsx @@ -17,6 +17,8 @@ import {colors} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' import {useTheme} from 'lib/ThemeContext' import {HITSLOP_10} from 'lib/constants' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' const ESTIMATED_BTN_HEIGHT = 50 const ESTIMATED_SEP_HEIGHT = 16 @@ -207,6 +209,7 @@ const DropdownItems = ({ }: DropDownItemProps) => { const pal = usePalette('default') const theme = useTheme() + const {_} = useLingui() const dropDownBackgroundColor = theme.colorScheme === 'dark' ? pal.btn : pal.view const separatorColor = @@ -224,7 +227,7 @@ const DropdownItems = ({ {/* This TouchableWithoutFeedback renders the background so if the user clicks outside, the dropdown closes */} <TouchableWithoutFeedback onPress={onOuterPress} - accessibilityLabel="Toggle dropdown" + accessibilityLabel={_(msg`Toggle dropdown`)} accessibilityHint=""> <View style={[styles.bg]} /> </TouchableWithoutFeedback> diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx index 1fffa3123..1ba5ae8ae 100644 --- a/src/view/com/util/forms/PostDropdownBtn.tsx +++ b/src/view/com/util/forms/PostDropdownBtn.tsx @@ -1,49 +1,101 @@ import React from 'react' -import {StyleProp, View, ViewStyle} from 'react-native' +import {Linking, StyleProp, View, ViewStyle} from 'react-native' +import Clipboard from '@react-native-clipboard/clipboard' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {AppBskyFeedDefs, AppBskyFeedPost, AtUri} from '@atproto/api' import {toShareUrl} from 'lib/strings/url-helpers' -import {useStores} from 'state/index' import {useTheme} from 'lib/ThemeContext' import {shareUrl} from 'lib/sharing' import { NativeDropdown, DropdownItem as NativeDropdownItem, } from './NativeDropdown' +import * as Toast from '../Toast' import {EventStopper} from '../EventStopper' +import {useModalControls} from '#/state/modals' +import {makeProfileLink} from '#/lib/routes/links' +import {getTranslatorLink} from '#/locale/helpers' +import {usePostDeleteMutation} from '#/state/queries/post' +import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads' +import {useLanguagePrefs} from '#/state/preferences' +import {logger} from '#/logger' +import {Shadow} from '#/state/cache/types' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useSession} from '#/state/session' +import {isWeb} from '#/platform/detection' export function PostDropdownBtn({ testID, - itemUri, - itemCid, - itemHref, - isAuthor, - isThreadMuted, - onCopyPostText, - onOpenTranslate, - onToggleThreadMute, - onDeletePost, + post, + record, style, }: { testID: string - itemUri: string - itemCid: string - itemHref: string - itemTitle: string - isAuthor: boolean - isThreadMuted: boolean - onCopyPostText: () => void - onOpenTranslate: () => void - onToggleThreadMute: () => void - onDeletePost: () => void + post: Shadow<AppBskyFeedDefs.PostView> + record: AppBskyFeedPost.Record style?: StyleProp<ViewStyle> }) { - const store = useStores() + const {hasSession, currentAccount} = useSession() const theme = useTheme() + const {_} = useLingui() const defaultCtrlColor = theme.palette.default.postCtrl + const {openModal} = useModalControls() + const langPrefs = useLanguagePrefs() + const mutedThreads = useMutedThreads() + const toggleThreadMute = useToggleThreadMute() + const postDeleteMutation = usePostDeleteMutation() + + const rootUri = record.reply?.root?.uri || post.uri + const isThreadMuted = mutedThreads.includes(rootUri) + const isAuthor = post.author.did === currentAccount?.did + const href = React.useMemo(() => { + const urip = new AtUri(post.uri) + return makeProfileLink(post.author, 'post', urip.rkey) + }, [post.uri, post.author]) + + const translatorUrl = getTranslatorLink( + record.text, + langPrefs.primaryLanguage, + ) + + const onDeletePost = React.useCallback(() => { + postDeleteMutation.mutateAsync({uri: post.uri}).then( + () => { + Toast.show('Post deleted') + }, + e => { + logger.error('Failed to delete post', {error: e}) + Toast.show('Failed to delete post, please try again') + }, + ) + }, [post, postDeleteMutation]) + + const onToggleThreadMute = React.useCallback(() => { + try { + const muted = toggleThreadMute(rootUri) + if (muted) { + Toast.show('You will no longer receive notifications for this thread') + } else { + Toast.show('You will now receive notifications for this thread') + } + } catch (e) { + logger.error('Failed to toggle thread mute', {error: e}) + } + }, [rootUri, toggleThreadMute]) + + const onCopyPostText = React.useCallback(() => { + Clipboard.setString(record?.text || '') + Toast.show('Copied to clipboard') + }, [record]) + + const onOpenTranslate = React.useCallback(() => { + Linking.openURL(translatorUrl) + }, [translatorUrl]) const dropdownItems: NativeDropdownItem[] = [ { - label: 'Translate', + label: _(msg`Translate`), onPress() { onOpenTranslate() }, @@ -57,7 +109,7 @@ export function PostDropdownBtn({ }, }, { - label: 'Copy post text', + label: _(msg`Copy post text`), onPress() { onCopyPostText() }, @@ -71,9 +123,9 @@ export function PostDropdownBtn({ }, }, { - label: 'Share', + label: isWeb ? _(msg`Copy link to post`) : _(msg`Share`), onPress() { - const url = toShareUrl(itemHref) + const url = toShareUrl(href) shareUrl(url) }, testID: 'postDropdownShareBtn', @@ -85,11 +137,11 @@ export function PostDropdownBtn({ web: 'share', }, }, - { + hasSession && { label: 'separator', }, - { - label: isThreadMuted ? 'Unmute thread' : 'Mute thread', + hasSession && { + label: isThreadMuted ? _(msg`Unmute thread`) : _(msg`Mute thread`), onPress() { onToggleThreadMute() }, @@ -102,37 +154,38 @@ export function PostDropdownBtn({ web: 'comment-slash', }, }, - { + hasSession && { label: 'separator', }, - !isAuthor && { - label: 'Report post', - onPress() { - store.shell.openModal({ - name: 'report', - uri: itemUri, - cid: itemCid, - }) - }, - testID: 'postDropdownReportBtn', - icon: { - ios: { - name: 'exclamationmark.triangle', + !isAuthor && + hasSession && { + label: _(msg`Report post`), + onPress() { + openModal({ + name: 'report', + uri: post.uri, + cid: post.cid, + }) + }, + testID: 'postDropdownReportBtn', + icon: { + ios: { + name: 'exclamationmark.triangle', + }, + android: 'ic_menu_report_image', + web: 'circle-exclamation', }, - android: 'ic_menu_report_image', - web: 'circle-exclamation', }, - }, isAuthor && { label: 'separator', }, isAuthor && { - label: 'Delete post', + label: _(msg`Delete post`), onPress() { - store.shell.openModal({ + openModal({ name: 'confirm', - title: 'Delete this post?', - message: 'Are you sure? This can not be undone.', + title: _(msg`Delete this post?`), + message: _(msg`Are you sure? This cannot be undone.`), onPressConfirm: onDeletePost, }) }, diff --git a/src/view/com/util/forms/SearchInput.tsx b/src/view/com/util/forms/SearchInput.tsx index c1eb82bd4..02b462b55 100644 --- a/src/view/com/util/forms/SearchInput.tsx +++ b/src/view/com/util/forms/SearchInput.tsx @@ -14,6 +14,8 @@ import { import {MagnifyingGlassIcon} from 'lib/icons' import {useTheme} from 'lib/ThemeContext' import {usePalette} from 'lib/hooks/usePalette' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' interface Props { query: string @@ -33,6 +35,7 @@ export function SearchInput({ }: Props) { const theme = useTheme() const pal = usePalette('default') + const {_} = useLingui() const textInput = React.useRef<TextInput>(null) const onPressCancelSearchInner = React.useCallback(() => { @@ -58,7 +61,7 @@ export function SearchInput({ onChangeText={onChangeQuery} onSubmitEditing={onSubmitQuery} accessibilityRole="search" - accessibilityLabel="Search" + accessibilityLabel={_(msg`Search`)} accessibilityHint="" autoCorrect={false} autoCapitalize="none" @@ -67,7 +70,7 @@ export function SearchInput({ <TouchableOpacity onPress={onPressCancelSearchInner} accessibilityRole="button" - accessibilityLabel="Clear search query" + accessibilityLabel={_(msg`Clear search query`)} accessibilityHint=""> <FontAwesomeIcon icon="xmark" |