about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMinseo Lee <itoupluk427@gmail.com>2024-02-29 13:05:45 +0900
committerGitHub <noreply@github.com>2024-02-29 13:05:45 +0900
commit200c4c1d379e591e82d6d1bd065a443f6abc03f5 (patch)
treedb7257f0178b2d9514642a7faf3e003d60d2b418 /src
parenta1127bfcfc7ad080a5bd6210c6561788f1643db8 (diff)
parenta35976cdc9b6467ad8b6e0c4ff46ba684fee9064 (diff)
downloadvoidsky-200c4c1d379e591e82d6d1bd065a443f6abc03f5.tar.zst
Merge branch 'bluesky-social:main' into patch-3
Diffstat (limited to 'src')
-rw-r--r--src/components/Dialog/context.ts13
-rw-r--r--src/components/Dialog/index.tsx8
-rw-r--r--src/components/Dialog/index.web.tsx8
-rw-r--r--src/components/Dialog/types.ts19
-rw-r--r--src/lib/__tests__/moderatePost_wrapped.test.ts89
-rw-r--r--src/lib/moderatePost_wrapped.ts9
-rw-r--r--src/state/dialogs/index.tsx28
-rw-r--r--src/state/util.ts8
8 files changed, 162 insertions, 20 deletions
diff --git a/src/components/Dialog/context.ts b/src/components/Dialog/context.ts
index f0c7c983a..eb717d8e2 100644
--- a/src/components/Dialog/context.ts
+++ b/src/components/Dialog/context.ts
@@ -3,7 +3,7 @@ import React from 'react'
 import {useDialogStateContext} from '#/state/dialogs'
 import {
   DialogContextProps,
-  DialogControlProps,
+  DialogControlRefProps,
   DialogOuterProps,
 } from '#/components/Dialog/types'
 
