diff --git a/src/timelines/MediaAttachmentGrid.css b/src/timelines/MediaAttachmentGrid.css index a26085c..55bb30d 100644 --- a/src/timelines/MediaAttachmentGrid.css +++ b/src/timelines/MediaAttachmentGrid.css @@ -6,34 +6,23 @@ gap: 4px; contain: layout style; - >.cell { + > :where(img, video) { max-height: 35vh; min-height: 40px; min-width: 40px; + object-fit: contain; max-width: 100%; - contain: strict; - content-visibility: auto; background-color: var(--media-color-accent, var(--tutu-color-surface-d)); border-radius: 2px; border: 1px solid var(--tutu-color-surface-d); transition: outline-width 60ms var(--tutu-anim-curve-std), border-color 60ms var(--tutu-anim-curve-std); + contain: strict; + content-visibility: auto; &:hover, &:focus-visible { outline: 8px solid var(--media-color-accent, var(--tutu-color-surface-d)); border-color: var(--media-color-accent, var(--tutu-color-surface-d)); } - - & > :where(img, video, .sensitive-placeholder) { - object-fit: contain; - width: 100%; - height: 100%; - } - - & > .sensitive-placeholder { - display: flex; - align-items: center; - justify-content: center; - } } } \ No newline at end of file diff --git a/src/timelines/MediaAttachmentGrid.tsx b/src/timelines/MediaAttachmentGrid.tsx index 948bb7b..2b9db57 100644 --- a/src/timelines/MediaAttachmentGrid.tsx +++ b/src/timelines/MediaAttachmentGrid.tsx @@ -1,6 +1,7 @@ import type { mastodon } from "masto"; import { type Component, + For, Index, Match, Switch, @@ -8,7 +9,6 @@ import { createRenderEffect, createSignal, onCleanup, - untrack, } from "solid-js"; import MediaViewer from "./MediaViewer"; import { render } from "solid-js/web"; @@ -21,8 +21,6 @@ import { $settings } from "../settings/stores"; import { averageColorHex } from "../platform/blurhash"; import "./MediaAttachmentGrid.css"; import cardStyle from "../material/cards.module.css"; -import { Preview } from "@suid/icons-material"; -import { IconButton } from "@suid/material"; type ElementSize = { width: number; height: number }; @@ -46,30 +44,23 @@ function constraintedSize( }; } -function isolateCallback(event: Event) { - if (event.target !== event.currentTarget) { - event.stopPropagation(); - } -} - const MediaAttachmentGrid: Component<{ attachments: mastodon.v1.MediaAttachment[]; - sensitive?: boolean; }> = (props) => { const [rootRef, setRootRef] = createSignal(); const [viewerIndex, setViewerIndex] = createSignal(); const viewerOpened = () => typeof viewerIndex() !== "undefined"; const settings = useStore($settings); const windowSize = useWindowSize(); - const [reveal, setReveal] = createSignal([] as number[]); - createRenderEffect(() => { + createRenderEffect((lastDispose?: () => void) => { + lastDispose?.(); const vidx = viewerIndex(); if (typeof vidx === "undefined") return; const container = document.createElement("div"); container.setAttribute("role", "presentation"); document.body.appendChild(container); - const dispose = render(() => { + return render(() => { onCleanup(() => { document.body.removeChild(container); }); @@ -84,8 +75,6 @@ const MediaAttachmentGrid: Component<{ /> ); }, container); - - onCleanup(dispose); }); const openViewerFor = (index: number) => { @@ -138,88 +127,73 @@ const MediaAttachmentGrid: Component<{ accentColor ? { "--media-color-accent": accentColor } : {}, ); }; - - const isReveal = (idx: number) => { - return reveal().includes(idx); - }; - - const addReveal = (idx: number) => { - if (!untrack(() => isReveal(idx))) { - setReveal((x) => [...x, idx]); - } - }; return (
{ + if (e.target !== e.currentTarget) { + e.stopImmediatePropagation(); + } + }} > {(item, index) => { const itemType = () => item().type; return ( - + + + {item().description + + + + + + + + + ); }} diff --git a/src/timelines/RegularToot.tsx b/src/timelines/RegularToot.tsx index 07541ad..5a4faa0 100644 --- a/src/timelines/RegularToot.tsx +++ b/src/timelines/RegularToot.tsx @@ -5,8 +5,6 @@ import { type JSX, Show, createRenderEffect, - createSignal, - type Setter, } from "solid-js"; import tootStyle from "./toot.module.css"; import { formatRelative } from "date-fns"; @@ -33,9 +31,9 @@ import MediaAttachmentGrid from "./MediaAttachmentGrid.js"; import { useDateFnLocale } from "../platform/i18n"; import { canShare, share } from "../platform/share"; import { makeAcctText, useDefaultSession } from "../masto/clients"; -import TootContent from "./toots/TootContent"; -import BoostIcon from "./toots/BoostIcon"; -import PreviewCard from "./toots/PreviewCard"; +import TootContent from "./toot-components/TootContent"; +import BoostIcon from "./toot-components/BoostIcon"; +import PreviewCard from "./toot-components/PreviewCard"; type TootActionGroupProps = { onRetoot?: (value: T) => void; @@ -47,7 +45,7 @@ type TootActionGroupProps = { ) => void; }; -type RegularTootProps = { +type TootCardProps = { status: mastodon.v1.Status; actionable?: boolean; evaluated?: boolean; @@ -202,11 +200,6 @@ export function findElementActionable( return current; } -function onToggleReveal(setValue: Setter, event: Event) { - event.stopPropagation(); - setValue((x) => !x); -} - /** * Component for a toot. * @@ -235,7 +228,7 @@ function onToggleReveal(setValue: Setter, event: Event) { * You can extract the intent from the attributes of the "actionable" element. * The action type is the dataset's `action`. */ -const RegularToot: Component = (props) => { +const RegularToot: Component = (props) => { let rootRef: HTMLElement; const [managed, managedActionGroup, rest] = splitProps( props, @@ -246,7 +239,6 @@ const RegularToot: Component = (props) => { const status = () => managed.status; const toot = () => status().reblog ?? status(); const session = useDefaultSession(); - const [reveal, setReveal] = createSignal(false); css` .reply-sep { @@ -293,7 +285,7 @@ const RegularToot: Component = (props) => { return ( <> -
= (props) => { emojis={toot().emojis} mentions={toot().mentions} class={cardStyle.cardNoPad} - sensitive={toot().sensitive} - spoilerText={toot().spoilerText} - reveal={reveal()} - onToggleReveal={[onToggleReveal, setReveal]} /> - + 0}> - + = (props) => { /> -
+
); }; diff --git a/src/timelines/toots/BoostIcon.css b/src/timelines/toot-components/BoostIcon.css similarity index 100% rename from src/timelines/toots/BoostIcon.css rename to src/timelines/toot-components/BoostIcon.css diff --git a/src/timelines/toots/BoostIcon.tsx b/src/timelines/toot-components/BoostIcon.tsx similarity index 100% rename from src/timelines/toots/BoostIcon.tsx rename to src/timelines/toot-components/BoostIcon.tsx diff --git a/src/timelines/toots/PreviewCard.css b/src/timelines/toot-components/PreviewCard.css similarity index 100% rename from src/timelines/toots/PreviewCard.css rename to src/timelines/toot-components/PreviewCard.css diff --git a/src/timelines/toots/PreviewCard.tsx b/src/timelines/toot-components/PreviewCard.tsx similarity index 100% rename from src/timelines/toots/PreviewCard.tsx rename to src/timelines/toot-components/PreviewCard.tsx diff --git a/src/timelines/toots/TootContent.css b/src/timelines/toot-components/TootContent.css similarity index 92% rename from src/timelines/toots/TootContent.css rename to src/timelines/toot-components/TootContent.css index 79c4ffc..b5e56e6 100644 --- a/src/timelines/toots/TootContent.css +++ b/src/timelines/toot-components/TootContent.css @@ -4,10 +4,6 @@ margin-right: var(--card-pad, 0); line-height: 1.5; - > .content { - display: contents; - } - & * { user-select: text; } diff --git a/src/timelines/toot-components/TootContent.tsx b/src/timelines/toot-components/TootContent.tsx new file mode 100644 index 0000000..fd0dab6 --- /dev/null +++ b/src/timelines/toot-components/TootContent.tsx @@ -0,0 +1,68 @@ +import type { mastodon } from "masto"; +import { + splitProps, + type Component, + type JSX, + createRenderEffect, + createMemo, +} from "solid-js"; +import { resolveCustomEmoji } from "../../masto/toot.js"; +import { makeAcctText, useDefaultSession } from "../../masto/clients"; +import "./TootContent.css"; + +function preventDefault(event: Event) { + event.preventDefault(); +} + +export type TootContentProps = { + source?: string; + emojis?: mastodon.v1.CustomEmoji[]; + mentions: mastodon.v1.StatusMention[]; +} & JSX.HTMLAttributes; + +const TootContent: Component = (oprops) => { + const session = useDefaultSession(); + const [props, rest] = splitProps(oprops, [ + "source", + "emojis", + "mentions", + "class", + ]); + + const clientFinder = createMemo(() => + session() ? makeAcctText(session()!) : undefined, + ); + + return ( +
{ + createRenderEffect(() => { + ref.innerHTML = props.source + ? props.emojis + ? resolveCustomEmoji(props.source, props.emojis) + : props.source + : ""; + }); + + createRenderEffect(() => { + const finder = clientFinder(); + for (const mention of props.mentions) { + const elements = ref.querySelectorAll( + `a[href='${mention.url}']`, + ); + for (const e of elements) { + e.onclick = preventDefault; + e.dataset.action = "acct"; + e.dataset.client = finder; + e.dataset.acctId = mention.id.toString(); + } + } + }); + }} + class={`TootContent ${props.class || ""}`} + {...rest} + >
+ ); +}; + +export default TootContent; diff --git a/src/timelines/toots/TootContent.tsx b/src/timelines/toots/TootContent.tsx deleted file mode 100644 index 7a2286d..0000000 --- a/src/timelines/toots/TootContent.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import type { mastodon } from "masto"; -import { - splitProps, - type Component, - type JSX, - createRenderEffect, - createMemo, - Show, -} from "solid-js"; -import { resolveCustomEmoji } from "../../masto/toot.js"; -import { makeAcctText, useDefaultSession } from "../../masto/clients.js"; -import "./TootContent.css"; -import { Button } from "@suid/material"; -import { createTranslator } from "../../platform/i18n.jsx"; - -function preventDefault(event: Event) { - event.preventDefault(); -} - -export type TootContentProps = JSX.HTMLAttributes & { - source?: string; - emojis?: mastodon.v1.CustomEmoji[]; - mentions: mastodon.v1.StatusMention[]; - sensitive?: boolean; - spoilerText?: string; - reveal?: boolean; - onToggleReveal?: JSX.EventHandlerUnion; -}; - -const TootContent: Component = (oprops) => { - const [t] = createTranslator( - (code) => - import(`./i18n/${code}.json`) as Promise<{ - default: { - cw: string; - }; - }>, - ); - - const session = useDefaultSession(); - const [props, rest] = splitProps(oprops, [ - "source", - "emojis", - "mentions", - "class", - "sensitive", - "spoilerText", - "reveal", - "onToggleReveal", - ]); - - const clientFinder = createMemo(() => - session() ? makeAcctText(session()!) : undefined, - ); - - const shouldRevealContent = () => { - return !props.sensitive || (props.sensitive && props.reveal); - }; - - return ( -
{ - createRenderEffect(() => { - const finder = clientFinder(); - for (const mention of props.mentions) { - const elements = ref.querySelectorAll( - `a[href='${mention.url}']`, - ); - for (const e of elements) { - e.onclick = preventDefault; - e.dataset.action = "acct"; - e.dataset.client = finder; - e.dataset.acctId = mention.id.toString(); - } - } - }); - }} - class={`TootContent ${props.class || ""}`} - {...rest} - > - -
- { - createRenderEffect(() => { - ref.innerHTML = props.spoilerText - ? props.emojis - ? resolveCustomEmoji(props.spoilerText, props.emojis) - : props.spoilerText - : ""; - }); - }} - > - -
-
- -
- createRenderEffect(() => { - ref.innerHTML = props.source - ? props.emojis - ? resolveCustomEmoji(props.source, props.emojis) - : props.source - : ""; - }) - } - >
-
-
- ); -}; - -export default TootContent; diff --git a/src/timelines/toots/i18n/en.json b/src/timelines/toots/i18n/en.json deleted file mode 100644 index 3d75ddd..0000000 --- a/src/timelines/toots/i18n/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "cw": "\"Content Warning\"" -} \ No newline at end of file diff --git a/src/timelines/toots/i18n/zh-Hans.json b/src/timelines/toots/i18n/zh-Hans.json deleted file mode 100644 index 0e64a62..0000000 --- a/src/timelines/toots/i18n/zh-Hans.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "cw": "“内容警告”" -} \ No newline at end of file