48 lines
2.5 KiB
Svelte
48 lines
2.5 KiB
Svelte
<script lang="ts">
|
|
import { onMount } from 'svelte';
|
|
import type { PluginUpdate } from '$lib/plugin-github.js';
|
|
import Card from './primitives/Card.svelte';
|
|
import Button from './primitives/Button.svelte';
|
|
let updates = $state<PluginUpdate[]>([]);
|
|
let checking = $state(false);
|
|
let checked = $state(false);
|
|
let error = $state('');
|
|
const tracked = $derived(updates.filter(u => u.status !== 'untracked'));
|
|
async function check() {
|
|
if (checking) return;
|
|
checking = true; error = '';
|
|
try {
|
|
const response = await fetch('/api/plugins/updates');
|
|
const body = await response.json();
|
|
if (!response.ok) throw new Error(body.error ?? 'Could not check updates.');
|
|
updates = body.updates; checked = true;
|
|
} catch (e) { error = (e as Error).message; }
|
|
finally { checking = false; }
|
|
}
|
|
onMount(() => { void check(); });
|
|
</script>
|
|
|
|
<Card padding="lg">
|
|
<div class="heading"><h2>Release updates</h2><Button size="sm" loading={checking} onclick={check}>Check for updates</Button></div>
|
|
<p>Latest stable GitHub releases. Checks are cached for five minutes.</p>
|
|
{#if error}<p class="error" role="alert">{error}</p>{/if}
|
|
{#if checked && !tracked.length}<p>Import a plugin from GitHub to start tracking its releases.</p>{/if}
|
|
{#each tracked as update (update.pluginId)}
|
|
<div class="row">
|
|
<div><a href={`/plugins/${update.pluginId}`}>{update.name}</a>
|
|
<p>{update.status === 'available' ? `${update.release?.tag} available` : update.status === 'imported' ? `${update.release?.tag} imported — awaiting promotion` : update.status === 'current' ? `${update.release?.tag} promoted` : update.status === 'ahead' ? 'Ahead of the latest stable release' : update.error}</p>
|
|
</div>
|
|
{#if update.status === 'available'}<Button size="sm" href={`/plugins/${update.pluginId}#github-releases`}>Review update</Button>{/if}
|
|
</div>
|
|
{/each}
|
|
</Card>
|
|
|
|
<style>
|
|
.heading, .row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
|
|
h2 { margin: 0; font-size: var(--text-md); font-weight: var(--weight-semibold); }
|
|
p { color: var(--text-secondary); font-size: var(--text-sm); margin: var(--space-2) 0 0; }
|
|
.row { padding-top: var(--space-3); margin-top: var(--space-3); border-top: 1px solid var(--border-subtle); }
|
|
a { color: var(--accent-primary-hover); }
|
|
.error { color: var(--state-danger); }
|
|
</style>
|