diff --git a/src/masto/timelines.ts b/src/masto/timelines.ts index 556bc83..f7bb5a3 100644 --- a/src/masto/timelines.ts +++ b/src/masto/timelines.ts @@ -18,9 +18,9 @@ type Timeline = { type TimelineParamsOf = T extends Timeline ? P : never; -function createControlsForLookup( - lookup: ReactiveMap>, -) { +export type ThreadNode = TreeNode; + +function createControlsForLookup(lookup: ReactiveMap) { return { get(id: string) { return lookup.get(id); @@ -37,7 +37,7 @@ function createControlsForLookup( set(id: string, value: mastodon.v1.Status) { const node = untrack(() => lookup.get(id)); if (!node) return; - lookup.set(id, {...node, value}); + lookup.set(id, { ...node, value }); }, }; } @@ -45,7 +45,7 @@ function createControlsForLookup( export function createTimelineControlsForArray( status: () => mastodon.v1.Status[] | undefined, ): TimelineControls { - const lookup = new ReactiveMap>(); + const lookup = new ReactiveMap(); const [threads, setThreads] = createStore([] as mastodon.v1.Status["id"][]); @@ -55,22 +55,24 @@ export function createTimelineControlsForArray( }); if (!nls) return; - setThreads([]); - lookup.clear(); + batch(() => { + setThreads([]); + lookup.clear(); - const existence = [] as boolean[]; + for (const status of nls) { + lookup.set(status.id, { + value: status, + }); + } + }); - for (const [idx, status] of nls.entries()) { - existence[idx] = !!untrack(() => lookup.get(status.id)); - lookup.set(status.id, { - value: status, - }); - } + untrack(() => { + for (const status of nls) { + const node = lookup.get(status.id)!; + const parent = status.inReplyToId + ? lookup.get(status.inReplyToId) + : undefined; - for (const status of nls) { - const node = untrack(() => lookup.get(status.id))!; - if (status.inReplyToId) { - const parent = lookup.get(status.inReplyToId); if (parent) { const children = parent.children ?? []; if (!children.find((x) => x.value.id == status.id)) { @@ -80,12 +82,13 @@ export function createTimelineControlsForArray( node.parent = parent; } } - } + }); - const newThreads = nls - .filter((x, i) => !existence[i]) - .map((x) => x.id) - .filter((id) => (lookup.get(id)!.children?.length ?? 0) === 0); + const newThreads = untrack(() => + nls + .map((x) => x.id) + .filter((id) => (lookup.get(id)!.children?.length ?? 0) === 0), + ); setThreads(newThreads); }); @@ -269,32 +272,34 @@ export function createTimeline< return; } - if (chk.rebuilt) { - lookup.clear(); - setThreads([]); - } - const existence = [] as boolean[]; - for (const [idx, status] of chk.chunk.entries()) { - existence[idx] = !!untrack(() => lookup.get(status.id)); - lookup.set(status.id, { - value: status, - }); - } + batch(() => { + if (chk.rebuilt) { + lookup.clear(); + setThreads([]); + } + + for (const [idx, status] of chk.chunk.entries()) { + existence[idx] = !!untrack(() => lookup.get(status.id)); + lookup.set(status.id, { + value: status, + }); + } + }); for (const status of chk.chunk) { const node = untrack(() => lookup.get(status.id))!; - if (status.inReplyToId) { - const parent = lookup.get(status.inReplyToId); - if (parent) { - const children = parent.children ?? []; - if (!children.find((x) => x.value.id == status.id)) { - children.push(node); - } - parent.children = children; - node.parent = parent; + const parent = untrack(() => + status.inReplyToId ? lookup.get(status.inReplyToId) : undefined, + ); + if (parent) { + const children = parent.children ?? []; + if (!children.find((x) => x.value.id == status.id)) { + children.push(node); } + parent.children = children; + node.parent = parent; } } diff --git a/src/timelines/TootBottomSheet.tsx b/src/timelines/TootBottomSheet.tsx index 4cfb16f..c2b29e3 100644 --- a/src/timelines/TootBottomSheet.tsx +++ b/src/timelines/TootBottomSheet.tsx @@ -58,9 +58,10 @@ const TootBottomSheet: Component = (props) => { }, ); - const toot = () => catchError(remoteToot, (error) => { - console.error(error) - }) ?? getCache(acctText(), params.id); + const toot = () => + catchError(remoteToot, (error) => { + console.error(error); + }) ?? getCache(acctText(), params.id); createEffect((lastTootId?: string) => { const tootId = toot()?.id; diff --git a/src/timelines/TootList.tsx b/src/timelines/TootList.tsx index 667212e..48a7836 100644 --- a/src/timelines/TootList.tsx +++ b/src/timelines/TootList.tsx @@ -20,6 +20,8 @@ import RegularToot, { findRootToot, } from "./RegularToot"; import cardStyle from "../material/cards.module.css"; +import type { ReactiveMap } from "@solid-primitives/map"; +import type { ThreadNode } from "../masto/timelines"; function positionTootInThread(index: number, threadLength: number) { if (index === 0) { @@ -34,7 +36,7 @@ const TootList: Component<{ ref?: Ref; id?: string; threads: readonly string[]; - onUnknownThread: (id: string) => { value: mastodon.v1.Status }[] | undefined; + onUnknownThread: (id: string) => ThreadNode[] | undefined; onChangeToot: (id: string, value: mastodon.v1.Status) => void; }> = (props) => { const session = useDefaultSession(); @@ -71,7 +73,7 @@ const TootList: Component<{ }); } - const result = reblogged + /* const result = reblogged ? await client.v1.statuses.$select(status.id).unreblog() : (await client.v1.statuses.$select(status.id).reblog()).reblog!; @@ -82,13 +84,15 @@ const TootList: Component<{ }); } else { props.onChangeToot(status.id, result); - } + } */ }; const toggleFavourite = async (status: mastodon.v1.Status) => { const client = session()?.client; if (!client) return; const ovalue = status.favourited; + props.onChangeToot(status.id, { ...status, favourited: !ovalue }); + const result = ovalue ? await client.v1.statuses.$select(status.id).unfavourite() : await client.v1.statuses.$select(status.id).favourite(); @@ -170,11 +174,12 @@ const TootList: Component<{ const checkIsExpended = (status: mastodon.v1.Status) => checkIsExpendedId(status.id); - const getPath = (itemId: string) => { - return props - .onUnknownThread(itemId)! - .reverse() - .map((x) => x.value); + const reply = ( + status: mastodon.v1.Status, + event: { currentTarget: HTMLElement }, + ) => { + const element = findRootToot(event.currentTarget); + openFullScreenToot(status, element, true); }; return ( @@ -184,38 +189,46 @@ const TootList: Component<{ }} >
- - {(itemId) => { - const toots = createMemo(() => getPath(itemId)); + + {(threadId, threadIdx) => { + const thread = createMemo(() => + props.onUnknownThread(threadId())?.reverse(), + ); + + const threadLength = () => thread()?.length ?? 0; + return ( - - {(status, index) => ( - 1 - ? positionTootInThread(index, toots().length) - : undefined - } - class={cardStyle.card} - evaluated={checkIsExpended(status())} - actionable={checkIsExpended(status())} - onBookmark={onBookmark} - onRetoot={toggleBoost} - onFavourite={toggleFavourite} - onReply={(status, event) => { - const element = findRootToot(event.currentTarget); - openFullScreenToot(status, element, true); - }} - onClick={[onItemClick, status()]} - /> - )} + + {(threadNode, index) => { + const status = () => threadNode().value; + + return ( + 1 + ? positionTootInThread(index, threadLength()) + : undefined + } + class={cardStyle.card} + evaluated={checkIsExpended(status())} + actionable={checkIsExpended(status())} + onBookmark={onBookmark} + onRetoot={toggleBoost} + onFavourite={toggleFavourite} + onReply={reply} + onClick={[onItemClick, status()]} + /> + ); + }} ); }} - +
); diff --git a/src/timelines/TrendTimelinePanel.tsx b/src/timelines/TrendTimelinePanel.tsx index e34edcf..1691ec8 100644 --- a/src/timelines/TrendTimelinePanel.tsx +++ b/src/timelines/TrendTimelinePanel.tsx @@ -1,18 +1,14 @@ import { Component, - For, createSignal, Match, Switch as JsSwitch, ErrorBoundary, - createSelector, } from "solid-js"; import { type mastodon } from "masto"; import { Button } from "@suid/material"; import { createTimelineSnapshot } from "../masto/timelines.js"; -import { vibrate } from "../platform/hardware.js"; import PullDownToRefresh from "./PullDownToRefresh.jsx"; -import Thread from "./Thread.jsx"; import TootList from "./TootList.jsx"; const TrendTimelinePanel: Component<{ @@ -25,11 +21,10 @@ const TrendTimelinePanel: Component<{ ) => void; }> = (props) => { const [scrollLinked, setScrollLinked] = createSignal(); - const [timeline, snapshot, { refetch: refetchTimeline }] = - createTimelineSnapshot( - () => props.client.v1.trends.statuses, - () => ({ limit: 120 }), - ); + const [tl, snapshot, { refetch: refetchTimeline }] = createTimelineSnapshot( + () => props.client.v1.trends.statuses, + () => ({ limit: 120 }), + ); return (