From 9499182a8dacfcd7723af5c29c55110c85c68281 Mon Sep 17 00:00:00 2001 From: thislight Date: Mon, 25 Nov 2024 20:08:03 +0800 Subject: [PATCH] ItemSelectionProvider: promote toot expansion state * used in Profile, TrendTimelinePanel, TimelinePanel --- src/profiles/Profile.tsx | 37 +++++++---- src/timelines/Home.tsx | 70 +++++++++++--------- src/timelines/RegularToot.tsx | 1 - src/timelines/TootList.tsx | 45 +++++++------ src/timelines/toots/ItemSelectionProvider.ts | 35 ++++++++++ 5 files changed, 120 insertions(+), 68 deletions(-) create mode 100644 src/timelines/toots/ItemSelectionProvider.ts diff --git a/src/profiles/Profile.tsx b/src/profiles/Profile.tsx index cc29999..5e1ad0c 100644 --- a/src/profiles/Profile.tsx +++ b/src/profiles/Profile.tsx @@ -58,6 +58,10 @@ import Menu, { createManagedMenuState } from "~material/Menu"; import { share } from "~platform/share"; import "./Profile.css"; import { useNavigator } from "~platform/StackedRouter"; +import { + createSingluarItemSelection, + default as ItemSelectionProvider, +} from "../timelines/toots/ItemSelectionProvider"; const Profile: Component = () => { const { pop } = useNavigator(); @@ -70,6 +74,7 @@ const Profile: Component = () => { }>(); const windowSize = useWindowSize(); const time = createTimeSource(); + const [, selectionState] = createSingluarItemSelection(); const menuButId = createUniqueId(); const recentTootListId = createUniqueId(); @@ -499,22 +504,26 @@ const Profile: Component = () => { > - - 0}> + + + 0} + > + + + - - - - + +
{ let panelList: HTMLDivElement; useDocumentTitle("Timelines"); const now = createTimeSource(); + const [, selectionState] = createSingluarItemSelection( + undefined as string | undefined, + ); const settings$ = useStore($settings); @@ -115,7 +122,6 @@ const Home: ParentComponent = (props) => { } }; - css` .tab-panel { overflow: visible auto; @@ -195,40 +201,42 @@ const Home: ParentComponent = (props) => { } > - - -
-
-
- + + + +
+
+
+ +
-
-
-
- +
+
+ +
-
-
-
- +
+
+ +
+
-
-
- - + + + ); diff --git a/src/timelines/RegularToot.tsx b/src/timelines/RegularToot.tsx index 63ef7c1..84c1c38 100644 --- a/src/timelines/RegularToot.tsx +++ b/src/timelines/RegularToot.tsx @@ -4,7 +4,6 @@ import { type Component, type JSX, Show, - createRenderEffect, createSignal, type Setter, createContext, diff --git a/src/timelines/TootList.tsx b/src/timelines/TootList.tsx index b35dcce..35986a5 100644 --- a/src/timelines/TootList.tsx +++ b/src/timelines/TootList.tsx @@ -7,6 +7,7 @@ import { Index, createMemo, For, + createUniqueId, } from "solid-js"; import { type mastodon } from "masto"; import { vibrate } from "~platform/hardware"; @@ -21,6 +22,7 @@ import cardStyle from "~material/cards.module.css"; import type { ThreadNode } from "../masto/timelines"; import { useNavigator } from "~platform/StackedRouter"; import { ANIM_CURVE_STD } from "~material/theme"; +import { useItemSelection } from "./toots/ItemSelectionProvider"; function durationOf(rect0: DOMRect, rect1: DOMRect) { const distancelt = Math.sqrt( @@ -45,6 +47,9 @@ function positionTootInThread(index: number, threadLength: number) { return "middle"; } +/** + * Full-feature toot list. + */ const TootList: Component<{ ref?: Ref; id?: string; @@ -53,7 +58,7 @@ const TootList: Component<{ onChangeToot: (id: string, value: mastodon.v1.Status) => void; }> = (props) => { const session = useDefaultSession(); - const [expandedThreadId, setExpandedThreadId] = createSignal(); + const [isExpanded, setExpanded] = useItemSelection(); const { push } = useNavigator(); const onBookmark = async (status: mastodon.v1.Status) => { @@ -190,7 +195,7 @@ const TootList: Component<{ event.currentTarget, ); - if (actionableElement && checkIsExpended(status)) { + if (actionableElement && isExpanded(event.currentTarget.id)) { if (actionableElement.dataset.action === "acct") { event.stopPropagation(); @@ -214,18 +219,13 @@ const TootList: Component<{ } // else if (!actionableElement || !checkIsExpended(status) || ) - if (status.id !== expandedThreadId()) { - setExpandedThreadId((x) => (x ? undefined : status.id)); + if (!isExpanded(event.currentTarget.id)) { + setExpanded(event.currentTarget.id); } else { openFullScreenToot(status, event.currentTarget as HTMLElement); } }; - const checkIsExpendedId = createSelector(expandedThreadId); - - const checkIsExpended = (status: mastodon.v1.Status) => - checkIsExpendedId(status.id); - const reply = ( status: mastodon.v1.Status, event: { currentTarget: HTMLElement }, @@ -234,10 +234,7 @@ const TootList: Component<{ openFullScreenToot(status, element, true); }; - const vote = async ( - status: mastodon.v1.Status, - votes: readonly number[] - ) => { + const vote = async (status: mastodon.v1.Status, votes: readonly number[]) => { const client = session()?.client; if (!client) return; @@ -271,13 +268,15 @@ const TootList: Component<{ return

Oops: {String(err)}

; }} > - +
{(threadId, threadIdx) => { @@ -291,11 +290,13 @@ const TootList: Component<{ {(threadNode, index) => { const status = () => threadNode().value; + const id = createUniqueId(); return ( 1 @@ -303,8 +304,8 @@ const TootList: Component<{ : undefined } class={cardStyle.card} - evaluated={checkIsExpended(status())} - actionable={checkIsExpended(status())} + evaluated={isExpanded(id)} + actionable={isExpanded(id)} onClick={[onItemClick, status()]} /> ); diff --git a/src/timelines/toots/ItemSelectionProvider.ts b/src/timelines/toots/ItemSelectionProvider.ts new file mode 100644 index 0000000..e19e024 --- /dev/null +++ b/src/timelines/toots/ItemSelectionProvider.ts @@ -0,0 +1,35 @@ +import { + createContext, + createSelector, + createSignal, + useContext, + type Accessor, +} from "solid-js"; + +export type ItemSelectionState = [(value: T) => boolean, (value: T) => void]; + +const ItemSelectionContext = /* @__PURE__ */ createContext< + ItemSelectionState +>([() => false, () => undefined]); + +export function createSingluarItemSelection( + intial?: T, +): readonly [Accessor, ItemSelectionState] { + const [value, setValue] = createSignal(intial); + + const select = createSelector(value, (a, b) => + typeof b !== "undefined" ? a === b : false, + ); + + const toggle = (v: T | undefined) => { + setValue((o) => (o ? undefined : v)); + }; + + return [value, [select, toggle]]; +} + +export function useItemSelection() { + return useContext(ItemSelectionContext); +} + +export default ItemSelectionContext.Provider;