about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2024-10-16 18:52:14 +0300
committerGitHub <noreply@github.com>2024-10-16 08:52:14 -0700
commit6bc00f8d714fa1113d03f57ec06caccd4ffbb9dc (patch)
tree852790dfdfcc04584a56496e8219c08414120773
parentc3d0cc55d98fb32b25cd2164cfa1c399985e7c84 (diff)
downloadvoidsky-6bc00f8d714fa1113d03f57ec06caccd4ffbb9dc.tar.zst
Composer - Self label dialog ALF rewrite (#4354)
-rw-r--r--src/components/dialogs/GifSelect.tsx2
-rw-r--r--src/components/forms/ToggleButton.tsx2
-rw-r--r--src/state/modals/index.tsx8
-rw-r--r--src/view/com/composer/Composer.tsx10
-rw-r--r--src/view/com/composer/labels/LabelsBtn.tsx225
-rw-r--r--src/view/com/composer/photos/SelectGifBtn.tsx2
-rw-r--r--src/view/com/modals/Modal.tsx4
-rw-r--r--src/view/com/modals/Modal.web.tsx3
-rw-r--r--src/view/com/modals/SelfLabel.tsx204
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,
-  },
-})