@@ -17,7 +17,7 @@ export function useDialogContext() {
 
 export function useDialogControl(): DialogOuterProps['control'] {
   const id = React.useId()
-  const control = React.useRef<DialogControlProps>({
+  const control = React.useRef<DialogControlRefProps>({
     open: () => {},
     close: () => {},
   })
@@ -32,8 +32,13 @@ export function useDialogControl(): DialogOuterProps['control'] {
   }, [id, activeDialogs])
 
   return {
+    id,
     ref: control,
-    open: () => control.current.open(),
-    close: cb => control.current.close(cb),
+    open: () => {
+      control.current.open()
+    },
+    close: cb => {
+      control.current.close(cb)
+    },
   }
 }
diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx
index 5c0350274..6dfc24f3b 100644
--- a/src/components/Dialog/index.tsx
+++ b/src/components/Dialog/index.tsx
@@ -12,6 +12,7 @@ import {useTheme, atoms as a, flatten} from '#/alf'
 import {Portal} from '#/components/Portal'
 import {createInput} from '#/components/forms/TextField'
 import {logger} from '#/logger'
+import {useDialogStateContext} from '#/state/dialogs'
 
 import {
   DialogOuterProps,
@@ -37,6 +38,7 @@ export function Outer({
   const hasSnapPoints = !!sheetOptions.snapPoints
   const insets = useSafeAreaInsets()
   const closeCallback = React.useRef<() => void>()
+  const {openDialogs} = useDialogStateContext()
 
   /*
    * Used to manage open/closed, but index is otherwise handled internally by `BottomSheet`
@@ -50,10 +52,11 @@ export function Outer({
 
   const open = React.useCallback<DialogControlProps['open']>(
     ({index} = {}) => {
+      openDialogs.current.add(control.id)
       // can be set to any index of `snapPoints`, but `0` is the first i.e. "open"
       setOpenIndex(index || 0)
     },
-    [setOpenIndex],
+    [setOpenIndex, openDialogs, control.id],
   )
 
   const close = React.useCallback<DialogControlProps['close']>(cb => {
@@ -85,11 +88,12 @@ export function Outer({
           closeCallback.current = undefined
         }
 
+        openDialogs.current.delete(control.id)
         onClose?.()
         setOpenIndex(-1)
       }
     },
-    [onClose, setOpenIndex],
+    [onClose, setOpenIndex, openDialogs, control.id],
   )
 
   const context = React.useMemo(() => ({close}), [close])
diff --git a/src/components/Dialog/index.web.tsx b/src/components/Dialog/index.web.tsx
index ff05fed91..32163e735 100644
--- a/src/components/Dialog/index.web.tsx
+++ b/src/components/Dialog/index.web.tsx
@@ -12,6 +12,7 @@ import {DialogOuterProps, DialogInnerProps} from '#/components/Dialog/types'
 import {Context} from '#/components/Dialog/context'
 import {Button, ButtonIcon} from '#/components/Button'
 import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
+import {useDialogStateContext} from '#/state/dialogs'
 
 export {useDialogControl, useDialogContext} from '#/components/Dialog/context'
 export * from '#/components/Dialog/types'
@@ -29,18 +30,21 @@ export function Outer({
   const {gtMobile} = useBreakpoints()
   const [isOpen, setIsOpen] = React.useState(false)
   const [isVisible, setIsVisible] = React.useState(true)
+  const {openDialogs} = useDialogStateContext()
 
   const open = React.useCallback(() => {
     setIsOpen(true)
-  }, [setIsOpen])
+    openDialogs.current.add(control.id)
+  }, [setIsOpen, openDialogs, control.id])
 
   const close = React.useCallback(async () => {
     setIsVisible(false)
     await new Promise(resolve => setTimeout(resolve, 150))
     setIsOpen(false)
     setIsVisible(true)
+    openDialogs.current.delete(control.id)
     onClose?.()
-  }, [onClose, setIsOpen])
+  }, [onClose, setIsOpen, openDialogs, control.id])
 
   useImperativeHandle(
     control.ref,
diff --git a/src/components/Dialog/types.ts b/src/components/Dialog/types.ts
index 161c03734..78dfedf5a 100644
--- a/src/components/Dialog/types.ts
+++ b/src/components/Dialog/types.ts
@@ -6,11 +6,24 @@ import {ViewStyleProp} from '#/alf'
 
 type A11yProps = Required<AccessibilityProps>
 
-export type DialogControlProps = {
+/**
+ * Mutated by useImperativeHandle to provide a public API for controlling the
+ * dialog. The methods here will actually become the handlers defined within
+ * the `Dialog.Outer` component.
+ */
+export type DialogControlRefProps = {
   open: (options?: DialogControlOpenOptions) => void
   close: (callback?: () => void) => void
 }
 
+/**
+ * The return type of the useDialogControl hook.
+ */
+export type DialogControlProps = DialogControlRefProps & {
+  id: string
+  ref: React.RefObject<DialogControlRefProps>
+}
+
 export type DialogContextProps = {
   close: DialogControlProps['close']
 }
@@ -26,9 +39,7 @@ export type DialogControlOpenOptions = {
 }
 
 export type DialogOuterProps = {
-  control: {
-    ref: React.RefObject<DialogControlProps>
-  } & DialogControlProps
+  control: DialogControlProps
   onClose?: () => void
   nativeOptions?: {
     sheet?: Omit<BottomSheetProps, 'children'>
diff --git a/src/lib/__tests__/moderatePost_wrapped.test.ts b/src/lib/__tests__/moderatePost_wrapped.test.ts
index c35c1ef77..45566281a 100644
--- a/src/lib/__tests__/moderatePost_wrapped.test.ts
+++ b/src/lib/__tests__/moderatePost_wrapped.test.ts
@@ -16,6 +16,7 @@ describe(`hasMutedWord`, () => {
         text: rt.text,
         facets: rt.facets,
         outlineTags: ['outlineTag'],
+        isOwnPost: false,
       })
 
       expect(match).toBe(true)
@@ -32,6 +33,7 @@ describe(`hasMutedWord`, () => {
         text: rt.text,
         facets: rt.facets,
         outlineTags: ['outlineTag'],
+        isOwnPost: false,
       })
 
       expect(match).toBe(true)
@@ -48,6 +50,7 @@ describe(`hasMutedWord`, () => {
         text: rt.text,
         facets: rt.facets,
         outlineTags: ['outlineTag'],
+        isOwnPost: false,
       })
 
       expect(match).toBe(true)
@@ -64,6 +67,7 @@ describe(`hasMutedWord`, () => {
         text: rt.text,
         facets: rt.facets,
         outlineTags: [],
+        isOwnPost: false,
       })
 
       expect(match).toBe(false)
@@ -85,6 +89,7 @@ describe(`hasMutedWord`, () => {
         text: rt.text,
         facets: rt.facets,
         outlineTags: [],
+        isOwnPost: false,
       })
 
       expect(match).toBe(true)
@@ -101,6 +106,7 @@ describe(`hasMutedWord`, () => {
         text: rt.text,
         facets: rt.facets,
         outlineTags: [],
+        isOwnPost: false,
       })
 
       expect(match).toBe(false)
@@ -117,6 +123,7 @@ describe(`hasMutedWord`, () => {
         text: rt.text,
         facets: rt.facets,
         outlineTags: [],
+        isOwnPost: false,
       })
 
       expect(match).toBe(true)
@@ -135,6 +142,7 @@ describe(`hasMutedWord`, () => {
         text: rt.text,
         facets: rt.facets,
         outlineTags: [],
+        isOwnPost: false,
       })
 
       expect(match).toBe(true)
@@ -151,6 +159,7 @@ describe(`hasMutedWord`, () => {
         text: rt.text,
         facets: rt.facets,
         outlineTags: [],
+        isOwnPost: false,
       })
 
       expect(match).toBe(false)
@@ -167,6 +176,7 @@ describe(`hasMutedWord`, () => {
         text: rt.text,
         facets: rt.facets,
         outlineTags: [],
+        isOwnPost: false,
       })
 
       expect(match).toBe(true)
@@ -183,6 +193,7 @@ describe(`hasMutedWord`, () => {
         text: rt.text,
         facets: rt.facets,
         outlineTags: [],
+        isOwnPost: false,
       })
 
       expect(match).toBe(true)
@@ -202,6 +213,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -213,6 +225,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -231,6 +244,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -243,6 +257,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -261,6 +276,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -272,6 +288,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -291,6 +308,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -309,6 +327,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -320,6 +339,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -336,6 +356,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(false)
@@ -354,6 +375,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -365,6 +387,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -383,6 +406,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -394,6 +418,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -405,6 +430,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -416,6 +442,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(false)
@@ -434,6 +461,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -448,6 +476,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(false)
@@ -460,6 +489,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -471,6 +501,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(false)
@@ -489,6 +520,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -500,6 +532,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -511,6 +544,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -522,6 +556,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -540,6 +575,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -560,6 +596,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -571,6 +608,7 @@ describe(`hasMutedWord`, () => {
           text: rt.text,
           facets: rt.facets,
           outlineTags: [],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
@@ -594,10 +632,61 @@ describe(`hasMutedWord`, () => {
           facets: rt.facets,
           outlineTags: [],
           languages: ['ja'],
+          isOwnPost: false,
         })
 
         expect(match).toBe(true)
       })
     })
   })
+
+  describe(`doesn't mute own post`, () => {
+    it(`does mute if it isn't own post`, () => {
+      const rt = new RichText({
+        text: `Mute words!`,
+      })
+
+      const match = hasMutedWord({
+        mutedWords: [{value: 'words', targets: ['content']}],
+        text: rt.text,
+        facets: rt.facets,
+        outlineTags: [],
+        isOwnPost: false,
+      })
+
+      expect(match).toBe(true)
+    })
+
+    it(`doesn't mute own post when muted word is in text`, () => {
+      const rt = new RichText({
+        text: `Mute words!`,
+      })
+
+      const match = hasMutedWord({
+        mutedWords: [{value: 'words', targets: ['content']}],
+        text: rt.text,
+        facets: rt.facets,
+        outlineTags: [],
+        isOwnPost: true,
+      })
+
+      expect(match).toBe(false)
+    })
+
+    it(`doesn't mute own post when muted word is in tags`, () => {
+      const rt = new RichText({
+        text: `Mute #words!`,
+      })
+
+      const match = hasMutedWord({
+        mutedWords: [{value: 'words', targets: ['tags']}],
+        text: rt.text,
+        facets: rt.facets,
+        outlineTags: [],
+        isOwnPost: true,
+      })
+
+      expect(match).toBe(false)
+    })
+  })
 })
diff --git a/src/lib/moderatePost_wrapped.ts b/src/lib/moderatePost_wrapped.ts
index 428dbabf4..92543b42c 100644
--- a/src/lib/moderatePost_wrapped.ts
+++ b/src/lib/moderatePost_wrapped.ts
@@ -41,13 +41,17 @@ export function hasMutedWord({
   facets,
   outlineTags,
   languages,
+  isOwnPost,
 }: {
   mutedWords: AppBskyActorDefs.MutedWord[]
   text: string
   facets?: AppBskyRichtextFacet.Main[]
   outlineTags?: string[]
   languages?: string[]
+  isOwnPost: boolean
 }) {
+  if (isOwnPost) return false
+
   const exception = LANGUAGE_EXCEPTIONS.includes(languages?.[0] || '')
   const tags = ([] as string[])
     .concat(outlineTags || [])
@@ -142,6 +146,7 @@ export function moderatePost_wrapped(
 ) {
   const {hiddenPosts = [], mutedWords = [], ...options} = opts
   const moderations = moderatePost(subject, options)
+  const isOwnPost = subject.author.did === opts.userDid
 
   if (hiddenPosts.includes(subject.uri)) {
     moderations.content.filter = true
@@ -163,6 +168,7 @@ export function moderatePost_wrapped(
       facets: subject.record.facets || [],
       outlineTags: subject.record.tags || [],
       languages: subject.record.langs,
+      isOwnPost,
     })
 
     if (
@@ -178,6 +184,7 @@ export function moderatePost_wrapped(
             facets: [],
             outlineTags: [],
             languages: subject.record.langs,
+            isOwnPost,
           })
       }
     }
@@ -210,6 +217,7 @@ export function moderatePost_wrapped(
             facets: subject.embed.record.value.facets,
             outlineTags: subject.embed.record.value.tags,
             languages: subject.embed.record.value.langs,
+            isOwnPost,
           })
 
         if (AppBskyEmbedImages.isMain(subject.embed.record.value.embed)) {
@@ -222,6 +230,7 @@ export function moderatePost_wrapped(
                 facets: [],
                 outlineTags: [],
                 languages: subject.embed.record.value.langs,
+                isOwnPost,
               })
           }
         }
