diff --git a/src/App.native.tsx b/src/App.native.tsx
index d2c20fc8e7..7ad47ac149 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -50,6 +50,7 @@ import {Provider as LoggedOutViewProvider} from '#/state/shell/logged-out'
import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide'
import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
import {Provider as StarterPackProvider} from '#/state/shell/starter-pack'
+import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies'
import {TestCtrls} from '#/view/com/testing/TestCtrls'
import {ActiveVideoProvider} from '#/view/com/util/post-embeds/ActiveVideoContext'
import * as Toast from '#/view/com/util/Toast'
@@ -181,7 +182,9 @@ function App() {
-
+
+
+
diff --git a/src/App.web.tsx b/src/App.web.tsx
index df6fbf2449..45e46cf5df 100644
--- a/src/App.web.tsx
+++ b/src/App.web.tsx
@@ -39,6 +39,7 @@ import {Provider as LoggedOutViewProvider} from '#/state/shell/logged-out'
import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide'
import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
import {Provider as StarterPackProvider} from '#/state/shell/starter-pack'
+import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies'
import {ActiveVideoProvider} from '#/view/com/util/post-embeds/ActiveVideoContext'
import * as Toast from '#/view/com/util/Toast'
import {ToastContainer} from '#/view/com/util/Toast.web'
@@ -162,7 +163,9 @@ function App() {
-
+
+
+
diff --git a/src/state/queries/threadgate/index.ts b/src/state/queries/threadgate/index.ts
index 968e7fc8ba..97c7db92a8 100644
--- a/src/state/queries/threadgate/index.ts
+++ b/src/state/queries/threadgate/index.ts
@@ -19,6 +19,7 @@ import {
threadgateViewToAllowUISetting,
} from '#/state/queries/threadgate/util'
import {useAgent} from '#/state/session'
+import {useThreadgateHiddenReplyUrisAPI} from '#/state/threadgate-hidden-replies'
export * from '#/state/queries/threadgate/types'
export * from '#/state/queries/threadgate/util'
@@ -298,6 +299,7 @@ export function useSetThreadgateAllowMutation() {
export function useToggleReplyVisibilityMutation() {
const agent = useAgent()
const queryClient = useQueryClient()
+ const hiddenReplies = useThreadgateHiddenReplyUrisAPI()
return useMutation({
mutationFn: async ({
@@ -309,6 +311,12 @@ export function useToggleReplyVisibilityMutation() {
replyUri: string
action: 'hide' | 'show'
}) => {
+ if (action === 'hide') {
+ hiddenReplies.addHiddenReplyUri(replyUri)
+ } else if (action === 'show') {
+ hiddenReplies.removeHiddenReplyUri(replyUri)
+ }
+
await upsertThreadgate({agent, postUri}, async prev => {
if (prev) {
if (action === 'hide') {
@@ -337,5 +345,12 @@ export function useToggleReplyVisibilityMutation() {
queryKey: [threadgateRecordQueryKeyRoot],
})
},
+ onError(_, {replyUri, action}) {
+ if (action === 'hide') {
+ hiddenReplies.removeHiddenReplyUri(replyUri)
+ } else if (action === 'show') {
+ hiddenReplies.addHiddenReplyUri(replyUri)
+ }
+ },
})
}
diff --git a/src/state/threadgate-hidden-replies.tsx b/src/state/threadgate-hidden-replies.tsx
new file mode 100644
index 0000000000..5f270df36b
--- /dev/null
+++ b/src/state/threadgate-hidden-replies.tsx
@@ -0,0 +1,55 @@
+import React from 'react'
+
+type StateContext = {
+ uris: string[]
+}
+type ApiContext = {
+ addHiddenReplyUri: (uri: string) => void
+ removeHiddenReplyUri: (uri: string) => void
+}
+
+const StateContext = React.createContext({
+ uris: [],
+})
+
+const ApiContext = React.createContext({
+ addHiddenReplyUri: () => {},
+ removeHiddenReplyUri: () => {},
+})
+
+export function Provider({children}: {children: React.ReactNode}) {
+ const [uris, setHiddenReplyUris] = React.useState([])
+
+ const stateCtx = React.useMemo(
+ () => ({
+ uris,
+ }),
+ [uris],
+ )
+
+ const apiCtx = React.useMemo(
+ () => ({
+ addHiddenReplyUri(uri: string) {
+ setHiddenReplyUris(prev => Array.from(new Set([...prev, uri])))
+ },
+ removeHiddenReplyUri(uri: string) {
+ setHiddenReplyUris(prev => prev.filter(u => u !== uri))
+ },
+ }),
+ [setHiddenReplyUris],
+ )
+
+ return (
+
+ {children}
+
+ )
+}
+
+export function useThreadgateHiddenReplyUris() {
+ return React.useContext(StateContext)
+}
+
+export function useThreadgateHiddenReplyUrisAPI() {
+ return React.useContext(ApiContext)
+}
diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx
index 89d637b762..4e5bb5a89f 100644
--- a/src/view/com/util/forms/PostDropdownBtn.tsx
+++ b/src/view/com/util/forms/PostDropdownBtn.tsx
@@ -37,6 +37,7 @@ import {useToggleQuoteDetachmentMutation} from '#/state/queries/postgate'
import {getMaybeDetachedQuoteEmbed} from '#/state/queries/postgate/util'
import {useToggleReplyVisibilityMutation} from '#/state/queries/threadgate'
import {useSession} from '#/state/session'
+import {useThreadgateHiddenReplyUris} from '#/state/threadgate-hidden-replies'
import {getCurrentRoute} from 'lib/routes/helpers'
import {shareUrl} from 'lib/sharing'
import {toShareUrl} from 'lib/strings/url-helpers'
@@ -122,6 +123,7 @@ let PostDropdownBtn = ({
const quotePostDetachConfirmControl = useDialogControl()
const {mutateAsync: toggleReplyVisibility} =
useToggleReplyVisibilityMutation()
+ const {uris: hiddenReplies} = useThreadgateHiddenReplyUris()
const postUri = post.uri
const postCid = post.cid
@@ -144,7 +146,8 @@ let PostDropdownBtn = ({
const isAuthor = postAuthor.did === currentAccount?.did
const isRootPostAuthor = new AtUri(rootUri).host === currentAccount?.did
const isReplyHiddenByThreadgate =
- threadgateRecord?.hiddenReplies?.includes(postUri)
+ threadgateRecord?.hiddenReplies?.includes(postUri) ||
+ hiddenReplies.includes(postUri)
const {mutateAsync: toggleQuoteDetachment, isPending} =
useToggleQuoteDetachmentMutation()