about summary refs log tree commit diff
path: root/src/components/dialogs
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2024-08-21 21:20:45 -0500
committerGitHub <noreply@github.com>2024-08-21 19:20:45 -0700
commit6616a6467ec53aa71e5f823c2d8c46dc01442703 (patch)
tree5e49d6916bc9b9fc71a475cf0d02f169c744bf59 /src/components/dialogs
parent56ab5e177fa2b24d0e5d9d969aa37532b96128da (diff)
downloadvoidsky-6616a6467ec53aa71e5f823c2d8c46dc01442703.tar.zst
Detached QPs and hidden replies (#4878)
Co-authored-by: Hailey <me@haileyok.com>
Diffstat (limited to 'src/components/dialogs')
-rw-r--r--src/components/dialogs/PostInteractionSettingsDialog.tsx538
-rw-r--r--src/components/dialogs/ThreadgateEditor.tsx217
2 files changed, 538 insertions, 217 deletions
diff --git a/src/components/dialogs/PostInteractionSettingsDialog.tsx b/src/components/dialogs/PostInteractionSettingsDialog.tsx
new file mode 100644
index 000000000..a326602b7
--- /dev/null
+++ b/src/components/dialogs/PostInteractionSettingsDialog.tsx
@@ -0,0 +1,538 @@
+import React from 'react'
+import {StyleProp, View, ViewStyle} from 'react-native'
+import {AppBskyFeedDefs, AppBskyFeedPostgate, AtUri} from '@atproto/api'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {useQueryClient} from '@tanstack/react-query'
+import isEqual from 'lodash.isequal'
+
+import {logger} from '#/logger'
+import {STALE} from '#/state/queries'
+import {useMyListsQuery} from '#/state/queries/my-lists'
+import {
+  createPostgateQueryKey,
+  getPostgateRecord,
+  usePostgateQuery,
+  useWritePostgateMutation,
+} from '#/state/queries/postgate'
+import {
+  createPostgateRecord,
+  embeddingRules,
+} from '#/state/queries/postgate/util'
+import {
+  createThreadgateViewQueryKey,
+  getThreadgateView,
+  ThreadgateAllowUISetting,
+  threadgateViewToAllowUISetting,
+  useSetThreadgateAllowMutation,
+  useThreadgateViewQuery,
+} from '#/state/queries/threadgate'
+import {useAgent, useSession} from '#/state/session'
+import * as Toast from '#/view/com/util/Toast'
+import {atoms as a, useTheme} from '#/alf'
+import {Button, ButtonIcon, ButtonText} from '#/components/Button'
+import * as Dialog from '#/components/Dialog'
+import {Divider} from '#/components/Divider'
+import * as Toggle from '#/components/forms/Toggle'
+import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
+import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
+import {Loader} from '#/components/Loader'
+import {Text} from '#/components/Typography'
+
+export type PostInteractionSettingsFormProps = {
+  onSave: () => void
+  isSaving?: boolean
+
+  postgate: AppBskyFeedPostgate.Record
+  onChangePostgate: (v: AppBskyFeedPostgate.Record) => void
+
+  threadgateAllowUISettings: ThreadgateAllowUISetting[]
+  onChangeThreadgateAllowUISettings: (v: ThreadgateAllowUISetting[]) => void
+
+  replySettingsDisabled?: boolean
+}
+
+export function PostInteractionSettingsControlledDialog({
+  control,
+  ...rest
+}: PostInteractionSettingsFormProps & {
+  control: Dialog.DialogControlProps
+}) {
+  const {_} = useLingui()
+  return (
+    <Dialog.Outer control={control}>
+      <Dialog.Handle />
+      <Dialog.ScrollableInner
+        label={_(msg`Edit post interaction settings`)}
+        style={[{maxWidth: 500}, a.w_full]}>
+        <PostInteractionSettingsForm {...rest} />
+        <Dialog.Close />
+      </Dialog.ScrollableInner>
+    </Dialog.Outer>
+  )
+}
+
+export type PostInteractionSettingsDialogProps = {
+  control: Dialog.DialogControlProps
+  /**
+   * URI of the post to edit the interaction settings for. Could be a root post
+   * or could be a reply.
+   */
+  postUri: string
+  /**
+   * The URI of the root post in the thread. Used to determine if the viewer
+   * owns the threadgate record and can therefore edit it.
+   */
+  rootPostUri: string
+  /**
+   * Optional initial {@link AppBskyFeedDefs.ThreadgateView} to use if we
+   * happen to have one before opening the settings dialog.
+   */
+  initialThreadgateView?: AppBskyFeedDefs.ThreadgateView
+}
+
+export function PostInteractionSettingsDialog(
+  props: PostInteractionSettingsDialogProps,
+) {
+  return (
+    <Dialog.Outer control={props.control}>
+      <Dialog.Handle />
+      <PostInteractionSettingsDialogControlledInner {...props} />
+    </Dialog.Outer>
+  )
+}
+
+export function PostInteractionSettingsDialogControlledInner(
+  props: PostInteractionSettingsDialogProps,
+) {
+  const {_} = useLingui()
+  const {currentAccount} = useSession()
+  const [isSaving, setIsSaving] = React.useState(false)
+
+  const {data: threadgateViewLoaded, isLoading: isLoadingThreadgate} =
+    useThreadgateViewQuery({postUri: props.rootPostUri})
+  const {data: postgate, isLoading: isLoadingPostgate} = usePostgateQuery({
+    postUri: props.postUri,
+  })
+
+  const {mutateAsync: writePostgateRecord} = useWritePostgateMutation()
+  const {mutateAsync: setThreadgateAllow} = useSetThreadgateAllowMutation()
+
+  const [editedPostgate, setEditedPostgate] =
+    React.useState<AppBskyFeedPostgate.Record>()
+  const [editedAllowUISettings, setEditedAllowUISettings] =
+    React.useState<ThreadgateAllowUISetting[]>()
+
+  const isLoading = isLoadingThreadgate || isLoadingPostgate
+  const threadgateView = threadgateViewLoaded || props.initialThreadgateView
+  const isThreadgateOwnedByViewer = React.useMemo(() => {
+    return currentAccount?.did === new AtUri(props.rootPostUri).host
+  }, [props.rootPostUri, currentAccount?.did])
+
+  const postgateValue = React.useMemo(() => {
+    return (
+      editedPostgate || postgate || createPostgateRecord({post: props.postUri})
+    )
+  }, [postgate, editedPostgate, props.postUri])
+  const allowUIValue = React.useMemo(() => {
+    return (
+      editedAllowUISettings || threadgateViewToAllowUISetting(threadgateView)
+    )
+  }, [threadgateView, editedAllowUISettings])
+
+  const onSave = React.useCallback(async () => {
+    if (!editedPostgate && !editedAllowUISettings) {
+      props.control.close()
+      return
+    }
+
+    setIsSaving(true)
+
+    try {
+      const requests = []
+
+      if (editedPostgate) {
+        requests.push(
+          writePostgateRecord({
+            postUri: props.postUri,
+            postgate: editedPostgate,
+          }),
+        )
+      }
+
+      if (editedAllowUISettings && isThreadgateOwnedByViewer) {
+        requests.push(
+          setThreadgateAllow({
+            postUri: props.rootPostUri,
+            allow: editedAllowUISettings,
+          }),
+        )
+      }
+
+      await Promise.all(requests)
+
+      props.control.close()
+    } catch (e: any) {
+      logger.error(`Failed to save post interaction settings`, {
+        context: 'PostInteractionSettingsDialogControlledInner',
+        safeMessage: e.message,
+      })
+      Toast.show(
+        _(
+          msg`There was an issue. Please check your internet connection and try again.`,
+        ),
+        'xmark',
+      )
+    } finally {
+      setIsSaving(false)
+    }
+  }, [
+    _,
+    props.postUri,
+    props.rootPostUri,
+    props.control,
+    editedPostgate,
+    editedAllowUISettings,
+    setIsSaving,
+    writePostgateRecord,
+    setThreadgateAllow,
+    isThreadgateOwnedByViewer,
+  ])
+
+  return (
+    <Dialog.ScrollableInner
+      label={_(msg`Edit post interaction settings`)}
+      style={[{maxWidth: 500}, a.w_full]}>
+      {isLoading ? (
+        <Loader size="xl" />
+      ) : (
+        <PostInteractionSettingsForm
+          replySettingsDisabled={!isThreadgateOwnedByViewer}
+          isSaving={isSaving}
+          onSave={onSave}
+          postgate={postgateValue}
+          onChangePostgate={setEditedPostgate}
+          threadgateAllowUISettings={allowUIValue}
+          onChangeThreadgateAllowUISettings={setEditedAllowUISettings}
+        />
+      )}
+    </Dialog.ScrollableInner>
+  )
+}
+
+export function PostInteractionSettingsForm({
+  onSave,
+  isSaving,
+  postgate,
+  onChangePostgate,
+  threadgateAllowUISettings,
+  onChangeThreadgateAllowUISettings,
+  replySettingsDisabled,
+}: PostInteractionSettingsFormProps) {
+  const t = useTheme()
+  const {_} = useLingui()
+  const control = Dialog.useDialogContext()
+  const {data: lists} = useMyListsQuery('curate')
+  const [quotesEnabled, setQuotesEnabled] = React.useState(
+    !(
+      postgate.embeddingRules &&
+      postgate.embeddingRules.find(
+        v => v.$type === embeddingRules.disableRule.$type,
+      )
+    ),
+  )
+
+  const onPressAudience = (setting: ThreadgateAllowUISetting) => {
+    // remove boolean values
+    let newSelected: ThreadgateAllowUISetting[] =
+      threadgateAllowUISettings.filter(
+        v => v.type !== 'nobody' && v.type !== 'everybody',
+      )
+    // toggle
+    const i = newSelected.findIndex(v => isEqual(v, setting))
+    if (i === -1) {
+      newSelected.push(setting)
+    } else {
+      newSelected.splice(i, 1)
+    }
+
+    onChangeThreadgateAllowUISettings(newSelected)
+  }
+
+  const onChangeQuotesEnabled = React.useCallback(
+    (enabled: boolean) => {
+      setQuotesEnabled(enabled)
+      onChangePostgate(
+        createPostgateRecord({
+          ...postgate,
+          embeddingRules: enabled ? [] : [embeddingRules.disableRule],
+        }),
+      )
+    },
+    [setQuotesEnabled, postgate, onChangePostgate],
+  )
+
+  const noOneCanReply = !!threadgateAllowUISettings.find(
+    v => v.type === 'nobody',
+  )
+
+  return (
+    <View>
+      <View style={[a.flex_1, a.gap_md]}>
+        <Text style={[a.text_2xl, a.font_bold]}>
+          <Trans>Post interaction settings</Trans>
+        </Text>
+
+        <View style={[a.gap_lg]}>
+          <Text style={[a.text_md]}>
+            <Trans>Customize who can interact with this post.</Trans>
+          </Text>
+
+          <Divider />
+
+          <View style={[a.gap_sm]}>
+            <Text style={[a.font_bold, a.text_lg]}>
+              <Trans>Quote settings</Trans>
+            </Text>
+
+            <Toggle.Item
+              name="quoteposts"
+              type="checkbox"
+              label={
+                quotesEnabled
+                  ? _(msg`Click to disable quote posts of this post.`)
+                  : _(msg`Click to enable quote posts of this post.`)
+              }
+              value={quotesEnabled}
+              onChange={onChangeQuotesEnabled}
+              style={[, a.justify_between, a.pt_xs]}>
+              <Text style={[t.atoms.text_contrast_medium]}>
+                {quotesEnabled ? (
+                  <Trans>Quote posts enabled</Trans>
+                ) : (
+                  <Trans>Quote posts disabled</Trans>
+                )}
+              </Text>
+              <Toggle.Switch />
+            </Toggle.Item>
+          </View>
+
+          <Divider />
+
+          {replySettingsDisabled && (
+            <View
+              style={[
+                a.px_md,
+                a.py_sm,
+                a.rounded_sm,
+                a.flex_row,
+                a.align_center,
+                a.gap_sm,
+                t.atoms.bg_contrast_25,
+              ]}>
+              <CircleInfo fill={t.atoms.text_contrast_low.color} />
+              <Text
+                style={[
+                  a.flex_1,
+                  a.leading_snug,
+                  t.atoms.text_contrast_medium,
+                ]}>
+                <Trans>
+                  Reply settings are chosen by the author of the thread
+                </Trans>
+              </Text>
+            </View>
+          )}
+
+          <View
+            style={[
+              a.gap_sm,
+              {
+                opacity: replySettingsDisabled ? 0.3 : 1,
+              },
+            ]}>
+            <Text style={[a.font_bold, a.text_lg]}>
+              <Trans>Reply settings</Trans>
+            </Text>
+
+            <Text style={[a.pt_sm, t.atoms.text_contrast_medium]}>
+              <Trans>Allow replies from:</Trans>
+            </Text>
+
+            <View style={[a.flex_row, a.gap_sm]}>
+              <Selectable
+                label={_(msg`Everybody`)}
+                isSelected={
+                  !!threadgateAllowUISettings.find(v => v.type === 'everybody')
+                }
+                onPress={() =>
+                  onChangeThreadgateAllowUISettings([{type: 'everybody'}])
+                }
+                style={{flex: 1}}
+                disabled={replySettingsDisabled}
+              />
+              <Selectable
+                label={_(msg`Nobody`)}
+                isSelected={noOneCanReply}
+                onPress={() =>
+                  onChangeThreadgateAllowUISettings([{type: 'nobody'}])
+                }
+                style={{flex: 1}}
+                disabled={replySettingsDisabled}
+              />
+            </View>
+
+            {!noOneCanReply && (
+              <>
+                <Text style={[a.pt_sm, t.atoms.text_contrast_medium]}>
+                  <Trans>Or combine these options:</Trans>
+                </Text>
+
+                <View style={[a.gap_sm]}>
+                  <Selectable
+                    label={_(msg`Mentioned users`)}
+                    isSelected={
+                      !!threadgateAllowUISettings.find(
+                        v => v.type === 'mention',
+                      )
+                    }
+                    onPress={() => onPressAudience({type: 'mention'})}
+                    disabled={replySettingsDisabled}
+                  />
+                  <Selectable
+                    label={_(msg`Followed users`)}
+                    isSelected={
+                      !!threadgateAllowUISettings.find(
+                        v => v.type === 'following',
+                      )
+                    }
+                    onPress={() => onPressAudience({type: 'following'})}
+                    disabled={replySettingsDisabled}
+                  />
+                  {lists && lists.length > 0
+                    ? lists.map(list => (
+                        <Selectable
+                          key={list.uri}
+                          label={_(msg`Users in "${list.name}"`)}
+                          isSelected={
+                            !!threadgateAllowUISettings.find(
+                              v => v.type === 'list' && v.list === list.uri,
+                            )
+                          }
+                          onPress={() =>
+                            onPressAudience({type: 'list', list: list.uri})
+                          }
+                          disabled={replySettingsDisabled}
+                        />
+                      ))
+                    : // No loading states to avoid jumps for the common case (no lists)
+                      null}
+                </View>
+              </>
+            )}
+          </View>
+        </View>
+      </View>
+
+      <Button
+        label={_(msg`Save`)}
+        onPress={onSave}
+        onAccessibilityEscape={control.close}
+        color="primary"
+        size="medium"
+        variant="solid"
+        style={a.mt_xl}>
+        <ButtonText>{_(msg`Save`)}</ButtonText>
+        {isSaving && <ButtonIcon icon={Loader} position="right" />}
+      </Button>
+    </View>
+  )
+}
+
+function Selectable({
+  label,
+  isSelected,
+  onPress,
+  style,
+  disabled,
+}: {
+  label: string
+  isSelected: boolean
+  onPress: () => void
+  style?: StyleProp<ViewStyle>
+  disabled?: boolean
+}) {
+  const t = useTheme()
+  return (
+    <Button
+      disabled={disabled}
+      onPress={onPress}
+      label={label}
+      accessibilityRole="checkbox"
+      aria-checked={isSelected}
+      accessibilityState={{
+        checked: isSelected,
+      }}
+      style={a.flex_1}>
+      {({hovered, focused}) => (
+        <View
+          style={[
+            a.flex_1,
+            a.flex_row,
+            a.align_center,
+            a.justify_between,
+            a.rounded_sm,
+            a.p_md,
+            {height: 40}, // for consistency with checkmark icon visible or not
+            t.atoms.bg_contrast_50,
+            (hovered || focused) && t.atoms.bg_contrast_100,
+            isSelected && {
+              backgroundColor: t.palette.primary_100,
+            },
+            style,
+          ]}>
+          <Text style={[a.text_sm, isSelected && a.font_semibold]}>
+            {label}
+          </Text>
+          {isSelected ? (
+            <Check size="sm" fill={t.palette.primary_500} />
+          ) : (
+            <View />
+          )}
+        </View>
+      )}
+    </Button>
+  )
+}
+
+export function usePrefetchPostInteractionSettings({
+  postUri,
+  rootPostUri,
+}: {
+  postUri: string
+  rootPostUri: string
+}) {
+  const queryClient = useQueryClient()
+  const agent = useAgent()
+
+  return React.useCallback(async () => {
+    try {
+      await Promise.all([
+        queryClient.prefetchQuery({
+          queryKey: createPostgateQueryKey(postUri),
+          queryFn: () => getPostgateRecord({agent, postUri}),
+          staleTime: STALE.SECONDS.THIRTY,
+        }),
+        queryClient.prefetchQuery({
+          queryKey: createThreadgateViewQueryKey(rootPostUri),
+          queryFn: () => getThreadgateView({agent, postUri: rootPostUri}),
+          staleTime: STALE.SECONDS.THIRTY,
+        }),
+      ])
+    } catch (e: any) {
+      logger.error(`Failed to prefetch post interaction settings`, {
+        safeMessage: e.message,
+      })
+    }
+  }, [queryClient, agent, postUri, rootPostUri])
+}
diff --git a/src/components/dialogs/ThreadgateEditor.tsx b/src/components/dialogs/ThreadgateEditor.tsx
deleted file mode 100644
index 90483b3ad..000000000
--- a/src/components/dialogs/ThreadgateEditor.tsx
+++ /dev/null
@@ -1,217 +0,0 @@
-import React from 'react'
-import {StyleProp, View, ViewStyle} from 'react-native'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
-import isEqual from 'lodash.isequal'
-
-import {useMyListsQuery} from '#/state/queries/my-lists'
-import {ThreadgateSetting} from '#/state/queries/threadgate'
-import {atoms as a, useTheme} from '#/alf'
-import {Button, ButtonText} from '#/components/Button'
-import * as Dialog from '#/components/Dialog'
-import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
-import {Text} from '#/components/Typography'
-
-interface ThreadgateEditorDialogProps {
-  control: Dialog.DialogControlProps
-  threadgate: ThreadgateSetting[]
-  onChange?: (v: ThreadgateSetting[]) => void
-  onConfirm?: (v: ThreadgateSetting[]) => void
-}
-
-export function ThreadgateEditorDialog({
-  control,
-  threadgate,
-  onChange,
-  onConfirm,
-}: ThreadgateEditorDialogProps) {
-  return (
-    <Dialog.Outer control={control}>
-      <Dialog.Handle />
-      <DialogContent
-        seedThreadgate={threadgate}
-        onChange={onChange}
-        onConfirm={onConfirm}
-      />
-    </Dialog.Outer>
-  )
-}
-
-function DialogContent({
-  seedThreadgate,
-  onChange,
-  onConfirm,
-}: {
-  seedThreadgate: ThreadgateSetting[]
-  onChange?: (v: ThreadgateSetting[]) => void
-  onConfirm?: (v: ThreadgateSetting[]) => void
-}) {
-  const {_} = useLingui()
-  const control = Dialog.useDialogContext()
-  const {data: lists} = useMyListsQuery('curate')
-  const [draft, setDraft] = React.useState(seedThreadgate)
-
-  const [prevSeedThreadgate, setPrevSeedThreadgate] =
-    React.useState(seedThreadgate)
-  if (seedThreadgate !== prevSeedThreadgate) {
-    // New data flowed from above (e.g. due to update coming through).
-    setPrevSeedThreadgate(seedThreadgate)
-    setDraft(seedThreadgate) // Reset draft.
-  }
-
-  function updateThreadgate(nextThreadgate: ThreadgateSetting[]) {
-    setDraft(nextThreadgate)
-    onChange?.(nextThreadgate)
-  }
-
-  const onPressEverybody = () => {
-    updateThreadgate([])
-  }
-
-  const onPressNobody = () => {
-    updateThreadgate([{type: 'nobody'}])
-  }
-
-  const onPressAudience = (setting: ThreadgateSetting) => {
-    // remove nobody
-    let newSelected: ThreadgateSetting[] = draft.filter(
-      v => v.type !== 'nobody',
-    )
-    // toggle
-    const i = newSelected.findIndex(v => isEqual(v, setting))
-    if (i === -1) {
-      newSelected.push(setting)
-    } else {
-      newSelected.splice(i, 1)
-    }
-    updateThreadgate(newSelected)
-  }
-
-  const doneLabel = onConfirm ? _(msg`Save`) : _(msg`Done`)
-  return (
-    <Dialog.ScrollableInner
-      label={_(msg`Choose who can reply`)}
-      style={[{maxWidth: 500}, a.w_full]}>
-      <View style={[a.flex_1, a.gap_md]}>
-        <Text style={[a.text_2xl, a.font_bold]}>
-          <Trans>Choose who can reply</Trans>
-        </Text>
-        <Text style={a.mt_xs}>
-          <Trans>Either choose "Everybody" or "Nobody"</Trans>
-        </Text>
-        <View style={[a.flex_row, a.gap_sm]}>
-          <Selectable
-            label={_(msg`Everybody`)}
-            isSelected={draft.length === 0}
-            onPress={onPressEverybody}
-            style={{flex: 1}}
-          />
-          <Selectable
-            label={_(msg`Nobody`)}
-            isSelected={!!draft.find(v => v.type === 'nobody')}
-            onPress={onPressNobody}
-            style={{flex: 1}}
-          />
-        </View>
-        <Text style={a.mt_md}>
-          <Trans>Or combine these options:</Trans>
-        </Text>
-        <View style={[a.gap_sm]}>
-          <Selectable
-            label={_(msg`Mentioned users`)}
-            isSelected={!!draft.find(v => v.type === 'mention')}
-            onPress={() => onPressAudience({type: 'mention'})}
-          />
-          <Selectable
-            label={_(msg`Followed users`)}
-            isSelected={!!draft.find(v => v.type === 'following')}
-            onPress={() => onPressAudience({type: 'following'})}
-          />
-          {lists && lists.length > 0
-            ? lists.map(list => (
-                <Selectable
-                  key={list.uri}
-                  label={_(msg`Users in "${list.name}"`)}
-                  isSelected={
-                    !!draft.find(v => v.type === 'list' && v.list === list.uri)
-                  }
-                  onPress={() =>
-                    onPressAudience({type: 'list', list: list.uri})
-                  }
-                />
-              ))
-            : // No loading states to avoid jumps for the common case (no lists)
-              null}
-        </View>
-      </View>
-      <Button
-        label={doneLabel}
-        onPress={() => {
-          control.close()
-          onConfirm?.(draft)
-        }}
-        onAccessibilityEscape={control.close}
-        color="primary"
-        size="medium"
-        variant="solid"
-        style={a.mt_xl}>
-        <ButtonText>{doneLabel}</ButtonText>
-      </Button>
-      <Dialog.Close />
-    </Dialog.ScrollableInner>
-  )
-}
-
-function Selectable({
-  label,
-  isSelected,
-  onPress,
-  style,
-}: {
-  label: string
-  isSelected: boolean
-  onPress: () => void
-  style?: StyleProp<ViewStyle>
-}) {
-  const t = useTheme()
-  return (
-    <Button
-      onPress={onPress}
-      label={label}
-      accessibilityHint="Select this option"
-      accessibilityRole="checkbox"
-      aria-checked={isSelected}
-      accessibilityState={{
-        checked: isSelected,
-      }}
-      style={a.flex_1}>
-      {({hovered, focused}) => (
-        <View
-          style={[
-            a.flex_1,
-            a.flex_row,
-            a.align_center,
-            a.justify_between,
-            a.rounded_sm,
-            a.p_md,
-            {height: 40}, // for consistency with checkmark icon visible or not
-            t.atoms.bg_contrast_50,
-            (hovered || focused) && t.atoms.bg_contrast_100,
-            isSelected && {
-              backgroundColor: t.palette.primary_100,
-            },
-            style,
-          ]}>
-          <Text style={[a.text_sm, isSelected && a.font_semibold]}>
-            {label}
-          </Text>
-          {isSelected ? (
-            <Check size="sm" fill={t.palette.primary_500} />
-          ) : (
-            <View />
-          )}
-        </View>
-      )}
-    </Button>
-  )
-}