diff options
Diffstat (limited to 'src/view/com')
-rw-r--r-- | src/view/com/composer/labels/LabelsBtn.tsx | 288 | ||||
-rw-r--r-- | src/view/com/composer/state/composer.ts | 5 |
2 files changed, 174 insertions, 119 deletions
diff --git a/src/view/com/composer/labels/LabelsBtn.tsx b/src/view/com/composer/labels/LabelsBtn.tsx index d72366aea..a176426dc 100644 --- a/src/view/com/composer/labels/LabelsBtn.tsx +++ b/src/view/com/composer/labels/LabelsBtn.tsx @@ -1,44 +1,52 @@ import React from 'react' -import {Keyboard, LayoutAnimation, View} from 'react-native' +import {Keyboard, View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {ShieldExclamation} from '#/lib/icons' +import { + ADULT_CONTENT_LABELS, + AdultSelfLabel, + OTHER_SELF_LABELS, + OtherSelfLabel, + SelfLabel, +} from '#/lib/moderation' +import {isWeb} from '#/platform/detection' 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 * as Toggle from '#/components/forms/Toggle' 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, hasMedia, onChange, }: { - labels: string[] + labels: SelfLabel[] hasMedia: boolean - onChange: (v: string[]) => void + onChange: (v: SelfLabel[]) => void }) { const control = Dialog.useDialogControl() const t = useTheme() const {_} = useLingui() - const removeAdultLabel = () => { - const final = labels.filter(l => !ADULT_CONTENT_LABELS.includes(l)) - onChange(final) - LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) + const hasLabel = labels.length > 0 + + const updateAdultLabels = (newLabels: AdultSelfLabel[]) => { + const newLabel = newLabels[newLabels.length - 1] + const filtered = labels.filter(l => !ADULT_CONTENT_LABELS.includes(l)) + onChange([...filtered, newLabel].filter(Boolean) as SelfLabel[]) } - const hasAdultSelection = - labels.includes('sexual') || - labels.includes('nudity') || - labels.includes('porn') + const updateOtherLabels = (newLabels: OtherSelfLabel[]) => { + const newLabel = newLabels[newLabels.length - 1] + const filtered = labels.filter(l => !OTHER_SELF_LABELS.includes(l)) + onChange([...filtered, newLabel].filter(Boolean) as SelfLabel[]) + } - if (!hasMedia && hasAdultSelection) { - removeAdultLabel() + if (!hasMedia && hasLabel) { + onChange([]) } return ( @@ -64,10 +72,9 @@ export function LabelsBtn({ <Dialog.Handle /> <DialogInner labels={labels} - onChange={onChange} - hasAdultSelection={hasAdultSelection} hasMedia={hasMedia} - removeAdultLabel={removeAdultLabel} + updateAdultLabels={updateAdultLabels} + updateOtherLabels={updateOtherLabels} /> </Dialog.Outer> </> @@ -76,16 +83,14 @@ export function LabelsBtn({ function DialogInner({ labels, - onChange, - hasAdultSelection, hasMedia, - removeAdultLabel, + updateAdultLabels, + updateOtherLabels, }: { labels: string[] - onChange: (v: string[]) => void - hasAdultSelection: boolean hasMedia: boolean - removeAdultLabel: () => void + updateAdultLabels: (labels: AdultSelfLabel[]) => void + updateOtherLabels: (labels: OtherSelfLabel[]) => void }) { const {_} = useLingui() const control = Dialog.useDialogContext() @@ -95,104 +100,153 @@ function DialogInner({ <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> + <View style={[a.flex_1]}> + <View style={[a.gap_sm]}> + <Text style={[a.text_2xl, a.font_bold]}> + <Trans>Add a content warning</Trans> + </Text> + <Text style={[t.atoms.text_contrast_medium, a.leading_snug]}> + {hasMedia ? ( + <Trans> + Choose self-labels that are applicable for the media you are + posting. If none are selected, this post is suitable for all + audiences. + </Trans> + ) : ( + <Trans> + There are no self-labels that can be applied to this post. + </Trans> + )} + </Text> + </View> - <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> + <View style={[a.my_md, a.gap_lg]}> {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> + <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> + </View> + <View + style={[ + a.p_md, + a.rounded_sm, + a.border, + t.atoms.border_contrast_medium, + ]}> + <Toggle.Group + label={_(msg`Adult Content labels`)} + values={labels} + onChange={values => { + updateAdultLabels(values as AdultSelfLabel[]) + }}> + <View style={[a.gap_sm]}> + <Toggle.Item name="sexual" label={_(msg`Suggestive`)}> + <Toggle.Radio /> + <Toggle.LabelText> + <Trans>Suggestive</Trans> + </Toggle.LabelText> + </Toggle.Item> + <Toggle.Item name="nudity" label={_(msg`Nudity`)}> + <Toggle.Radio /> + <Toggle.LabelText> + <Trans>Nudity</Trans> + </Toggle.LabelText> + </Toggle.Item> + <Toggle.Item name="porn" label={_(msg`Porn`)}> + <Toggle.Radio /> + <Toggle.LabelText> + <Trans>Porn</Trans> + </Toggle.LabelText> + </Toggle.Item> + </View> + </Toggle.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>Does not contain adult content.</Trans> + )} + </Text> + </View> + </View> + <View> + <View + style={[ + a.flex_row, + a.align_center, + a.justify_between, + a.pb_sm, + ]}> + <Text style={[a.font_bold, a.text_lg]}> + <Trans>Other</Trans> + </Text> + </View> + <View + style={[ + a.p_md, + a.rounded_sm, + a.border, + t.atoms.border_contrast_medium, + ]}> + <Toggle.Group + label={_(msg`Adult Content labels`)} + values={labels} + onChange={values => { + updateOtherLabels(values as OtherSelfLabel[]) + }}> + <Toggle.Item + name="graphic-media" + label={_(msg`Graphic Media`)}> + <Toggle.Checkbox /> + <Toggle.LabelText> + <Trans>Graphic Media</Trans> + </Toggle.LabelText> + </Toggle.Item> + </Toggle.Group> + <Text style={[a.mt_sm, t.atoms.text_contrast_medium]}> + {labels.includes('graphic-media') ? ( + <Trans> + Media that may be disturbing or inappropriate for some + audiences. + </Trans> + ) : ( + <Trans> + Does not contain graphic or disturbing content. + </Trans> + )} + </Text> + </View> + </View> </> - ) : ( - <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> - )} + ) : null} </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> + <View style={[a.mt_sm]}> + <Button + label={_(msg`Done`)} + onPress={() => control.close()} + color="primary" + size={isWeb ? 'small' : 'large'} + variant="solid"> + <ButtonText> + <Trans>Done</Trans> + </ButtonText> + </Button> + </View> </Dialog.ScrollableInner> ) } diff --git a/src/view/com/composer/state/composer.ts b/src/view/com/composer/state/composer.ts index e37690342..049488f3a 100644 --- a/src/view/com/composer/state/composer.ts +++ b/src/view/com/composer/state/composer.ts @@ -1,6 +1,7 @@ import {ImagePickerAsset} from 'expo-image-picker' import {AppBskyFeedPostgate, RichText} from '@atproto/api' +import {SelfLabel} from '#/lib/moderation' import {insertMentionAt} from '#/lib/strings/mention-manip' import { isBskyPostUrl, @@ -48,7 +49,7 @@ export type EmbedDraft = { export type ComposerDraft = { richtext: RichText - labels: string[] + labels: SelfLabel[] postgate: AppBskyFeedPostgate.Record threadgate: ThreadgateAllowUISetting[] embed: EmbedDraft @@ -56,7 +57,7 @@ export type ComposerDraft = { export type ComposerAction = | {type: 'update_richtext'; richtext: RichText} - | {type: 'update_labels'; labels: string[]} + | {type: 'update_labels'; labels: SelfLabel[]} | {type: 'update_postgate'; postgate: AppBskyFeedPostgate.Record} | {type: 'update_threadgate'; threadgate: ThreadgateAllowUISetting[]} | {type: 'embed_add_images'; images: ComposerImage[]} |