feat: 1-to-1 message render + web data-lake backend
This commit is contained in:
1 parent
f0afb7ec5b
commit
75425d1bee
110 files changed
+10199
-54
No files matched your search
@@ -0,0 +1,106 @@
|
||||
<script lang="ts">
|
||||
import { visible } from "$lib/actions/visible";
|
||||
import { fetchMedia } from "$lib/api/endpoints";
|
||||
import { type InlineMedia, loadMediaItem, visualKind } from "$lib/api/media";
|
||||
import type { MediaRef } from "$lib/api/types";
|
||||
import Icon from "$lib/components/ui/Icon.svelte";
|
||||
import Spinner from "$lib/components/ui/Spinner.svelte";
|
||||
import { toasts } from "$lib/stores/toasts.svelte";
|
||||
|
||||
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 src={ready.url} muted preload="metadata"></video>
|
||||
<span class="play"><Icon name="large-play" size="2rem" /></span>
|
||||
</button>
|
||||
{:else if ready}
|
||||
<button class="tile" onclick={onopen} type="button">
|
||||
<img src={ready.url} alt="attachment">
|
||||
</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>
|
||||
Reference in new issue
Block a user