WIP kill me

This commit is contained in:
Eric Bailey
2025-05-23 16:35:22 -05:00
parent 9e9ab55027
commit da4afb78d0
8 changed files with 147 additions and 43 deletions
@@ -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

+5
View File
@@ -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',
})
+2 -1
View File
@@ -47,7 +47,8 @@ export interface ThreadCtx {
isHighlightedPost?: 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
/**
+4 -2
View File
@@ -41,11 +41,12 @@ export function usePostThread({
const query = useQuery({
enabled,
queryKey,
gcTime: 0,
async queryFn() {
const {data} = await agent.app.bsky.unspecced.getPostThreadV2({
anchor: uri!,
branchingFactor: params.view === 'linear' ? 1 : 100,
below: 10,
branchingFactor: params.view === 'linear' ? 1 : 3, // 100 TODO
below: 3,
sorting: mapSortOptionsToSortID(params.sort),
})
return data
@@ -95,6 +96,7 @@ export function usePostThread({
return {
...query,
data: {
anchorIndex: items.findIndex(i => Boolean(i.ui?.isAnchor)),
items,
threadgate: query.data?.threadgate,
},
+50 -24
View File
@@ -1,4 +1,6 @@
import {
APP_BSKY_UNSPECCED,
AtUri,
AppBskyUnspeccedGetPostThreadV2,
type ModerationDecision,
type ModerationOpts,
@@ -21,6 +23,54 @@ export function flatten(
) {
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 (showHidden) {
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
}
+6 -2
View File
@@ -1,6 +1,7 @@
import {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
type APP_BSKY_UNSPECCED,
type AtUri,
type AppBskyFeedDefs,
type AppBskyFeedPost,
type AppBskyUnspeccedGetPostThreadV2,
@@ -89,7 +90,10 @@ export type Slice =
kind: HiddenReplyKind
}
| {
type: 'threadPostNoOp'
type: 'readMore'
key: string
comment: string
indent: number
replyCount: number
nextAnchor: Extract<Slice, {type: 'threadPost'}>
nextAnchorUri: AtUri
}
+7 -5
View File
@@ -90,7 +90,6 @@ type ThreadSkeletonParts = {
}
const keyExtractor = (item: RowItem) => {
console.log(item._reactKey)
return item._reactKey
}
@@ -258,7 +257,6 @@ export function PostThread({uri}: {uri: string | undefined}) {
fetchedAt,
randomCache,
])
console.log({thread, skeleton})
const error = React.useMemo(() => {
if (AppBskyFeedDefs.isNotFoundPost(thread)) {
@@ -299,6 +297,9 @@ export function PostThread({uri}: {uri: string | undefined}) {
// maintainVisibleContentPosition and onContentSizeChange
// 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) {
// When progressively revealing parents, rendering a placeholder
// 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
}, [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.
// On native, we rely on `maintainVisibleContentPosition` instead.
const didAdjustScrollWeb = useRef<boolean>(false)
@@ -340,11 +343,8 @@ export function PostThread({uri}: {uri: string | undefined}) {
const headerNode = headerRef.current
if (postNode && headerNode) {
let pageY = (postNode as any as Element).getBoundingClientRect().top
console.log({pageY})
pageY -= (headerNode as any as Element).getBoundingClientRect().height
console.log({pageY})
pageY = Math.max(0, pageY)
console.log({pageY})
ref.current?.scrollToOffset({
animated: false,
offset: pageY,
@@ -423,6 +423,8 @@ export function PostThread({uri}: {uri: string | undefined}) {
(skeleton.highlightedPost.ctx.isParentLoading ||
Boolean(skeleton?.parents && skeleton.parents.length > 0))
console.log({hasParents})
const renderItem = ({item, index}: {item: RowItem; index: number}) => {
if (item === REPLY_PROMPT && hasSession) {
return (
+72 -9
View File
@@ -13,9 +13,7 @@ import {cleanError} from '#/lib/strings/errors'
import {makeRecordUri} from '#/lib/strings/url-helpers'
import {isNative} from '#/platform/detection'
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 {useOpenComposer} from '#/lib/hooks/useOpenComposer'
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 {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
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 {CirclePlus_Stroke2_Corner0_Rounded as CirclePlus} from '#/components/icons/CirclePlus'
import * as Layout from '#/components/Layout'
import {ListFooter} from '#/components/Lists'
import * as Menu from '#/components/Menu'
@@ -41,7 +42,7 @@ import {Text} from '#/components/Typography'
const MAINTAIN_VISIBLE_CONTENT_POSITION = {
// 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.
minIndexForVisible: 1,
minIndexForVisible: 0,
}
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'>
@@ -232,13 +233,11 @@ export function Inner({uri}: {uri: string | undefined}) {
// prevPost={prev}
// nextPost={next}
isHighlightedPost={item.ui.isAnchor}
// @ts-expect-error
hasMore={item.value.hasUnhydratedReplies}
hasMore={false} // TODO need to replace this entirely
showChildReplyLine={item.ui.showChildReplyLine}
showParentReplyLine={item.ui.showParentReplyLine}
hasPrecedingItem={
// @ts-expect-error
item.ui.showParentReplyLine || !!item.value.hasUnhydratedParents
item.ui.showParentReplyLine
} // !!hasUnrevealedParents // TODO
overrideBlur={
shownHiddenReplyKinds.has(HiddenReplyKind.Muted) && item.depth > 0
@@ -248,6 +247,68 @@ export function Inner({uri}: {uri: string | undefined}) {
/>
</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') {
return (
<View
@@ -297,6 +358,8 @@ export function Inner({uri}: {uri: string | undefined}) {
return null
}
console.log('PostThreadScreen', data?.anchorIndex)
return (
<>
<Layout.Header.Outer headerRef={headerRef}>
@@ -337,7 +400,7 @@ export function Inner({uri}: {uri: string | undefined}) {
*/
maintainVisibleContentPosition={
isNative // && hasParents // TODO not sure we need this
? MAINTAIN_VISIBLE_CONTENT_POSITION
? { minIndexForVisible: 0 } // MAINTAIN_VISIBLE_CONTENT_POSITION
: undefined
}
desktopFixedHeight