pics timeline

This commit is contained in:
Moon.eth
2026-07-29 11:04:22 +09:00
parent dd88daaf1c
commit 2f0c8821f3
5 changed files with 279 additions and 7 deletions
+120
View File
@@ -0,0 +1,120 @@
<script lang="ts">
/**
* Image attachments from an account timeline, presented like a MySpace photo
* stream. Videos/audio stay in the Blog; reposted pictures are not somebody's
* own Pics.
*/
import type { MediaAttachment, Status } from '$lib/api/types'
import type { Feed } from '$lib/stores/feed.svelte'
import { toPlainText } from '$lib/util/html'
import { stampDate } from '$lib/util/time'
import Pager from '../common/Pager.svelte'
interface Props {
feed: Feed<Status>
ownerName: string
}
interface Picture {
key: string
media: MediaAttachment
status: Status
caption: string
}
let { feed, ownerName }: Props = $props()
let revealed = $state<Record<string, boolean>>({})
const pictures = $derived.by<Picture[]>(() =>
feed.items.flatMap((status) => {
if (status.reblog) return []
const entryCaption = toPlainText(status.content)
return status.media_attachments
.filter((media) => media.type === 'image')
.map((media) => ({
key: `${status.id}:${media.id}`,
media,
status,
caption: media.description?.trim() || entryCaption,
}))
}),
)
function toggle(key: string): void {
revealed = { ...revealed, [key]: !revealed[key] }
}
</script>
<p class="pic-stream-intro">
Pictures from {ownerName}'s Blog Entries. Click a picture to view the full-size original.
</p>
{#if !feed.initialized && feed.loading}
<p class="loading-note">Loading pictures&hellip;</p>
{:else if pictures.length === 0 && feed.items.length > 0}
<p class="empty-note">
No image attachments in the entries loaded so far.
</p>
{:else}
<ul class="pic-stream">
{#each pictures as picture (picture.key)}
{@const hidden = picture.status.sensitive && !revealed[picture.key]}
<li
class="pic-card"
data-status-id={picture.status.id}
data-sensitive={picture.status.sensitive ? 'true' : 'false'}
data-revealed={hidden ? 'false' : 'true'}
>
<figure class="pic-card-figure">
<a
class="pic-card-image-link"
href={picture.media.url}
target="_blank"
rel="noopener noreferrer"
aria-label={hidden ? 'Sensitive picture; reveal it before opening' : 'View full-size picture'}
aria-disabled={hidden ? 'true' : undefined}
onclick={hidden ? (event) => event.preventDefault() : undefined}
>
<img
class="pic-card-image"
src={picture.media.preview_url ?? picture.media.url}
alt={picture.media.description ?? ''}
loading="lazy"
decoding="async"
/>
</a>
{#if picture.status.sensitive}
<button
type="button"
class="button button--small pic-card-reveal"
onclick={() => toggle(picture.key)}
>
{hidden ? 'Show' : 'Hide'} sensitive picture
</button>
{/if}
<figcaption class="pic-card-caption">
{#if hidden}
<span class="pic-card-description">
{picture.status.spoiler_text || 'Sensitive picture'}
</span>
{:else if picture.caption}
<span class="pic-card-description">{picture.caption}</span>
{/if}
<a class="pic-card-entry-link" href={`#/blog/${picture.status.id}`}>
Posted {stampDate(picture.status.created_at)} &middot; view entry
</a>
</figcaption>
</figure>
</li>
{/each}
</ul>
{/if}
<Pager
{feed}
label="View More Pictures"
emptyText={`${ownerName} hasn't posted any pictures yet.`}
endText={pictures.length > 0 ? 'Thats the whole picture stream.' : ''}
/>