Files
plspace/src/routes/Timeline.svelte
T
2026-07-29 12:51:33 +09:00

102 lines
3.1 KiB
Svelte

<script lang="ts">
/**
* A timeline, presented as a blog: "My Blog" (home), "This Server" (local),
* "The Whole Network" (federated), or a hashtag.
*/
import { untrack } from 'svelte'
import type { Status } from '$lib/api/types'
import type { TimelineKind } from '$lib/api/endpoints'
import { useAppServices } from '$lib/app-services'
import { Feed } from '$lib/stores/feed.svelte'
import { instanceDomain } from '$lib/api/endpoints'
import { useTimelineRefresh } from '$lib/timeline-refresh'
import Module from '$components/common/Module.svelte'
import TabBar from '$components/common/TabBar.svelte'
import BlogList from '$components/blog/BlogList.svelte'
import Composer from '$components/blog/Composer.svelte'
interface Props {
kind: TimelineKind
tag?: string
}
let { kind, tag }: Props = $props()
const { endpoints, session } = useAppServices()
const timelineRefresh = useTimelineRefresh()
let feed = $state<Feed<Status>>(new Feed<Status>(async () => ({ items: [], links: {} })))
const domain = $derived(session.host ? instanceDomain(session.instance, session.host) : '')
const title = $derived(
kind === 'home'
? 'My Blog'
: kind === 'local'
? `Blogs on ${domain}`
: kind === 'tag'
? `#${tag}`
: 'The Whole Network',
)
const tabs = $derived([
...(session.signedIn ? [{ label: 'My Blog', href: '#/timeline/home' }] : []),
{ label: 'This Server', href: '#/timeline/local' },
{ label: 'Whole Network', href: '#/timeline/public' },
])
const currentTab = $derived(kind === 'tag' ? '' : `#/timeline/${kind}`)
$effect(() => {
const currentKind = kind
const currentTag = tag
const host = session.host
const authed = session.signedIn
if (!host) return
untrack(() => {
// Home needs a token; fall back rather than showing a 401.
const resolved: TimelineKind = currentKind === 'home' && !authed ? 'local' : currentKind
feed = new Feed<Status>((cursor) =>
endpoints.fetchTimeline(session.api, resolved, cursor, { tag: currentTag }),
)
void feed.reload()
document.title = `${title} | plspace`
})
})
$effect(() => timelineRefresh?.register(() => void feed.reload()))
</script>
<div class="page timeline-page" data-timeline={kind} data-tag={tag ?? ''}>
<h1 class="page-title">{title}</h1>
{#if kind !== 'tag'}
<TabBar {tabs} current={currentTab} label="Timelines" />
{/if}
{#if kind === 'home' && !session.signedIn}
<p class="notice">
You're browsing as a guest, so this is <strong>{domain}</strong>'s local timeline.
<a href="#/login">Sign in</a> to read your own.
</p>
{/if}
<div class="layout--single">
{#if kind === 'home' && session.signedIn}
<Module title="Post a new entry">
<Composer onposted={(status) => feed.prepend(status)} />
</Module>
{/if}
<Module title={title} variant="band">
<BlogList
{feed}
emptyText={kind === 'tag'
? `Nobody has posted with #${tag} that this server knows about.`
: 'There are no Blog Entries yet.'}
longFormDate
/>
</Module>
</div>
</div>