diff --git a/src/timelines/MediaAttachmentGrid.tsx b/src/timelines/MediaAttachmentGrid.tsx index 2ae56be..dcc5af1 100644 --- a/src/timelines/MediaAttachmentGrid.tsx +++ b/src/timelines/MediaAttachmentGrid.tsx @@ -2,29 +2,52 @@ import type { mastodon } from "masto"; import { type Component, For, - createEffect, + createMemo, createRenderEffect, createSignal, onCleanup, - onMount, } from "solid-js"; import { css } from "solid-styled"; import tootStyle from "./toot.module.css"; import MediaViewer from "./MediaViewer"; import { render } from "solid-js/web"; -import { useWindowSize } from "@solid-primitives/resize-observer"; +import { + createElementSize, + useWindowSize, +} from "@solid-primitives/resize-observer"; import { useStore } from "@nanostores/solid"; import { $settings } from "../settings/stores"; +type ElementSize = { width: number; height: number }; + +function constraintedSize( + { width: owidth, height: oheight }: Readonly, // originalSize + { width: mwidth, height: mheight }: Readonly>, // modifier + { width: maxWidth, height: maxHeight }: Readonly, // maxSize +) { + const ySize = owidth + (mwidth ?? 0); + const yScale = ySize > maxWidth ? ySize / maxWidth : 1; + const xSize = oheight + (mheight ?? 0); + const xScale = xSize > maxHeight ? xSize / maxHeight : 1; + + const maxScale = Math.max(yScale, xScale); + const scaledWidth = owidth / maxScale; + const scaledHeight = oheight / maxScale; + + return { + width: scaledWidth, + height: scaledHeight, + }; +} + const MediaAttachmentGrid: Component<{ attachments: mastodon.v1.MediaAttachment[]; }> = (props) => { - let rootRef: HTMLElement; + const [rootRef, setRootRef] = createSignal(); const [viewerIndex, setViewerIndex] = createSignal(); const viewerOpened = () => typeof viewerIndex() !== "undefined"; - const windowSize = useWindowSize(); - const vh35 = () => Math.floor(windowSize.height * 0.35); const settings = useStore($settings); + const windowSize = useWindowSize(); createRenderEffect((lastDispose?: () => void) => { lastDispose?.(); @@ -64,6 +87,22 @@ const MediaAttachmentGrid: Component<{ } }; + const rawElementSize = createElementSize(rootRef); + + const elementWidth = () => rawElementSize.width; + + const itemMaxSize = createMemo(() => { + const ewidth = elementWidth(); + const width = ewidth + ? (ewidth - (columnCount() - 1) * 4) / columnCount() + : 1; + + return { + height: windowSize.height * 0.35, + width, + }; + }); + css` .attachments { column-count: ${columnCount().toString()}; @@ -71,7 +110,7 @@ const MediaAttachmentGrid: Component<{ `; return (
{ if (e.target !== e.currentTarget) { @@ -89,19 +128,33 @@ const MediaAttachmentGrid: Component<{ // before using this. See #37. // TODO: use fast average color to extract accent color const accentColor = item.meta?.colors?.accent; + const { width, height } = item.meta?.small || {}; + + const size = () => { + return constraintedSize( + item.meta?.small || { width: 1, height: 1 }, + { width: 2, height: 2 }, + itemMaxSize(), + ); + }; + switch (item.type) { case "image": return ( {item.description ); case "video": @@ -112,8 +165,8 @@ const MediaAttachmentGrid: Component<{ playsinline={settings().autoPlayVideos ? true : undefined} controls poster={item.previewUrl} - width={item.meta?.small?.width} - height={item.meta?.small?.height} + width={width} + height={height} /> ); case "gifv": @@ -125,8 +178,8 @@ const MediaAttachmentGrid: Component<{ playsinline /* or safari on iOS will play in full-screen */ loop poster={item.previewUrl} - width={item.meta?.small?.width} - height={item.meta?.small?.height} + width={width} + height={height} /> ); diff --git a/src/timelines/toot.module.css b/src/timelines/toot.module.css index f4e458d..e89c1f6 100644 --- a/src/timelines/toot.module.css +++ b/src/timelines/toot.module.css @@ -223,6 +223,7 @@ } .tootAttachmentGrp { + /* Note: MeidaAttachmentGrid has hard-coded layout calcalation */ composes: cardNoPad from "../material/cards.module.css"; margin-top: 1em; margin-left: calc(var(--card-pad, 0) + var(--toot-avatar-size, 0) + 8px); @@ -233,14 +234,12 @@ max-height: 35vh; min-height: 40px; min-width: 40px; - width: auto; - height: auto; object-fit: contain; max-width: 100%; background-color: var(--tutu-color-surface-d); border-radius: 2px; - border: 1px solid var(--tutu-color-on-surface-d); - transition: outline-width 60ms var(--tutu-anim-curve-std), z-index 60ms var(--tutu-anim-curve-std); + 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); &:hover, &:focus-visible { @@ -248,6 +247,7 @@ /* but our infra is not prepared for this. The average color thing is slow */ /* and we need further managing to control its performance impact. */ outline: 8px solid var(--media-color-accent, var(--tutu-color-surface-d)); + border-color: transparent; } } }