tutu/src/timelines/MediaAttachmentGrid.tsx

144 lines
4.2 KiB
TypeScript
Raw Normal View History

2024-07-14 20:28:44 +08:00
import type { mastodon } from "masto";
import {
type Component,
For,
createEffect,
createRenderEffect,
createSignal,
onCleanup,
onMount,
} from "solid-js";
2024-07-14 20:28:44 +08:00
import { css } from "solid-styled";
import tootStyle from "./toot.module.css";
2024-08-12 21:55:26 +08:00
import MediaViewer from "./MediaViewer";
import { render } from "solid-js/web";
import { useWindowSize } from "@solid-primitives/resize-observer";
import { useStore } from "@nanostores/solid";
import { $settings } from "../settings/stores";
2024-07-14 20:28:44 +08:00
const MediaAttachmentGrid: Component<{
attachments: mastodon.v1.MediaAttachment[];
}> = (props) => {
let rootRef: HTMLElement;
const [viewerIndex, setViewerIndex] = createSignal<number>();
2024-08-05 15:33:00 +08:00
const viewerOpened = () => typeof viewerIndex() !== "undefined";
const windowSize = useWindowSize();
const vh35 = () => Math.floor(windowSize.height * 0.35);
const settings = useStore($settings);
2024-07-14 20:28:44 +08:00
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);
return render(() => {
onCleanup(() => {
document.body.removeChild(container);
});
return (
<MediaViewer
show={viewerOpened()}
index={viewerIndex() || 0}
onIndexUpdated={setViewerIndex}
media={props.attachments}
onClose={() => setViewerIndex()}
/>
);
}, container);
});
const openViewerFor = (index: number) => {
setViewerIndex(index);
2024-07-14 20:28:44 +08:00
};
const columnCount = () => {
if (props.attachments.length === 1) {
return 1;
} else if (props.attachments.length % 2 === 0) {
return 2;
} else {
return 3;
}
};
2024-07-14 20:28:44 +08:00
css`
.attachments {
2024-10-12 19:06:23 +08:00
column-count: ${columnCount().toString()};
2024-07-14 20:28:44 +08:00
}
`;
return (
<section
ref={rootRef!}
class={[tootStyle.tootAttachmentGrp, "attachments"].join(" ")}
onClick={(e) => {
if (e.target !== e.currentTarget) {
e.stopImmediatePropagation();
}
}}
2024-07-14 20:28:44 +08:00
>
<For each={props.attachments}>
{(item, index) => {
// I don't know why mastodon does not return this
// and the condition for it to return this.
// Anyway, It is useless now.
// My hope is the FastAverageColor, but
// we may need better tool to manage the performance impact
// before using this. See #37.
// TODO: use fast average color to extract accent color
const accentColor = item.meta?.colors?.accent;
2024-07-14 20:28:44 +08:00
switch (item.type) {
case "image":
return (
<img
src={item.previewUrl}
width={item.meta?.small?.width}
height={item.meta?.small?.height}
2024-07-14 20:28:44 +08:00
alt={item.description || undefined}
onClick={[openViewerFor, index()]}
loading="lazy"
style={
accentColor ? { "--media-color-accent": accentColor } : {}
}
2024-07-14 20:28:44 +08:00
></img>
);
case "video":
return (
<video
src={item.url || undefined}
autoplay={settings().autoPlayVideos}
playsinline={settings().autoPlayVideos ? true : undefined}
controls
poster={item.previewUrl}
width={item.meta?.small?.width}
height={item.meta?.small?.height}
/>
);
case "gifv":
return (
<video
src={item.url || undefined}
autoplay={settings().autoPlayGIFs}
controls
playsinline /* or safari on iOS will play in full-screen */
loop
poster={item.previewUrl}
width={item.meta?.small?.width}
height={item.meta?.small?.height}
/>
);
2024-09-28 14:39:20 +08:00
2024-07-14 20:28:44 +08:00
case "audio":
case "unknown":
return <div></div>;
}
}}
</For>
</section>
);
};
export default MediaAttachmentGrid;