2024-07-14 20:28:44 +08:00
|
|
|
import type { mastodon } from "masto";
|
2024-09-14 20:43:44 +08:00
|
|
|
import {
|
|
|
|
type Component,
|
|
|
|
For,
|
2024-09-14 21:17:03 +08:00
|
|
|
createEffect,
|
|
|
|
createRenderEffect,
|
2024-09-14 20:43:44 +08:00
|
|
|
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";
|
2024-09-14 20:21:19 +08:00
|
|
|
import { render } from "solid-js/web";
|
2024-09-23 15:43:49 +08:00
|
|
|
import { useWindowSize } from "@solid-primitives/resize-observer";
|
2024-10-09 18:45:19 +08:00
|
|
|
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";
|
2024-09-23 15:43:49 +08:00
|
|
|
const windowSize = useWindowSize();
|
|
|
|
const vh35 = () => Math.floor(windowSize.height * 0.35);
|
2024-10-09 18:45:19 +08:00
|
|
|
const settings = useStore($settings);
|
2024-07-14 20:28:44 +08:00
|
|
|
|
2024-09-14 21:17:03 +08:00
|
|
|
createRenderEffect((lastDispose?: () => void) => {
|
|
|
|
lastDispose?.();
|
|
|
|
const vidx = viewerIndex();
|
|
|
|
if (typeof vidx === "undefined") return;
|
2024-09-14 20:21:19 +08:00
|
|
|
const container = document.createElement("div");
|
|
|
|
container.setAttribute("role", "presentation");
|
|
|
|
document.body.appendChild(container);
|
2024-09-14 21:17:03 +08:00
|
|
|
return render(() => {
|
|
|
|
onCleanup(() => {
|
|
|
|
document.body.removeChild(container);
|
|
|
|
});
|
2024-09-14 20:43:44 +08:00
|
|
|
|
2024-09-14 20:21:19 +08:00
|
|
|
return (
|
|
|
|
<MediaViewer
|
|
|
|
show={viewerOpened()}
|
|
|
|
index={viewerIndex() || 0}
|
|
|
|
onIndexUpdated={setViewerIndex}
|
|
|
|
media={props.attachments}
|
2024-09-14 21:17:03 +08:00
|
|
|
onClose={() => setViewerIndex()}
|
2024-09-14 20:21:19 +08:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
}, container);
|
2024-09-14 21:17:03 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
const openViewerFor = (index: number) => {
|
|
|
|
setViewerIndex(index);
|
2024-07-14 20:28:44 +08:00
|
|
|
};
|
|
|
|
|
2024-10-08 17:43:33 +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(" ")}
|
2024-10-09 18:45:19 +08:00
|
|
|
onClick={(e) => {
|
|
|
|
if (e.target !== e.currentTarget) {
|
|
|
|
e.stopImmediatePropagation();
|
|
|
|
}
|
|
|
|
}}
|
2024-07-14 20:28:44 +08:00
|
|
|
>
|
|
|
|
<For each={props.attachments}>
|
|
|
|
{(item, index) => {
|
2024-10-27 23:57:10 +08:00
|
|
|
// 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}
|
2024-10-27 23:57:10 +08:00
|
|
|
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"
|
2024-10-27 23:57:10 +08:00
|
|
|
style={
|
|
|
|
accentColor ? { "--media-color-accent": accentColor } : {}
|
|
|
|
}
|
2024-07-14 20:28:44 +08:00
|
|
|
></img>
|
|
|
|
);
|
|
|
|
case "video":
|
2024-09-23 15:43:49 +08:00
|
|
|
return (
|
|
|
|
<video
|
|
|
|
src={item.url || undefined}
|
2024-10-09 18:45:19 +08:00
|
|
|
autoplay={settings().autoPlayVideos}
|
|
|
|
playsinline={settings().autoPlayVideos ? true : undefined}
|
2024-09-25 17:37:11 +08:00
|
|
|
controls
|
2024-10-09 18:45:19 +08:00
|
|
|
poster={item.previewUrl}
|
2024-10-27 23:57:10 +08:00
|
|
|
width={item.meta?.small?.width}
|
|
|
|
height={item.meta?.small?.height}
|
2024-09-23 15:43:49 +08:00
|
|
|
/>
|
|
|
|
);
|
2024-10-09 18:45:19 +08:00
|
|
|
case "gifv":
|
2024-10-08 17:43:33 +08:00
|
|
|
return (
|
|
|
|
<video
|
|
|
|
src={item.url || undefined}
|
2024-10-09 18:45:19 +08:00
|
|
|
autoplay={settings().autoPlayGIFs}
|
2024-10-08 17:43:33 +08:00
|
|
|
controls
|
|
|
|
playsinline /* or safari on iOS will play in full-screen */
|
|
|
|
loop
|
2024-10-09 18:45:19 +08:00
|
|
|
poster={item.previewUrl}
|
2024-10-27 23:57:10 +08:00
|
|
|
width={item.meta?.small?.width}
|
|
|
|
height={item.meta?.small?.height}
|
2024-10-08 17:43:33 +08:00
|
|
|
/>
|
|
|
|
);
|
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;
|