From dafc2c47a8426a791d4377262056164290a6adc6 Mon Sep 17 00:00:00 2001 From: thislight Date: Mon, 25 Nov 2024 18:00:28 +0800 Subject: [PATCH 1/3] docs/optimizing: fix typo --- docs/optimizing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/optimizing.md b/docs/optimizing.md index 920c157..07f8e6f 100644 --- a/docs/optimizing.md +++ b/docs/optimizing.md @@ -33,7 +33,7 @@ Don't choose algorithm solely on the time complexity. GUI app needs smooth, not But it comes with cost. Modern browsers are already very smart on rendering. Using `contain`, you are trading onething off for another: - `layout` affects the reflow. This property usually won't make large change: mainline browsers already can incrementally reflow. -- `style` affacts the style computation, is automatically enabled when using `container` property. Usually won't make large change too, unless you frequently change the styles (and/or your stylesheet is large and/or with complex selectors). +- `style` affects the style computation, is automatically enabled when using `container` property. Usually won't make large change too, unless you frequently change the styles (and/or your stylesheet is large and/or with complex selectors), containing the computation may help performance. - `paint` affects the shading, the pixel-filling process. This is useful - the shading is resource-heavy - but the browser may need more buffers and more time to compose the final frame. - This containment may increase memory usage. - `size` says the size is not affected by outside elements and is defined. It hints the user agent can use the pre-defined size and/or cache the computed size (with `auto` keyword). From 9499182a8dacfcd7723af5c29c55110c85c68281 Mon Sep 17 00:00:00 2001 From: thislight Date: Mon, 25 Nov 2024 20:08:03 +0800 Subject: [PATCH 2/3] 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; From ad0076156b14913e2665b0ea41feeae26b2342ac Mon Sep 17 00:00:00 2001 From: thislight Date: Mon, 25 Nov 2024 20:13:02 +0800 Subject: [PATCH 3/3] TootAuthorGroup: visually identify clickable --- src/timelines/toots/TootAuthorGroup.css | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/timelines/toots/TootAuthorGroup.css b/src/timelines/toots/TootAuthorGroup.css index 1d62d16..e52794f 100644 --- a/src/timelines/toots/TootAuthorGroup.css +++ b/src/timelines/toots/TootAuthorGroup.css @@ -10,7 +10,7 @@ } } -.TootAuthorGroup > .name-grp { +.TootAuthorGroup>.name-grp { display: grid; grid-template-columns: 1fr auto; color: var(--tutu-color-secondary-text-on-surface); @@ -22,18 +22,16 @@ >time { text-align: end; } - - &:hover { - > .name-primary { - text-decoration: underline; - } - } } -.TootAuthorGroup > .name-grp > .name-primary { +.RegularToot.expanded .TootAuthorGroup>.name-grp>.name-primary { + text-decoration: underline; +} + +.TootAuthorGroup>.name-grp>.name-primary { color: var(--tutu-color-on-surface); - > .acct-mark { + >.acct-mark { font-size: 1.2em; color: var(--tutu-color-secondary-text-on-surface); vertical-align: sub; @@ -41,7 +39,7 @@ } } -.TootAuthorGroup > .avatar { +.TootAuthorGroup>.avatar { width: calc(var(--toot-avatar-size, 40px) - 1px); aspect-ratio: 1/1; object-fit: contain;