diff options
author | Samuel Newman <mozzius@protonmail.com> | 2024-10-16 18:52:14 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-16 08:52:14 -0700 |
commit | 6bc00f8d714fa1113d03f57ec06caccd4ffbb9dc (patch) | |
tree | 852790dfdfcc04584a56496e8219c08414120773 | |
parent | c3d0cc55d98fb32b25cd2164cfa1c399985e7c84 (diff) | |
download | voidsky-6bc00f8d714fa1113d03f57ec06caccd4ffbb9dc.tar.zst |
Composer - Self label dialog ALF rewrite (#4354)
-rw-r--r-- | src/components/dialogs/GifSelect.tsx | 2 | ||||
-rw-r--r-- | src/components/forms/ToggleButton.tsx | 2 | ||||
-rw-r--r-- | src/state/modals/index.tsx | 8 | ||||
-rw-r--r-- | src/view/com/composer/Composer.tsx | 10 | ||||
-rw-r--r-- | src/view/com/composer/labels/LabelsBtn.tsx | 225 | ||||
-rw-r--r-- | src/view/com/composer/photos/SelectGifBtn.tsx | 2 | ||||
-rw-r--r-- | src/view/com/modals/Modal.tsx | 4 | ||||
-rw-r--r-- | src/view/com/modals/Modal.web.tsx | 3 | ||||
-rw-r--r-- | src/view/com/modals/SelfLabel.tsx | 204 |
9 files changed, 182 insertions, 278 deletions
diff --git a/src/components/dialogs/GifSelect.tsx b/src/components/dialogs/GifSelect.tsx index ffd8130db..64ad2624a 100644 --- a/src/components/dialogs/GifSelect.tsx +++ b/src/components/dialogs/GifSelect.tsx @@ -37,7 +37,7 @@ export function GifSelectDialog({ onSelectGif: onSelectGifProp, }: { controlRef: React.RefObject<{open: () => void}> - onClose: () => void + onClose?: () => void onSelectGif: (gif: Gif) => void }) { const control = Dialog.useDialogControl() diff --git a/src/components/forms/ToggleButton.tsx b/src/components/forms/ToggleButton.tsx index f47a272b1..8e08665fd 100644 --- a/src/components/forms/ToggleButton.tsx +++ b/src/components/forms/ToggleButton.tsx @@ -36,7 +36,7 @@ export function Group({children, multiple, ...props}: GroupProps) { export function Button({children, ...props}: ItemProps) { return ( - <Toggle.Item {...props} style={[a.flex_grow]}> + <Toggle.Item {...props} style={[a.flex_grow, a.flex_1]}> <ButtonInner>{children}</ButtonInner> </Toggle.Item> ) diff --git a/src/state/modals/index.tsx b/src/state/modals/index.tsx index 03ab73f43..05e0c53f6 100644 --- a/src/state/modals/index.tsx +++ b/src/state/modals/index.tsx @@ -42,13 +42,6 @@ export interface DeleteAccountModal { name: 'delete-account' } -export interface SelfLabelModal { - name: 'self-label' - labels: string[] - hasMedia: boolean - onChange: (labels: string[]) => void -} - export interface ChangeHandleModal { name: 'change-handle' onChanged: () => void @@ -120,7 +113,6 @@ export type Modal = // Posts | CropImageModal - | SelfLabelModal // Bluesky access | WaitlistModal diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index a45477d11..2f28b9665 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -499,10 +499,6 @@ export const ComposePost = ({ openEmojiPicker?.(textInput.current?.getCursorPosition()) }, [openEmojiPicker]) - const focusTextInput = useCallback(() => { - textInput.current?.focus() - }, []) - const onSelectGif = useCallback((gif: Gif) => { dispatch({type: 'embed_add_gif', gif}) }, []) @@ -808,11 +804,7 @@ export const ComposePost = ({ disabled={!canSelectImages} onAdd={onImageAdd} /> - <SelectGifBtn - onClose={focusTextInput} - onSelectGif={onSelectGif} - disabled={hasMedia} - /> + <SelectGifBtn onSelectGif={onSelectGif} disabled={hasMedia} /> {!isMobile ? ( <Button onPress={onEmojiButtonPress} diff --git a/src/view/com/composer/labels/LabelsBtn.tsx b/src/view/com/composer/labels/LabelsBtn.tsx index 27e3813dc..d72366aea 100644 --- a/src/view/com/composer/labels/LabelsBtn.tsx +++ b/src/view/com/composer/labels/LabelsBtn.tsx @@ -1,15 +1,17 @@ import React from 'react' -import {Keyboard, StyleSheet} from 'react-native' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome' -import {msg} from '@lingui/macro' +import {Keyboard, LayoutAnimation, View} from 'react-native' +import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useModalControls} from '#/state/modals' -import {usePalette} from 'lib/hooks/usePalette' -import {ShieldExclamation} from 'lib/icons' -import {isNative} from 'platform/detection' -import {Button} from 'view/com/util/forms/Button' +import {ShieldExclamation} from '#/lib/icons' +import {atoms as a, useTheme} from '#/alf' +import {Button, ButtonText} from '#/components/Button' +import * as Dialog from '#/components/Dialog' +import * as ToggleButton from '#/components/forms/ToggleButton' +import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' +import {Text} from '#/components/Typography' + +const ADULT_CONTENT_LABELS = ['sexual', 'nudity', 'porn'] export function LabelsBtn({ labels, @@ -20,48 +22,177 @@ export function LabelsBtn({ hasMedia: boolean onChange: (v: string[]) => void }) { - const pal = usePalette('default') + const control = Dialog.useDialogControl() + const t = useTheme() const {_} = useLingui() - const {openModal} = useModalControls() + + const removeAdultLabel = () => { + const final = labels.filter(l => !ADULT_CONTENT_LABELS.includes(l)) + onChange(final) + LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) + } + + const hasAdultSelection = + labels.includes('sexual') || + labels.includes('nudity') || + labels.includes('porn') + + if (!hasMedia && hasAdultSelection) { + removeAdultLabel() + } return ( - <Button - type="default-light" - testID="labelsBtn" - style={[styles.button, !hasMedia && styles.dimmed]} - accessibilityLabel={_(msg`Content warnings`)} - accessibilityHint="" - onPress={() => { - if (isNative) { - if (Keyboard.isVisible()) { - Keyboard.dismiss() - } - } - openModal({name: 'self-label', labels, hasMedia, onChange}) - }}> - <ShieldExclamation style={pal.link} size={24} /> - {labels.length > 0 ? ( - <FontAwesomeIcon - icon="check" - size={16} - style={pal.link as FontAwesomeIconStyle} + <> + <Button + testID="labelsBtn" + style={!hasMedia && {opacity: 0.4}} + label={_(msg`Content warnings`)} + accessibilityHint={_( + msg`Opens a dialog to add a content warning to your post`, + )} + onPress={() => { + Keyboard.dismiss() + control.open() + }}> + <ShieldExclamation style={{color: t.palette.primary_500}} size={24} /> + {labels.length > 0 ? ( + <Check size="sm" fill={t.palette.primary_500} /> + ) : null} + </Button> + + <Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}> + <Dialog.Handle /> + <DialogInner + labels={labels} + onChange={onChange} + hasAdultSelection={hasAdultSelection} + hasMedia={hasMedia} + removeAdultLabel={removeAdultLabel} /> - ) : null} - </Button> + </Dialog.Outer> + </> ) } -const styles = StyleSheet.create({ - button: { - flexDirection: 'row', - alignItems: 'center', - paddingVertical: 2, - paddingHorizontal: 6, - }, - dimmed: { - opacity: 0.4, - }, - label: { - maxWidth: 100, - }, -}) +function DialogInner({ + labels, + onChange, + hasAdultSelection, + hasMedia, + removeAdultLabel, +}: { + labels: string[] + onChange: (v: string[]) => void + hasAdultSelection: boolean + hasMedia: boolean + removeAdultLabel: () => void +}) { + const {_} = useLingui() + const control = Dialog.useDialogContext() + const t = useTheme() + + return ( + <Dialog.ScrollableInner + label={_(msg`Add a content warning`)} + style={[{maxWidth: 500}, a.w_full]}> + <View style={[a.flex_1, a.gap_md]}> + <Text style={[a.text_2xl, a.font_bold]}> + <Trans>Add a content warning</Trans> + </Text> + + <View + style={[ + a.border, + a.p_md, + t.atoms.border_contrast_high, + a.rounded_md, + ]}> + <View + style={[a.flex_row, a.align_center, a.justify_between, a.pb_sm]}> + <Text style={[a.font_bold, a.text_lg]}> + <Trans>Adult Content</Trans> + </Text> + + <Button + label={_(msg`Remove`)} + variant="ghost" + color="primary" + size="tiny" + onPress={removeAdultLabel} + disabled={!hasAdultSelection} + style={{opacity: hasAdultSelection ? 1 : 0}} + aria-hidden={!hasAdultSelection}> + <ButtonText> + <Trans>Remove</Trans> + </ButtonText> + </Button> + </View> + {hasMedia ? ( + <> + <ToggleButton.Group + label={_(msg`Adult Content labels`)} + values={labels} + onChange={values => { + onChange(values) + LayoutAnimation.configureNext( + LayoutAnimation.Presets.easeInEaseOut, + ) + }}> + <ToggleButton.Button name="sexual" label={_(msg`Suggestive`)}> + <ToggleButton.ButtonText> + <Trans>Suggestive</Trans> + </ToggleButton.ButtonText> + </ToggleButton.Button> + <ToggleButton.Button name="nudity" label={_(msg`Nudity`)}> + <ToggleButton.ButtonText> + <Trans>Nudity</Trans> + </ToggleButton.ButtonText> + </ToggleButton.Button> + <ToggleButton.Button name="porn" label={_(msg`Porn`)}> + <ToggleButton.ButtonText> + <Trans>Porn</Trans> + </ToggleButton.ButtonText> + </ToggleButton.Button> + </ToggleButton.Group> + + <Text style={[a.mt_sm, t.atoms.text_contrast_medium]}> + {labels.includes('sexual') ? ( + <Trans>Pictures meant for adults.</Trans> + ) : labels.includes('nudity') ? ( + <Trans>Artistic or non-erotic nudity.</Trans> + ) : labels.includes('porn') ? ( + <Trans>Sexual activity or erotic nudity.</Trans> + ) : ( + <Trans>If none are selected, suitable for all ages.</Trans> + )} + </Text> + </> + ) : ( + <View> + <Text style={t.atoms.text_contrast_medium}> + <Trans> + <Text style={[a.font_bold, t.atoms.text_contrast_medium]}> + Not Applicable. + </Text>{' '} + This warning is only available for posts with media attached. + </Trans> + </Text> + </View> + )} + </View> + </View> + + <Button + label={_(msg`Done`)} + onPress={() => control.close()} + color="primary" + size="large" + variant="solid" + style={a.mt_xl}> + <ButtonText> + <Trans>Done</Trans> + </ButtonText> + </Button> + </Dialog.ScrollableInner> + ) +} diff --git a/src/view/com/composer/photos/SelectGifBtn.tsx b/src/view/com/composer/photos/SelectGifBtn.tsx index d13df0a11..74f9acdc6 100644 --- a/src/view/com/composer/photos/SelectGifBtn.tsx +++ b/src/view/com/composer/photos/SelectGifBtn.tsx @@ -11,7 +11,7 @@ import {GifSelectDialog} from '#/components/dialogs/GifSelect' import {GifSquare_Stroke2_Corner0_Rounded as GifIcon} from '#/components/icons/Gif' type Props = { - onClose: () => void + onClose?: () => void onSelectGif: (gif: Gif) => void disabled?: boolean } diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx index 8cb6ddfef..c2360742e 100644 --- a/src/view/com/modals/Modal.tsx +++ b/src/view/com/modals/Modal.tsx @@ -19,7 +19,6 @@ import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguages import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings' import * as LinkWarningModal from './LinkWarning' import * as ListAddUserModal from './ListAddRemoveUsers' -import * as SelfLabelModal from './SelfLabel' import * as UserAddRemoveListsModal from './UserAddRemoveLists' import * as VerifyEmailModal from './VerifyEmail' @@ -66,9 +65,6 @@ export function ModalsContainer() { } else if (activeModal?.name === 'delete-account') { snapPoints = DeleteAccountModal.snapPoints element = <DeleteAccountModal.Component /> - } else if (activeModal?.name === 'self-label') { - snapPoints = SelfLabelModal.snapPoints - element = <SelfLabelModal.Component {...activeModal} /> } else if (activeModal?.name === 'change-handle') { snapPoints = ChangeHandleModal.snapPoints element = <ChangeHandleModal.Component {...activeModal} /> diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx index 013028944..76b2811b1 100644 --- a/src/view/com/modals/Modal.web.tsx +++ b/src/view/com/modals/Modal.web.tsx @@ -19,7 +19,6 @@ import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguages import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings' import * as LinkWarningModal from './LinkWarning' import * as ListAddUserModal from './ListAddRemoveUsers' -import * as SelfLabelModal from './SelfLabel' import * as UserAddRemoveLists from './UserAddRemoveLists' import * as VerifyEmailModal from './VerifyEmail' @@ -72,8 +71,6 @@ function Modal({modal}: {modal: ModalIface}) { element = <CropImageModal.Component {...modal} /> } else if (modal.name === 'delete-account') { element = <DeleteAccountModal.Component /> - } else if (modal.name === 'self-label') { - element = <SelfLabelModal.Component {...modal} /> } else if (modal.name === 'change-handle') { element = <ChangeHandleModal.Component {...modal} /> } else if (modal.name === 'invite-codes') { diff --git a/src/view/com/modals/SelfLabel.tsx b/src/view/com/modals/SelfLabel.tsx deleted file mode 100644 index ce3fbcef8..000000000 --- a/src/view/com/modals/SelfLabel.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import React, {useState} from 'react' -import {StyleSheet, TouchableOpacity, View} from 'react-native' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' - -import {useModalControls} from '#/state/modals' -import {usePalette} from 'lib/hooks/usePalette' -import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' -import {colors, s} from 'lib/styles' -import {isWeb} from 'platform/detection' -import {ScrollView} from 'view/com/modals/util' -import {Button} from '../util/forms/Button' -import {SelectableBtn} from '../util/forms/SelectableBtn' -import {Text} from '../util/text/Text' - -const ADULT_CONTENT_LABELS = ['sexual', 'nudity', 'porn'] - -export const snapPoints = ['50%'] - -export function Component({ - labels, - hasMedia, - onChange, -}: { - labels: string[] - hasMedia: boolean - onChange: (labels: string[]) => void -}) { - const pal = usePalette('default') - const {closeModal} = useModalControls() - const {isMobile} = useWebMediaQueries() - const [selected, setSelected] = useState(labels) - const {_} = useLingui() - - const toggleAdultLabel = (label: string) => { - const hadLabel = selected.includes(label) - const stripped = selected.filter(l => !ADULT_CONTENT_LABELS.includes(l)) - const final = !hadLabel ? stripped.concat([label]) : stripped - setSelected(final) - onChange(final) - } - - const removeAdultLabel = () => { - const final = selected.filter(l => !ADULT_CONTENT_LABELS.includes(l)) - setSelected(final) - onChange(final) - } - - const hasAdultSelection = - selected.includes('sexual') || - selected.includes('nudity') || - selected.includes('porn') - return ( - <View testID="selfLabelModal" style={[pal.view, styles.container]}> - <View style={styles.titleSection}> - <Text type="title-lg" style={[pal.text, styles.title]}> - <Trans>Add a content warning</Trans> - </Text> - </View> - - <ScrollView> - <View - style={[ - styles.section, - pal.border, - {borderBottomWidth: 1, paddingHorizontal: isMobile ? 20 : 0}, - ]}> - <View - style={{ - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingBottom: 8, - }}> - <Text type="title" style={pal.text}> - <Trans>Adult Content</Trans> - </Text> - {hasAdultSelection ? ( - <Button - type="default-light" - onPress={removeAdultLabel} - style={{paddingTop: 0, paddingBottom: 0, paddingRight: 0}}> - <Text type="md" style={pal.link}> - <Trans>Remove</Trans> - </Text> - </Button> - ) : null} - </View> - {hasMedia ? ( - <> - <View style={s.flexRow}> - <SelectableBtn - testID="sexualLabelBtn" - selected={selected.includes('sexual')} - left - label={_(msg`Suggestive`)} - onSelect={() => toggleAdultLabel('sexual')} - accessibilityHint="" - style={s.flex1} - /> - <SelectableBtn - testID="nudityLabelBtn" - selected={selected.includes('nudity')} - label={_(msg`Nudity`)} - onSelect={() => toggleAdultLabel('nudity')} - accessibilityHint="" - style={s.flex1} - /> - <SelectableBtn - testID="pornLabelBtn" - selected={selected.includes('porn')} - label={_(msg`Porn`)} - right - onSelect={() => toggleAdultLabel('porn')} - accessibilityHint="" - style={s.flex1} - /> - </View> - - <Text style={[pal.text, styles.adultExplainer]}> - {selected.includes('sexual') ? ( - <Trans>Pictures meant for adults.</Trans> - ) : selected.includes('nudity') ? ( - <Trans>Artistic or non-erotic nudity.</Trans> - ) : selected.includes('porn') ? ( - <Trans>Sexual activity or erotic nudity.</Trans> - ) : ( - <Trans>If none are selected, suitable for all ages.</Trans> - )} - </Text> - </> - ) : ( - <View> - <Text style={[pal.textLight]}> - <Trans> - <Text type="md-bold" style={[pal.textLight]}> - Not Applicable. - </Text>{' '} - This warning is only available for posts with media attached. - </Trans> - </Text> - </View> - )} - </View> - </ScrollView> - - <View style={[styles.btnContainer, pal.borderDark]}> - <TouchableOpacity - testID="confirmBtn" - onPress={() => { - closeModal() - }} - style={styles.btn} - accessibilityRole="button" - accessibilityLabel={_(msg`Confirm`)} - accessibilityHint=""> - <Text style={[s.white, s.bold, s.f18]}> - <Trans context="action">Done</Trans> - </Text> - </TouchableOpacity> - </View> - </View> - ) -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - paddingBottom: isWeb ? 0 : 40, - }, - titleSection: { - paddingTop: isWeb ? 0 : 4, - paddingBottom: isWeb ? 14 : 10, - }, - title: { - textAlign: 'center', - fontWeight: '600', - marginBottom: 5, - }, - description: { - textAlign: 'center', - paddingHorizontal: 32, - }, - section: { - borderTopWidth: 1, - paddingVertical: 20, - }, - adultExplainer: { - paddingLeft: 5, - paddingTop: 10, - }, - btn: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - borderRadius: 32, - padding: 14, - backgroundColor: colors.blue3, - }, - btnContainer: { - paddingTop: 20, - paddingHorizontal: 20, - }, -}) |