Rough handling of hidden/muted
This commit is contained in:
@@ -43,7 +43,7 @@ import {
|
|||||||
findAllProfilesInQueryData as findAllProfilesInFeedQueryData,
|
findAllProfilesInQueryData as findAllProfilesInFeedQueryData,
|
||||||
} from './post-feed'
|
} from './post-feed'
|
||||||
|
|
||||||
export type PostThreadV2Options = {
|
export type PostThreadV2Params = {
|
||||||
view: 'tree' | 'linear'
|
view: 'tree' | 'linear'
|
||||||
sort: 'hotness' | 'oldest' | 'newest' | 'most-likes' | 'random' | string
|
sort: 'hotness' | 'oldest' | 'newest' | 'most-likes' | 'random' | string
|
||||||
prioritizeFollows: BskyThreadViewPreference['prioritizeFollowedUsers']
|
prioritizeFollows: BskyThreadViewPreference['prioritizeFollowedUsers']
|
||||||
@@ -51,13 +51,16 @@ export type PostThreadV2Options = {
|
|||||||
|
|
||||||
export const getPostThreadV2QueryKeyRoot = 'getPostThreadV2' as const
|
export const getPostThreadV2QueryKeyRoot = 'getPostThreadV2' as const
|
||||||
export const createGetPostThreadV2QueryKey = (
|
export const createGetPostThreadV2QueryKey = (
|
||||||
props: Pick<GetPostThreadV2Params, 'uri' | 'options'>,
|
props: Pick<GetPostThreadV2QueryProps, 'uri' | 'params'>,
|
||||||
) => [getPostThreadV2QueryKeyRoot, props] as const
|
) => [getPostThreadV2QueryKeyRoot, props] as const
|
||||||
|
|
||||||
export type GetPostThreadV2Params = {
|
export type GetPostThreadV2QueryProps = {
|
||||||
uri?: string
|
uri?: string
|
||||||
enabled?: boolean
|
enabled?: boolean
|
||||||
options: PostThreadV2Options
|
params: PostThreadV2Params
|
||||||
|
state: {
|
||||||
|
shownHiddenReplyKinds: Set<HiddenReplyKind>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetPostThreadV2QueryData = {
|
export type GetPostThreadV2QueryData = {
|
||||||
@@ -65,7 +68,7 @@ export type GetPostThreadV2QueryData = {
|
|||||||
threadgate?: AppBskyFeedDefs.ThreadgateView
|
threadgate?: AppBskyFeedDefs.ThreadgateView
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapSortOptionsToSortID(sort: PostThreadV2Options['sort']) {
|
export function mapSortOptionsToSortID(sort: PostThreadV2Params['sort']) {
|
||||||
switch (sort) {
|
switch (sort) {
|
||||||
case 'hotness':
|
case 'hotness':
|
||||||
return APP_BSKY_FEED.GetPostThreadV2Hotness
|
return APP_BSKY_FEED.GetPostThreadV2Hotness
|
||||||
@@ -83,8 +86,9 @@ export function mapSortOptionsToSortID(sort: PostThreadV2Options['sort']) {
|
|||||||
export function useGetPostThreadV2({
|
export function useGetPostThreadV2({
|
||||||
uri,
|
uri,
|
||||||
enabled: isEnabled,
|
enabled: isEnabled,
|
||||||
options,
|
params,
|
||||||
}: GetPostThreadV2Params) {
|
state,
|
||||||
|
}: GetPostThreadV2QueryProps) {
|
||||||
const qc = useQueryClient()
|
const qc = useQueryClient()
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
@@ -97,13 +101,13 @@ export function useGetPostThreadV2({
|
|||||||
enabled,
|
enabled,
|
||||||
queryKey: createGetPostThreadV2QueryKey({
|
queryKey: createGetPostThreadV2QueryKey({
|
||||||
uri,
|
uri,
|
||||||
options,
|
params,
|
||||||
}),
|
}),
|
||||||
async queryFn() {
|
async queryFn() {
|
||||||
const {data} = await agent.app.bsky.feed.getPostThreadV2({
|
const {data} = await agent.app.bsky.feed.getPostThreadV2({
|
||||||
uri: uri!,
|
uri: uri!,
|
||||||
below: 10,
|
below: 10,
|
||||||
sorting: mapSortOptionsToSortID(options.sort),
|
sorting: mapSortOptionsToSortID(params.sort),
|
||||||
})
|
})
|
||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
@@ -136,12 +140,12 @@ export function useGetPostThreadV2({
|
|||||||
|
|
||||||
const filtered = filterAndSort(query.data?.thread || [], {
|
const filtered = filterAndSort(query.data?.thread || [], {
|
||||||
hasSession,
|
hasSession,
|
||||||
options,
|
params,
|
||||||
threadgateHiddenReplies: mergeThreadgateHiddenReplies(
|
threadgateHiddenReplies: mergeThreadgateHiddenReplies(
|
||||||
query.data?.threadgate?.record,
|
query.data?.threadgate?.record,
|
||||||
),
|
),
|
||||||
moderationOpts: moderationOpts!,
|
moderationOpts: moderationOpts!,
|
||||||
showHiddenReplies: false,
|
shownHiddenReplyKinds: state.shownHiddenReplyKinds,
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -251,20 +255,24 @@ export function filterAndSort(
|
|||||||
thread: AppBskyFeedGetPostThreadV2.OutputSchema['thread'],
|
thread: AppBskyFeedGetPostThreadV2.OutputSchema['thread'],
|
||||||
{
|
{
|
||||||
hasSession,
|
hasSession,
|
||||||
options,
|
params,
|
||||||
threadgateHiddenReplies,
|
threadgateHiddenReplies,
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
showHiddenReplies = false,
|
shownHiddenReplyKinds,
|
||||||
}: {
|
}: {
|
||||||
hasSession: boolean
|
hasSession: boolean
|
||||||
options: PostThreadV2Options
|
params: PostThreadV2Params
|
||||||
threadgateHiddenReplies: Set<string>
|
threadgateHiddenReplies: Set<string>
|
||||||
moderationOpts: ModerationOpts
|
moderationOpts: ModerationOpts
|
||||||
showHiddenReplies: boolean
|
shownHiddenReplyKinds: Set<HiddenReplyKind>
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const sorted: Slice[] = []
|
const slices: Slice[] = []
|
||||||
const hidden: Slice[] = []
|
const hidden: Slice[] = []
|
||||||
|
const muted: Slice[] = []
|
||||||
|
|
||||||
|
const showMuted = shownHiddenReplyKinds.has(HiddenReplyKind.Muted)
|
||||||
|
const showHidden = shownHiddenReplyKinds.has(HiddenReplyKind.Hidden)
|
||||||
|
|
||||||
traversal: for (let i = 0; i < thread.length; i++) {
|
traversal: for (let i = 0; i < thread.length; i++) {
|
||||||
const item = thread[i]
|
const item = thread[i]
|
||||||
@@ -282,13 +290,13 @@ export function filterAndSort(
|
|||||||
*/
|
*/
|
||||||
} else if (item.depth === 0) {
|
} else if (item.depth === 0) {
|
||||||
if (AppBskyFeedDefs.isThreadItemNoUnauthenticated(item)) {
|
if (AppBskyFeedDefs.isThreadItemNoUnauthenticated(item)) {
|
||||||
sorted.push(views.noUnauthenticated({item}))
|
slices.push(views.noUnauthenticated({item}))
|
||||||
} else if (AppBskyFeedDefs.isThreadItemNotFound(item)) {
|
} else if (AppBskyFeedDefs.isThreadItemNotFound(item)) {
|
||||||
sorted.push(views.notFound({item}))
|
slices.push(views.notFound({item}))
|
||||||
} else if (AppBskyFeedDefs.isThreadItemBlocked(item)) {
|
} else if (AppBskyFeedDefs.isThreadItemBlocked(item)) {
|
||||||
sorted.push(views.blocked({item}))
|
slices.push(views.blocked({item}))
|
||||||
} else if (AppBskyFeedDefs.isThreadItemPost(item)) {
|
} else if (AppBskyFeedDefs.isThreadItemPost(item)) {
|
||||||
sorted.push(
|
slices.push(
|
||||||
views.post({
|
views.post({
|
||||||
item,
|
item,
|
||||||
oneUp: oneUp,
|
oneUp: oneUp,
|
||||||
@@ -298,7 +306,7 @@ export function filterAndSort(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (hasSession) {
|
if (hasSession) {
|
||||||
sorted.push({
|
slices.push({
|
||||||
type: 'replyComposer',
|
type: 'replyComposer',
|
||||||
key: 'replyComposer',
|
key: 'replyComposer',
|
||||||
})
|
})
|
||||||
@@ -310,16 +318,16 @@ export function filterAndSort(
|
|||||||
const parentOneUp = thread[pi - 1]
|
const parentOneUp = thread[pi - 1]
|
||||||
|
|
||||||
if (AppBskyFeedDefs.isThreadItemNoUnauthenticated(parent)) {
|
if (AppBskyFeedDefs.isThreadItemNoUnauthenticated(parent)) {
|
||||||
sorted.unshift(views.noUnauthenticated({item: parent}))
|
slices.unshift(views.noUnauthenticated({item: parent}))
|
||||||
break parentTraversal
|
break parentTraversal
|
||||||
} else if (AppBskyFeedDefs.isThreadItemNotFound(parent)) {
|
} else if (AppBskyFeedDefs.isThreadItemNotFound(parent)) {
|
||||||
sorted.unshift(views.notFound({item: parent}))
|
slices.unshift(views.notFound({item: parent}))
|
||||||
break parentTraversal
|
break parentTraversal
|
||||||
} else if (AppBskyFeedDefs.isThreadItemBlocked(parent)) {
|
} else if (AppBskyFeedDefs.isThreadItemBlocked(parent)) {
|
||||||
sorted.unshift(views.blocked({item: parent}))
|
slices.unshift(views.blocked({item: parent}))
|
||||||
break parentTraversal
|
break parentTraversal
|
||||||
} else if (AppBskyFeedDefs.isThreadItemPost(parent)) {
|
} else if (AppBskyFeedDefs.isThreadItemPost(parent)) {
|
||||||
sorted.unshift(
|
slices.unshift(
|
||||||
views.post({
|
views.post({
|
||||||
item,
|
item,
|
||||||
oneUp: parentOneUp,
|
oneUp: parentOneUp,
|
||||||
@@ -347,9 +355,18 @@ export function filterAndSort(
|
|||||||
i = branch.end
|
i = branch.end
|
||||||
continue traversal
|
continue traversal
|
||||||
} else if (AppBskyFeedDefs.isThreadItemPost(item)) {
|
} else if (AppBskyFeedDefs.isThreadItemPost(item)) {
|
||||||
|
const post = views.post({
|
||||||
|
item,
|
||||||
|
oneUp,
|
||||||
|
oneDown,
|
||||||
|
moderationOpts,
|
||||||
|
})
|
||||||
|
const modui = post.moderation.ui('contentList')
|
||||||
|
const isBlurred = modui.blur || modui.filter
|
||||||
|
const isMuted = (modui.blurs[0] || modui.filters[0])?.type === 'muted'
|
||||||
const isHidden = threadgateHiddenReplies.has(item.uri)
|
const isHidden = threadgateHiddenReplies.has(item.uri)
|
||||||
|
|
||||||
if (isHidden) {
|
if (isHidden || isBlurred || isMuted) {
|
||||||
const branch = getSubBranch(thread, i, item.depth)
|
const branch = getSubBranch(thread, i, item.depth)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -360,30 +377,29 @@ export function filterAndSort(
|
|||||||
const next = thread[ci]
|
const next = thread[ci]
|
||||||
|
|
||||||
if (AppBskyFeedDefs.isThreadItemPost(next)) {
|
if (AppBskyFeedDefs.isThreadItemPost(next)) {
|
||||||
hidden.push(
|
const post = views.post({
|
||||||
views.post({
|
item: next,
|
||||||
item: next,
|
oneUp: oneUp,
|
||||||
oneUp: oneUp,
|
oneDown: oneDown,
|
||||||
oneDown: oneDown,
|
moderationOpts,
|
||||||
moderationOpts,
|
})
|
||||||
}),
|
|
||||||
)
|
if (isMuted) {
|
||||||
|
muted.push(post)
|
||||||
|
} else {
|
||||||
|
hidden.push(post)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (showHiddenReplies) {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Nested hidden replies either filter entirely or show in situ
|
* Nested hidden replies either filter entirely or show in situ
|
||||||
*/
|
*/
|
||||||
sorted.push(
|
if ((isMuted && showMuted) || showHidden) {
|
||||||
views.post({
|
slices.push(post)
|
||||||
item,
|
}
|
||||||
oneUp,
|
|
||||||
oneDown,
|
|
||||||
moderationOpts,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -395,24 +411,49 @@ export function filterAndSort(
|
|||||||
/*
|
/*
|
||||||
* Not hidden, so show it
|
* Not hidden, so show it
|
||||||
*/
|
*/
|
||||||
sorted.push(
|
slices.push(post)
|
||||||
views.post({
|
|
||||||
item,
|
|
||||||
oneUp,
|
|
||||||
oneDown,
|
|
||||||
moderationOpts,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showHiddenReplies) {
|
const [hiddenKind1, hiddenKind2] = Array.from(shownHiddenReplyKinds)
|
||||||
return sorted.concat(hidden)
|
|
||||||
|
if (hiddenKind1 === HiddenReplyKind.Hidden) {
|
||||||
|
slices.push(...hidden)
|
||||||
|
}
|
||||||
|
if (hiddenKind1 === HiddenReplyKind.Muted) {
|
||||||
|
slices.push(...muted)
|
||||||
|
}
|
||||||
|
if (hiddenKind2 === HiddenReplyKind.Hidden) {
|
||||||
|
slices.push(...hidden)
|
||||||
|
}
|
||||||
|
if (hiddenKind2 === HiddenReplyKind.Muted) {
|
||||||
|
slices.push(...muted)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sorted
|
if (muted.length && !showMuted) {
|
||||||
|
slices.push({
|
||||||
|
type: 'showHiddenReplies',
|
||||||
|
key: 'showMutedReplies',
|
||||||
|
kind: HiddenReplyKind.Muted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hidden.length && !showHidden) {
|
||||||
|
slices.push({
|
||||||
|
type: 'showHiddenReplies',
|
||||||
|
key: 'showHiddenReplies',
|
||||||
|
kind: HiddenReplyKind.Hidden,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return slices
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum HiddenReplyKind {
|
||||||
|
Hidden = 'hidden',
|
||||||
|
Muted = 'muted',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Slice =
|
export type Slice =
|
||||||
@@ -453,11 +494,7 @@ export type Slice =
|
|||||||
| {
|
| {
|
||||||
type: 'showHiddenReplies'
|
type: 'showHiddenReplies'
|
||||||
key: string
|
key: string
|
||||||
}
|
kind: HiddenReplyKind
|
||||||
| {
|
|
||||||
// TODO needed?
|
|
||||||
type: 'showMutedReplies'
|
|
||||||
key: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getThreadgate(
|
function getThreadgate(
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import * as Layout from '#/components/Layout'
|
|||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||||
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
||||||
import {useGetPostThreadV2, Slice} from '#/state/queries/useGetPostThreadV2'
|
import {useGetPostThreadV2, Slice, HiddenReplyKind} from '#/state/queries/useGetPostThreadV2'
|
||||||
import * as Menu from '#/components/Menu'
|
import * as Menu from '#/components/Menu'
|
||||||
import {Button, ButtonIcon} from '#/components/Button'
|
import {Button, ButtonIcon} from '#/components/Button'
|
||||||
import {SettingsSliderVertical_Stroke2_Corner0_Rounded as SettingsSlider} from '#/components/icons/SettingsSlider'
|
import {SettingsSliderVertical_Stroke2_Corner0_Rounded as SettingsSlider} from '#/components/icons/SettingsSlider'
|
||||||
@@ -28,6 +28,7 @@ import {PostThreadItem} from '#/view/com/post-thread/PostThreadItem'
|
|||||||
import {PostThreadComposePrompt} from '#/view/com/post-thread/PostThreadComposePrompt'
|
import {PostThreadComposePrompt} from '#/view/com/post-thread/PostThreadComposePrompt'
|
||||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||||
import {useComposerControls} from '#/state/shell'
|
import {useComposerControls} from '#/state/shell'
|
||||||
|
import {PostThreadShowHiddenReplies} from '#/view/com/post-thread/PostThreadShowHiddenReplies'
|
||||||
|
|
||||||
const MAINTAIN_VISIBLE_CONTENT_POSITION = {
|
const MAINTAIN_VISIBLE_CONTENT_POSITION = {
|
||||||
// We don't insert any elements before the root row while loading.
|
// We don't insert any elements before the root row while loading.
|
||||||
@@ -126,14 +127,20 @@ export function Inner({uri}: {uri: string | undefined}) {
|
|||||||
setTreeViewEnabled,
|
setTreeViewEnabled,
|
||||||
} = useThreadPreferences()
|
} = useThreadPreferences()
|
||||||
|
|
||||||
|
const [showHiddenReplies, setShowHiddenReplies] = useState<HiddenReplyKind | null>(null)
|
||||||
|
const [shownHiddenReplyKinds, setShownHiddenReplyKinds] = useState<Set<HiddenReplyKind>>(new Set())
|
||||||
|
|
||||||
const {isFetching, isPlaceholderData, error, data, refetch} = useGetPostThreadV2({
|
const {isFetching, isPlaceholderData, error, data, refetch} = useGetPostThreadV2({
|
||||||
uri,
|
uri,
|
||||||
enabled: isThreadPreferencesLoaded,
|
enabled: isThreadPreferencesLoaded,
|
||||||
options: {
|
params: {
|
||||||
sort: sortReplies,
|
sort: sortReplies,
|
||||||
view: treeViewEnabled ? 'tree' : 'linear',
|
view: treeViewEnabled ? 'tree' : 'linear',
|
||||||
prioritizeFollows: prioritizeFollowedUsers,
|
prioritizeFollows: prioritizeFollowedUsers,
|
||||||
},
|
},
|
||||||
|
state: {
|
||||||
|
shownHiddenReplyKinds,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const ref = useRef<ListMethods>(null)
|
const ref = useRef<ListMethods>(null)
|
||||||
@@ -199,11 +206,9 @@ export function Inner({uri}: {uri: string | undefined}) {
|
|||||||
hasPrecedingItem={
|
hasPrecedingItem={
|
||||||
item.ui.showParentReplyLine || !!item.slice.hasUnhydratedParents
|
item.ui.showParentReplyLine || !!item.slice.hasUnhydratedParents
|
||||||
} // !!hasUnrevealedParents // TODO
|
} // !!hasUnrevealedParents // TODO
|
||||||
// overrideBlur={
|
overrideBlur={
|
||||||
// hiddenRepliesState ===
|
shownHiddenReplyKinds.has(HiddenReplyKind.Muted) && item.slice.depth > 0
|
||||||
// HiddenRepliesState.ShowAndOverridePostHider &&
|
}
|
||||||
// item.ctx.depth > 0
|
|
||||||
// }
|
|
||||||
onPostReply={onPostReply}
|
onPostReply={onPostReply}
|
||||||
hideTopBorder={index === 0} // && !item.slice.isParentLoading} // TODO
|
hideTopBorder={index === 0} // && !item.slice.isParentLoading} // TODO
|
||||||
/>
|
/>
|
||||||
@@ -215,6 +220,13 @@ export function Inner({uri}: {uri: string | undefined}) {
|
|||||||
{gtPhone && <PostThreadComposePrompt onPressCompose={onReplyToAnchor} />}
|
{gtPhone && <PostThreadComposePrompt onPressCompose={onReplyToAnchor} />}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
} else if (item.type === 'showHiddenReplies') {
|
||||||
|
return (
|
||||||
|
<PostThreadShowHiddenReplies
|
||||||
|
type={item.kind === 'muted' ? 'muted' : 'hidden'}
|
||||||
|
onPress={() => setShownHiddenReplyKinds(kinds => new Set([...kinds, item.kind]))}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user