118 lines
2.7 KiB
Svelte
118 lines
2.7 KiB
Svelte
<script lang="ts">
|
|
import { visible } from "#lib/actions/visible.js";
|
|
import { fetchMedia } from "#lib/api/endpoints.js";
|
|
import {
|
|
type InlineMedia,
|
|
loadMediaItem,
|
|
visualKind,
|
|
} from "#lib/api/media.js";
|
|
import type { MediaRef } from "#lib/api/types.js";
|
|
import Icon from "#lib/components/ui/Icon.svelte";
|
|
import Spinner from "#lib/components/ui/Spinner.svelte";
|
|
import { poster } from "#lib/media/poster.js";
|
|
import { toasts } from "#lib/stores/toasts.svelte.js";
|
|
|
|
interface Props {
|
|
chatId: number;
|
|
media: MediaRef;
|
|
onopen: () => void;
|
|
}
|
|
|
|
let { media, chatId, onopen }: Props = $props();
|
|
|
|
let loaded = $state(false);
|
|
let inline = $state<InlineMedia | null>(null);
|
|
let queuing = $state(false);
|
|
|
|
const ready = $derived(inline?.state === "ready" ? inline : null);
|
|
const isVideo = $derived(ready ? visualKind(ready.kind) === "video" : false);
|
|
|
|
async function start() {
|
|
inline = await loadMediaItem(media);
|
|
loaded = true;
|
|
}
|
|
|
|
async function queue() {
|
|
if (queuing) {
|
|
return;
|
|
}
|
|
queuing = true;
|
|
try {
|
|
await fetchMedia(chatId, media.message_id);
|
|
toasts.success("Download queued");
|
|
} catch {
|
|
toasts.error("Failed to queue download");
|
|
} finally {
|
|
queuing = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<div class="AlbumTile" use:visible={start}>
|
|
{#if ready && isVideo}
|
|
<button class="tile" onclick={onopen} type="button">
|
|
<video
|
|
muted
|
|
playsinline
|
|
preload="metadata"
|
|
src={ready.url}
|
|
use:poster
|
|
></video>
|
|
<span class="play"><Icon name="large-play" size="2rem" /></span>
|
|
</button>
|
|
{:else if ready}
|
|
<button class="tile" onclick={onopen} type="button">
|
|
<img alt="attachment" src={ready.url}>
|
|
</button>
|
|
{:else if inline?.state === "not-downloaded"}
|
|
<button class="tile placeholder" onclick={queue} type="button">
|
|
<Icon name={queuing ? "timer" : "download"} size="1.5rem" />
|
|
</button>
|
|
{:else if loaded}
|
|
<div class="tile placeholder"><Icon name="photo" size="1.5rem" /></div>
|
|
{:else}
|
|
<div class="tile placeholder"><Spinner /></div>
|
|
{/if}
|
|
</div>
|
|
|
|
<style lang="scss">
|
|
.AlbumTile {
|
|
aspect-ratio: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.tile {
|
|
cursor: pointer;
|
|
position: relative;
|
|
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: 0;
|
|
border: 0;
|
|
|
|
background-color: var(--color-default-shadow);
|
|
|
|
img,
|
|
video {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
}
|
|
|
|
.placeholder {
|
|
color: var(--color-primary);
|
|
background-color: var(--color-primary-tint);
|
|
}
|
|
|
|
.play {
|
|
position: absolute;
|
|
color: var(--color-white);
|
|
filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.5));
|
|
}
|
|
</style>
|