WIP kill me
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2Zm0 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm0 3a1 1 0 0 1 1 1v3h3l.102.005a1 1 0 0 1 0 1.99L16 13h-3v3a1 1 0 1 1-2 0v-3H8a1 1 0 0 1 0-2h3V8a1 1 0 0 1 1-1Z" fill="#000"/></svg>
|
||||||
|
After Width: | Height: | Size: 344 B |
@@ -0,0 +1,5 @@
|
|||||||
|
import {createSinglePathSVG} from './TEMPLATE'
|
||||||
|
|
||||||
|
export const CirclePlus_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||||
|
path: 'M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2Zm0 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm0 3a1 1 0 0 1 1 1v3h3l.102.005a1 1 0 0 1 0 1.99L16 13h-3v3a1 1 0 1 1-2 0v-3H8a1 1 0 0 1 0-2h3V8a1 1 0 0 1 1-1Z',
|
||||||
|
})
|
||||||
@@ -47,7 +47,8 @@ export interface ThreadCtx {
|
|||||||
isHighlightedPost?: boolean
|
isHighlightedPost?: boolean
|
||||||
hasMore?: boolean
|
hasMore?: boolean
|
||||||
/**
|
/**
|
||||||
* Means the loading state has parents
|
* Means the loading state has parents, but once the data loads we don't even
|
||||||
|
* populate this value, so it's the same as `threadNode.parents.length`
|
||||||
*/
|
*/
|
||||||
isParentLoading?: boolean
|
isParentLoading?: boolean
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -41,11 +41,12 @@ export function usePostThread({
|
|||||||
const query = useQuery({
|
const query = useQuery({
|
||||||
enabled,
|
enabled,
|
||||||
queryKey,
|
queryKey,
|
||||||
|
gcTime: 0,
|
||||||
async queryFn() {
|
async queryFn() {
|
||||||
const {data} = await agent.app.bsky.unspecced.getPostThreadV2({
|
const {data} = await agent.app.bsky.unspecced.getPostThreadV2({
|
||||||
anchor: uri!,
|
anchor: uri!,
|
||||||
branchingFactor: params.view === 'linear' ? 1 : 100,
|
branchingFactor: params.view === 'linear' ? 1 : 3, // 100 TODO
|
||||||
below: 10,
|
below: 3,
|
||||||
sorting: mapSortOptionsToSortID(params.sort),
|
sorting: mapSortOptionsToSortID(params.sort),
|
||||||
})
|
})
|
||||||
return data
|
return data
|
||||||
@@ -95,6 +96,7 @@ export function usePostThread({
|
|||||||
return {
|
return {
|
||||||
...query,
|
...query,
|
||||||
data: {
|
data: {
|
||||||
|
anchorIndex: items.findIndex(i => Boolean(i.ui?.isAnchor)),
|
||||||
items,
|
items,
|
||||||
threadgate: query.data?.threadgate,
|
threadgate: query.data?.threadgate,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
|
APP_BSKY_UNSPECCED,
|
||||||
|
AtUri,
|
||||||
AppBskyUnspeccedGetPostThreadV2,
|
AppBskyUnspeccedGetPostThreadV2,
|
||||||
type ModerationDecision,
|
type ModerationDecision,
|
||||||
type ModerationOpts,
|
type ModerationOpts,
|
||||||
@@ -21,6 +23,54 @@ export function flatten(
|
|||||||
) {
|
) {
|
||||||
const flattened: Slice[] = sorted.items
|
const flattened: Slice[] = sorted.items
|
||||||
|
|
||||||
|
const unhydratedReplyIntervals = []
|
||||||
|
|
||||||
|
for (let i = 0; i < flattened.length; i++) {
|
||||||
|
const item = flattened[i]
|
||||||
|
|
||||||
|
if (item.type === 'threadPost') {
|
||||||
|
// TODO should not insert if not found post etc
|
||||||
|
if (item.ui.isAnchor && hasSession && !item.value.post.viewer?.replyDisabled) {
|
||||||
|
flattened.splice(i + 1, 0, {
|
||||||
|
type: 'replyComposer',
|
||||||
|
key: 'replyComposer',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const prev = unhydratedReplyIntervals[unhydratedReplyIntervals.length - 1]
|
||||||
|
|
||||||
|
if (item.annotations.has(APP_BSKY_UNSPECCED.GetPostThreadV2HasMoreReplies)) {
|
||||||
|
unhydratedReplyIntervals.push({
|
||||||
|
item,
|
||||||
|
replyCount: item.value.post.replyCount || 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If direct child of previous item with `hasMoreReplies`, subtract
|
||||||
|
*/
|
||||||
|
if (prev && item.depth === prev.item.depth + 1) {
|
||||||
|
prev.replyCount = Math.max(0, prev.replyCount - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev && item.depth <= prev.item.depth) {
|
||||||
|
flattened.splice(i, 0, {
|
||||||
|
type: 'readMore',
|
||||||
|
key: `readMore:${prev.item.uri}`,
|
||||||
|
indent: prev.item.depth + (item.depth < prev.item.depth ? -1 : 0),
|
||||||
|
replyCount: prev.replyCount,
|
||||||
|
nextAnchor: prev.item,
|
||||||
|
nextAnchorUri: new AtUri(prev.item.uri),
|
||||||
|
})
|
||||||
|
unhydratedReplyIntervals.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert hidden items and buttons to show them
|
||||||
|
*/
|
||||||
|
|
||||||
if (sorted.hidden.length) {
|
if (sorted.hidden.length) {
|
||||||
if (showHidden) {
|
if (showHidden) {
|
||||||
flattened.push(...sorted.hidden)
|
flattened.push(...sorted.hidden)
|
||||||
@@ -55,30 +105,6 @@ export function flatten(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasSession) {
|
|
||||||
for (let i = 0; i < flattened.length; i++) {
|
|
||||||
const item = flattened[i]
|
|
||||||
|
|
||||||
// TODO should not insert if not found post etc
|
|
||||||
if (item.type === 'threadPost') {
|
|
||||||
if (item.ui.isAnchor) {
|
|
||||||
flattened.splice(i + 1, 0, {
|
|
||||||
type: 'replyComposer',
|
|
||||||
key: 'replyComposer',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
item.value.post.replyCount &&
|
|
||||||
item.value.post.replyCount > 0 &&
|
|
||||||
!item.ui.showChildReplyLine
|
|
||||||
) {
|
|
||||||
console.log('insert more link')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return flattened
|
return flattened
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
type APP_BSKY_UNSPECCED,
|
type APP_BSKY_UNSPECCED,
|
||||||
|
type AtUri,
|
||||||
type AppBskyFeedDefs,
|
type AppBskyFeedDefs,
|
||||||
type AppBskyFeedPost,
|
type AppBskyFeedPost,
|
||||||
type AppBskyUnspeccedGetPostThreadV2,
|
type AppBskyUnspeccedGetPostThreadV2,
|
||||||
@@ -89,7 +90,10 @@ export type Slice =
|
|||||||
kind: HiddenReplyKind
|
kind: HiddenReplyKind
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'threadPostNoOp'
|
type: 'readMore'
|
||||||
key: string
|
key: string
|
||||||
comment: string
|
indent: number
|
||||||
|
replyCount: number
|
||||||
|
nextAnchor: Extract<Slice, {type: 'threadPost'}>
|
||||||
|
nextAnchorUri: AtUri
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ type ThreadSkeletonParts = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const keyExtractor = (item: RowItem) => {
|
const keyExtractor = (item: RowItem) => {
|
||||||
console.log(item._reactKey)
|
|
||||||
return item._reactKey
|
return item._reactKey
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,7 +257,6 @@ export function PostThread({uri}: {uri: string | undefined}) {
|
|||||||
fetchedAt,
|
fetchedAt,
|
||||||
randomCache,
|
randomCache,
|
||||||
])
|
])
|
||||||
console.log({thread, skeleton})
|
|
||||||
|
|
||||||
const error = React.useMemo(() => {
|
const error = React.useMemo(() => {
|
||||||
if (AppBskyFeedDefs.isNotFoundPost(thread)) {
|
if (AppBskyFeedDefs.isNotFoundPost(thread)) {
|
||||||
@@ -299,6 +297,9 @@ export function PostThread({uri}: {uri: string | undefined}) {
|
|||||||
// maintainVisibleContentPosition and onContentSizeChange
|
// maintainVisibleContentPosition and onContentSizeChange
|
||||||
// to "hold onto" the correct row instead of the first one.
|
// to "hold onto" the correct row instead of the first one.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is basically `!!parents.length`, see notes on `isParentLoading`
|
||||||
|
*/
|
||||||
if (!highlightedPost.ctx.isParentLoading && !deferParents) {
|
if (!highlightedPost.ctx.isParentLoading && !deferParents) {
|
||||||
// When progressively revealing parents, rendering a placeholder
|
// When progressively revealing parents, rendering a placeholder
|
||||||
// here will cause scrolling jumps. Don't add it unless you test it.
|
// here will cause scrolling jumps. Don't add it unless you test it.
|
||||||
@@ -325,6 +326,8 @@ export function PostThread({uri}: {uri: string | undefined}) {
|
|||||||
return arr
|
return arr
|
||||||
}, [skeleton, deferParents, maxParents, maxReplies])
|
}, [skeleton, deferParents, maxParents, maxReplies])
|
||||||
|
|
||||||
|
console.log({anchorIndex: posts.findIndex(p => p.ctx?.isHighlightedPost)})
|
||||||
|
|
||||||
// This is only used on the web to keep the post in view when its parents load.
|
// This is only used on the web to keep the post in view when its parents load.
|
||||||
// On native, we rely on `maintainVisibleContentPosition` instead.
|
// On native, we rely on `maintainVisibleContentPosition` instead.
|
||||||
const didAdjustScrollWeb = useRef<boolean>(false)
|
const didAdjustScrollWeb = useRef<boolean>(false)
|
||||||
@@ -340,11 +343,8 @@ export function PostThread({uri}: {uri: string | undefined}) {
|
|||||||
const headerNode = headerRef.current
|
const headerNode = headerRef.current
|
||||||
if (postNode && headerNode) {
|
if (postNode && headerNode) {
|
||||||
let pageY = (postNode as any as Element).getBoundingClientRect().top
|
let pageY = (postNode as any as Element).getBoundingClientRect().top
|
||||||
console.log({pageY})
|
|
||||||
pageY -= (headerNode as any as Element).getBoundingClientRect().height
|
pageY -= (headerNode as any as Element).getBoundingClientRect().height
|
||||||
console.log({pageY})
|
|
||||||
pageY = Math.max(0, pageY)
|
pageY = Math.max(0, pageY)
|
||||||
console.log({pageY})
|
|
||||||
ref.current?.scrollToOffset({
|
ref.current?.scrollToOffset({
|
||||||
animated: false,
|
animated: false,
|
||||||
offset: pageY,
|
offset: pageY,
|
||||||
@@ -423,6 +423,8 @@ export function PostThread({uri}: {uri: string | undefined}) {
|
|||||||
(skeleton.highlightedPost.ctx.isParentLoading ||
|
(skeleton.highlightedPost.ctx.isParentLoading ||
|
||||||
Boolean(skeleton?.parents && skeleton.parents.length > 0))
|
Boolean(skeleton?.parents && skeleton.parents.length > 0))
|
||||||
|
|
||||||
|
console.log({hasParents})
|
||||||
|
|
||||||
const renderItem = ({item, index}: {item: RowItem; index: number}) => {
|
const renderItem = ({item, index}: {item: RowItem; index: number}) => {
|
||||||
if (item === REPLY_PROMPT && hasSession) {
|
if (item === REPLY_PROMPT && hasSession) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -13,9 +13,7 @@ import {cleanError} from '#/lib/strings/errors'
|
|||||||
import {makeRecordUri} from '#/lib/strings/url-helpers'
|
import {makeRecordUri} from '#/lib/strings/url-helpers'
|
||||||
import {isNative} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
import {useSetMinimalShellMode} from '#/state/shell'
|
import {useSetMinimalShellMode} from '#/state/shell'
|
||||||
{
|
import {PostThread as PostThreadComponent} from '#/view/com/post-thread/PostThread'
|
||||||
/* import {PostThread as PostThreadComponent} from '#/view/com/post-thread/PostThread' */
|
|
||||||
}
|
|
||||||
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
||||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||||
import {ScrollProvider} from '#/lib/ScrollContext'
|
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||||
@@ -32,7 +30,10 @@ import {PostThreadShowHiddenReplies} from '#/view/com/post-thread/PostThreadShow
|
|||||||
import {List, type ListMethods} from '#/view/com/util/List'
|
import {List, type ListMethods} from '#/view/com/util/List'
|
||||||
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
|
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
|
||||||
import {Button, ButtonIcon} from '#/components/Button'
|
import {Button, ButtonIcon} from '#/components/Button'
|
||||||
|
import {Link} from '#/components/Link'
|
||||||
|
import {makeProfileLink} from '#/lib/routes/links'
|
||||||
import {SettingsSliderVertical_Stroke2_Corner0_Rounded as SettingsSlider} from '#/components/icons/SettingsSlider'
|
import {SettingsSliderVertical_Stroke2_Corner0_Rounded as SettingsSlider} from '#/components/icons/SettingsSlider'
|
||||||
|
import {CirclePlus_Stroke2_Corner0_Rounded as CirclePlus} from '#/components/icons/CirclePlus'
|
||||||
import * as Layout from '#/components/Layout'
|
import * as Layout from '#/components/Layout'
|
||||||
import {ListFooter} from '#/components/Lists'
|
import {ListFooter} from '#/components/Lists'
|
||||||
import * as Menu from '#/components/Menu'
|
import * as Menu from '#/components/Menu'
|
||||||
@@ -41,7 +42,7 @@ import {Text} from '#/components/Typography'
|
|||||||
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.
|
||||||
// So the row we want to use as the scroll anchor is the first row.
|
// So the row we want to use as the scroll anchor is the first row.
|
||||||
minIndexForVisible: 1,
|
minIndexForVisible: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'>
|
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'>
|
||||||
@@ -232,13 +233,11 @@ export function Inner({uri}: {uri: string | undefined}) {
|
|||||||
// prevPost={prev}
|
// prevPost={prev}
|
||||||
// nextPost={next}
|
// nextPost={next}
|
||||||
isHighlightedPost={item.ui.isAnchor}
|
isHighlightedPost={item.ui.isAnchor}
|
||||||
// @ts-expect-error
|
hasMore={false} // TODO need to replace this entirely
|
||||||
hasMore={item.value.hasUnhydratedReplies}
|
|
||||||
showChildReplyLine={item.ui.showChildReplyLine}
|
showChildReplyLine={item.ui.showChildReplyLine}
|
||||||
showParentReplyLine={item.ui.showParentReplyLine}
|
showParentReplyLine={item.ui.showParentReplyLine}
|
||||||
hasPrecedingItem={
|
hasPrecedingItem={
|
||||||
// @ts-expect-error
|
item.ui.showParentReplyLine
|
||||||
item.ui.showParentReplyLine || !!item.value.hasUnhydratedParents
|
|
||||||
} // !!hasUnrevealedParents // TODO
|
} // !!hasUnrevealedParents // TODO
|
||||||
overrideBlur={
|
overrideBlur={
|
||||||
shownHiddenReplyKinds.has(HiddenReplyKind.Muted) && item.depth > 0
|
shownHiddenReplyKinds.has(HiddenReplyKind.Muted) && item.depth > 0
|
||||||
@@ -248,6 +247,68 @@ export function Inner({uri}: {uri: string | undefined}) {
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
} else if (item.type === 'readMore') {
|
||||||
|
return (
|
||||||
|
<View style={[a.flex_row, a.px_sm]}>
|
||||||
|
{Array.from(Array(item.indent - 1)).map((_, n: number) => (
|
||||||
|
<View
|
||||||
|
key={`${item.key}-padding-${n}`}
|
||||||
|
style={[
|
||||||
|
a.ml_sm,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
{
|
||||||
|
borderLeftWidth: 2,
|
||||||
|
paddingLeft: a.pl_sm.paddingLeft - 2, // minus border
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<View style={[a.ml_sm]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
{
|
||||||
|
borderLeftWidth: 2,
|
||||||
|
borderBottomWidth: 2,
|
||||||
|
borderBottomLeftRadius: a.rounded_sm.borderRadius,
|
||||||
|
height: 12,
|
||||||
|
width: a.pl_sm.paddingLeft * 2,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<Link
|
||||||
|
label={_(msg`Read more replies`)}
|
||||||
|
to={makeProfileLink(
|
||||||
|
{
|
||||||
|
did: item.nextAnchorUri.host,
|
||||||
|
handle: item.nextAnchor.value.post.author.handle,
|
||||||
|
},
|
||||||
|
'post',
|
||||||
|
item.nextAnchorUri.rkey,
|
||||||
|
)}
|
||||||
|
style={[a.pt_2xs, a.pb_sm, a.gap_xs]}>
|
||||||
|
{({hovered, pressed}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CirclePlus
|
||||||
|
fill={t.atoms.text_contrast_high.color}
|
||||||
|
width={18}
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.text_sm,
|
||||||
|
t.atoms.text_contrast_medium,
|
||||||
|
(hovered || pressed) && a.underline,
|
||||||
|
]}>
|
||||||
|
Read {item.replyCount} more replies
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</Link>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
} else if (item.type === 'threadPostBlocked') {
|
} else if (item.type === 'threadPostBlocked') {
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@@ -297,6 +358,8 @@ export function Inner({uri}: {uri: string | undefined}) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('PostThreadScreen', data?.anchorIndex)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Layout.Header.Outer headerRef={headerRef}>
|
<Layout.Header.Outer headerRef={headerRef}>
|
||||||
@@ -337,7 +400,7 @@ export function Inner({uri}: {uri: string | undefined}) {
|
|||||||
*/
|
*/
|
||||||
maintainVisibleContentPosition={
|
maintainVisibleContentPosition={
|
||||||
isNative // && hasParents // TODO not sure we need this
|
isNative // && hasParents // TODO not sure we need this
|
||||||
? MAINTAIN_VISIBLE_CONTENT_POSITION
|
? { minIndexForVisible: 0 } // MAINTAIN_VISIBLE_CONTENT_POSITION
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
desktopFixedHeight
|
desktopFixedHeight
|
||||||
|
|||||||
Reference in New Issue
Block a user