diff --git a/src/state/dialogs/index.tsx b/src/state/dialogs/index.tsx
index ae762bd97..9fc70c178 100644
--- a/src/state/dialogs/index.tsx
+++ b/src/state/dialogs/index.tsx
@@ -1,21 +1,32 @@
 import React from 'react'
-import {DialogControlProps} from '#/components/Dialog'
+import {DialogControlRefProps} from '#/components/Dialog'
 import {Provider as GlobalDialogsProvider} from '#/components/dialogs/Context'
 
 const DialogContext = React.createContext<{
+  /**
+   * The currently active `useDialogControl` hooks.
+   */
   activeDialogs: React.MutableRefObject<
-    Map<string, React.MutableRefObject<DialogControlProps>>
+    Map<string, React.MutableRefObject<DialogControlRefProps>>
   >
+  /**
+   * The currently open dialogs, referenced by their IDs, generated from
+   * `useId`.
+   */
+  openDialogs: React.MutableRefObject<Set<string>>
 }>({
   activeDialogs: {
     current: new Map(),
   },
+  openDialogs: {
+    current: new Set(),
+  },
 })
 
 const DialogControlContext = React.createContext<{
-  closeAllDialogs(): void
+  closeAllDialogs(): boolean
 }>({
-  closeAllDialogs: () => {},
+  closeAllDialogs: () => false,
 })
 
 export function useDialogStateContext() {
@@ -28,13 +39,18 @@ export function useDialogStateControlContext() {
 
 export function Provider({children}: React.PropsWithChildren<{}>) {
   const activeDialogs = React.useRef<
-    Map<string, React.MutableRefObject<DialogControlProps>>
+    Map<string, React.MutableRefObject<DialogControlRefProps>>
   >(new Map())
+  const openDialogs = React.useRef<Set<string>>(new Set())
+
   const closeAllDialogs = React.useCallback(() => {
     activeDialogs.current.forEach(dialog => dialog.current.close())
+    return openDialogs.current.size > 0
   }, [])
-  const context = React.useMemo(() => ({activeDialogs}), [])
+
+  const context = React.useMemo(() => ({activeDialogs, openDialogs}), [])
   const controls = React.useMemo(() => ({closeAllDialogs}), [closeAllDialogs])
+
   return (
     <DialogContext.Provider value={context}>
       <DialogControlContext.Provider value={controls}>
diff --git a/src/state/util.ts b/src/state/util.ts
index 7b49b5b46..f65d14a84 100644
--- a/src/state/util.ts
+++ b/src/state/util.ts
@@ -3,7 +3,7 @@ import {useLightboxControls} from './lightbox'
 import {useModalControls} from './modals'
 import {useComposerControls} from './shell/composer'
 import {useSetDrawerOpen} from './shell/drawer-open'
-import {useDialogStateControlContext} from 'state/dialogs'
+import {useDialogStateControlContext} from '#/state/dialogs'
 
 /**
  * returns true if something was closed
@@ -13,6 +13,7 @@ export function useCloseAnyActiveElement() {
   const {closeLightbox} = useLightboxControls()
   const {closeModal} = useModalControls()
   const {closeComposer} = useComposerControls()
+  const {closeAllDialogs} = useDialogStateControlContext()
   const setDrawerOpen = useSetDrawerOpen()
   return useCallback(() => {
     if (closeLightbox()) {
@@ -24,9 +25,12 @@ export function useCloseAnyActiveElement() {
     if (closeComposer()) {
       return true
     }
+    if (closeAllDialogs()) {
+      return true
+    }
     setDrawerOpen(false)
     return false
-  }, [closeLightbox, closeModal, closeComposer, setDrawerOpen])
+  }, [closeLightbox, closeModal, closeComposer, setDrawerOpen, closeAllDialogs])
 }
 
 /**