diff options
Diffstat (limited to 'src/view')
-rw-r--r-- | src/view/com/composer/Composer.tsx | 71 | ||||
-rw-r--r-- | src/view/com/composer/photos/OpenCameraBtn.tsx | 59 | ||||
-rw-r--r-- | src/view/com/composer/photos/SelectGifBtn.tsx | 53 | ||||
-rw-r--r-- | src/view/com/composer/photos/SelectPhotoBtn.tsx | 55 | ||||
-rw-r--r-- | src/view/screens/Storybook/Buttons.tsx | 2 | ||||
-rw-r--r-- | src/view/screens/Storybook/Icons.tsx | 6 |
6 files changed, 154 insertions, 92 deletions
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index f90bdbee2..f0f630dd4 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -13,7 +13,6 @@ import { KeyboardAvoidingView, LayoutAnimation, Platform, - Pressable, ScrollView, StyleSheet, TouchableOpacity, @@ -27,6 +26,7 @@ import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {observer} from 'mobx-react-lite' +import {LikelyType} from '#/lib/link-meta/link-meta' import {logEvent} from '#/lib/statsig/statsig' import {logger} from '#/logger' import {emitPostCreated} from '#/state/events' @@ -37,6 +37,7 @@ import { useLanguagePrefs, useLanguagePrefsApi, } from '#/state/preferences/languages' +import {Gif} from '#/state/queries/giphy' import {useProfileQuery} from '#/state/queries/profile' import {ThreadgateSetting} from '#/state/queries/threadgate' import {getAgent, useSession} from '#/state/session' @@ -56,6 +57,9 @@ import {useDialogStateControlContext} from 'state/dialogs' import {GalleryModel} from 'state/models/media/gallery' import {ComposerOpts} from 'state/shell/composer' import {ComposerReplyTo} from 'view/com/composer/ComposerReplyTo' +import {atoms as a} from '#/alf' +import {Button} from '#/components/Button' +import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji' import * as Prompt from '#/components/Prompt' import {QuoteEmbed} from '../util/post-embeds/QuoteEmbed' import {Text} from '../util/text/Text' @@ -66,6 +70,7 @@ import {ExternalEmbed} from './ExternalEmbed' import {LabelsBtn} from './labels/LabelsBtn' import {Gallery} from './photos/Gallery' import {OpenCameraBtn} from './photos/OpenCameraBtn' +import {SelectGifBtn} from './photos/SelectGifBtn' import {SelectPhotoBtn} from './photos/SelectPhotoBtn' import {SelectLangBtn} from './select-language/SelectLangBtn' import {SuggestedLanguage} from './select-language/SuggestedLanguage' @@ -314,13 +319,33 @@ export const ComposePost = observer(function ComposePost({ ? _(msg`Write your reply`) : _(msg`What's up?`) - const canSelectImages = useMemo(() => gallery.size < 4, [gallery.size]) + const canSelectImages = gallery.size < 4 && !extLink const hasMedia = gallery.size > 0 || Boolean(extLink) const onEmojiButtonPress = useCallback(() => { openPicker?.(textInput.current?.getCursorPosition()) }, [openPicker]) + const focusTextInput = useCallback(() => { + textInput.current?.focus() + }, []) + + const onSelectGif = useCallback( + (gif: Gif) => + setExtLink({ + uri: gif.url, + isLoading: true, + meta: { + url: gif.url, + image: gif.images.original_still.url, + likelyType: LikelyType.HTML, + title: `${gif.title} - Find & Share on GIPHY`, + description: `ALT: ${gif.alt_text}`, + }, + }), + [setExtLink], + ) + return ( <KeyboardAvoidingView testID="composePostView" @@ -473,25 +498,27 @@ export const ComposePost = observer(function ComposePost({ </ScrollView> <SuggestedLanguage text={richtext.text} /> <View style={[pal.border, styles.bottomBar]}> - {canSelectImages ? ( - <> - <SelectPhotoBtn gallery={gallery} /> - <OpenCameraBtn gallery={gallery} /> - </> - ) : null} - {!isMobile ? ( - <Pressable - onPress={onEmojiButtonPress} - accessibilityRole="button" - accessibilityLabel={_(msg`Open emoji picker`)} - accessibilityHint={_(msg`Open emoji picker`)}> - <FontAwesomeIcon - icon={['far', 'face-smile']} - color={pal.colors.link} - size={22} - /> - </Pressable> - ) : null} + <View style={[a.flex_row, a.align_center, a.gap_xs]}> + <SelectPhotoBtn gallery={gallery} disabled={!canSelectImages} /> + <OpenCameraBtn gallery={gallery} disabled={!canSelectImages} /> + <SelectGifBtn + onClose={focusTextInput} + onSelectGif={onSelectGif} + disabled={hasMedia} + /> + {!isMobile ? ( + <Button + onPress={onEmojiButtonPress} + style={a.p_sm} + label={_(msg`Open emoji picker`)} + accessibilityHint={_(msg`Open emoji picker`)} + variant="ghost" + shape="round" + color="primary"> + <EmojiSmile size="lg" /> + </Button> + ) : null} + </View> <View style={s.flex1} /> <SelectLangBtn /> <CharProgress count={graphemeLength} /> @@ -586,7 +613,7 @@ const styles = StyleSheet.create({ }, bottomBar: { flexDirection: 'row', - paddingVertical: 10, + paddingVertical: 4, paddingLeft: 15, paddingRight: 20, alignItems: 'center', diff --git a/src/view/com/composer/photos/OpenCameraBtn.tsx b/src/view/com/composer/photos/OpenCameraBtn.tsx index 4353704d5..8f9152e34 100644 --- a/src/view/com/composer/photos/OpenCameraBtn.tsx +++ b/src/view/com/composer/photos/OpenCameraBtn.tsx @@ -1,32 +1,31 @@ import React, {useCallback} from 'react' -import {TouchableOpacity, StyleSheet} from 'react-native' import * as MediaLibrary from 'expo-media-library' -import { - FontAwesomeIcon, - FontAwesomeIconStyle, -} from '@fortawesome/react-native-fontawesome' -import {usePalette} from 'lib/hooks/usePalette' -import {useAnalytics} from 'lib/analytics/analytics' -import {openCamera} from 'lib/media/picker' -import {useCameraPermission} from 'lib/hooks/usePermissions' -import {HITSLOP_10, POST_IMG_MAX} from 'lib/constants' -import {GalleryModel} from 'state/models/media/gallery' -import {isMobileWeb, isNative} from 'platform/detection' -import {logger} from '#/logger' -import {useLingui} from '@lingui/react' import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {useAnalytics} from '#/lib/analytics/analytics' +import {POST_IMG_MAX} from '#/lib/constants' +import {useCameraPermission} from '#/lib/hooks/usePermissions' +import {openCamera} from '#/lib/media/picker' +import {logger} from '#/logger' +import {isMobileWeb, isNative} from '#/platform/detection' +import {GalleryModel} from '#/state/models/media/gallery' +import {atoms as a, useTheme} from '#/alf' +import {Button} from '#/components/Button' +import {Camera_Stroke2_Corner0_Rounded as Camera} from '#/components/icons/Camera' type Props = { gallery: GalleryModel + disabled?: boolean } -export function OpenCameraBtn({gallery}: Props) { - const pal = usePalette('default') +export function OpenCameraBtn({gallery, disabled}: Props) { const {track} = useAnalytics() const {_} = useLingui() const {requestCameraAccessIfNeeded} = useCameraPermission() const [mediaPermissionRes, requestMediaPermission] = MediaLibrary.usePermissions() + const t = useTheme() const onPressTakePicture = useCallback(async () => { track('Composer:CameraOpened') @@ -68,25 +67,17 @@ export function OpenCameraBtn({gallery}: Props) { } return ( - <TouchableOpacity + <Button testID="openCameraButton" onPress={onPressTakePicture} - style={styles.button} - hitSlop={HITSLOP_10} - accessibilityRole="button" - accessibilityLabel={_(msg`Camera`)} - accessibilityHint={_(msg`Opens camera on device`)}> - <FontAwesomeIcon - icon="camera" - style={pal.link as FontAwesomeIconStyle} - size={24} - /> - </TouchableOpacity> + label={_(msg`Camera`)} + accessibilityHint={_(msg`Opens camera on device`)} + style={a.p_sm} + variant="ghost" + shape="round" + color="primary" + disabled={disabled}> + <Camera size="lg" style={disabled && t.atoms.text_contrast_low} /> + </Button> ) } - -const styles = StyleSheet.create({ - button: { - paddingHorizontal: 15, - }, -}) diff --git a/src/view/com/composer/photos/SelectGifBtn.tsx b/src/view/com/composer/photos/SelectGifBtn.tsx new file mode 100644 index 000000000..31310fdc1 --- /dev/null +++ b/src/view/com/composer/photos/SelectGifBtn.tsx @@ -0,0 +1,53 @@ +import React, {useCallback} from 'react' +import {Keyboard} from 'react-native' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {logEvent} from '#/lib/statsig/statsig' +import {Gif} from '#/state/queries/giphy' +import {atoms as a, useTheme} from '#/alf' +import {Button} from '#/components/Button' +import {useDialogControl} from '#/components/Dialog' +import {GifSelectDialog} from '#/components/dialogs/GifSelect' +import {GifSquare_Stroke2_Corner0_Rounded as GifIcon} from '#/components/icons/Gif' + +type Props = { + onClose: () => void + onSelectGif: (gif: Gif) => void + disabled?: boolean +} + +export function SelectGifBtn({onClose, onSelectGif, disabled}: Props) { + const {_} = useLingui() + const control = useDialogControl() + const t = useTheme() + + const onPressSelectGif = useCallback(async () => { + logEvent('composer:gif:open', {}) + Keyboard.dismiss() + control.open() + }, [control]) + + return ( + <> + <Button + testID="openGifBtn" + onPress={onPressSelectGif} + label={_(msg`Select GIF`)} + accessibilityHint={_(msg`Opens GIF select dialog`)} + style={a.p_sm} + variant="ghost" + shape="round" + color="primary" + disabled={disabled}> + <GifIcon size="lg" style={disabled && t.atoms.text_contrast_low} /> + </Button> + + <GifSelectDialog + control={control} + onClose={onClose} + onSelectGif={onSelectGif} + /> + </> + ) +} diff --git a/src/view/com/composer/photos/SelectPhotoBtn.tsx b/src/view/com/composer/photos/SelectPhotoBtn.tsx index f7fa9502d..747653fc8 100644 --- a/src/view/com/composer/photos/SelectPhotoBtn.tsx +++ b/src/view/com/composer/photos/SelectPhotoBtn.tsx @@ -1,27 +1,26 @@ +/* eslint-disable react-native-a11y/has-valid-accessibility-ignores-invert-colors */ import React, {useCallback} from 'react' -import {TouchableOpacity, StyleSheet} from 'react-native' -import { - FontAwesomeIcon, - FontAwesomeIconStyle, -} from '@fortawesome/react-native-fontawesome' -import {usePalette} from 'lib/hooks/usePalette' -import {useAnalytics} from 'lib/analytics/analytics' -import {usePhotoLibraryPermission} from 'lib/hooks/usePermissions' -import {GalleryModel} from 'state/models/media/gallery' -import {HITSLOP_10} from 'lib/constants' -import {isNative} from 'platform/detection' -import {useLingui} from '@lingui/react' import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {useAnalytics} from '#/lib/analytics/analytics' +import {usePhotoLibraryPermission} from '#/lib/hooks/usePermissions' +import {isNative} from '#/platform/detection' +import {GalleryModel} from '#/state/models/media/gallery' +import {atoms as a, useTheme} from '#/alf' +import {Button} from '#/components/Button' +import {Image_Stroke2_Corner0_Rounded as Image} from '#/components/icons/Image' type Props = { gallery: GalleryModel + disabled?: boolean } -export function SelectPhotoBtn({gallery}: Props) { - const pal = usePalette('default') +export function SelectPhotoBtn({gallery, disabled}: Props) { const {track} = useAnalytics() const {_} = useLingui() const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission() + const t = useTheme() const onPressSelectPhotos = useCallback(async () => { track('Composer:GalleryOpened') @@ -34,25 +33,17 @@ export function SelectPhotoBtn({gallery}: Props) { }, [track, requestPhotoAccessIfNeeded, gallery]) return ( - <TouchableOpacity + <Button testID="openGalleryBtn" onPress={onPressSelectPhotos} - style={styles.button} - hitSlop={HITSLOP_10} - accessibilityRole="button" - accessibilityLabel={_(msg`Gallery`)} - accessibilityHint={_(msg`Opens device photo gallery`)}> - <FontAwesomeIcon - icon={['far', 'image']} - style={pal.link as FontAwesomeIconStyle} - size={24} - /> - </TouchableOpacity> + label={_(msg`Gallery`)} + accessibilityHint={_(msg`Opens device photo gallery`)} + style={a.p_sm} + variant="ghost" + shape="round" + color="primary" + disabled={disabled}> + <Image size="lg" style={disabled && t.atoms.text_contrast_low} /> + </Button> ) } - -const styles = StyleSheet.create({ - button: { - paddingHorizontal: 15, - }, -}) diff --git a/src/view/screens/Storybook/Buttons.tsx b/src/view/screens/Storybook/Buttons.tsx index cae8ec314..b532b0dd1 100644 --- a/src/view/screens/Storybook/Buttons.tsx +++ b/src/view/screens/Storybook/Buttons.tsx @@ -9,7 +9,7 @@ import { ButtonText, ButtonVariant, } from '#/components/Button' -import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRight} from '#/components/icons/ArrowTopRight' +import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRight} from '#/components/icons/Arrow' import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron' import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' import {H1} from '#/components/Typography' diff --git a/src/view/screens/Storybook/Icons.tsx b/src/view/screens/Storybook/Icons.tsx index 9d7dc0aa8..bff1fdc9b 100644 --- a/src/view/screens/Storybook/Icons.tsx +++ b/src/view/screens/Storybook/Icons.tsx @@ -2,11 +2,11 @@ import React from 'react' import {View} from 'react-native' import {atoms as a, useTheme} from '#/alf' -import {H1} from '#/components/Typography' -import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' -import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRight} from '#/components/icons/ArrowTopRight' +import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRight} from '#/components/icons/Arrow' import {CalendarDays_Stroke2_Corner0_Rounded as CalendarDays} from '#/components/icons/CalendarDays' +import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' import {Loader} from '#/components/Loader' +import {H1} from '#/components/Typography' export function Icons() { const t = useTheme() |