This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { dev } from '$app/environment';
|
||||
import updateHistoryMarkdown from '../../../UPDATES.md?raw';
|
||||
import { renderReleaseMarkdown } from '$lib/release-notes/markdown';
|
||||
import { RELEASES_PAGE_URL } from '$lib/release-notes/release-note-service';
|
||||
import { currentAppVersion, releaseNotes } from '$lib/release-notes/release-note-store';
|
||||
import { versionTagIsNewer } from '$lib/release-notes/version';
|
||||
import { parseUpdateHistoryDocument } from '$lib/update-history-document.ts';
|
||||
|
||||
let previewUpdate = false;
|
||||
const updateHistoryDocument = parseUpdateHistoryDocument(updateHistoryMarkdown);
|
||||
|
||||
onMount(() => {
|
||||
previewUpdate = dev && new URLSearchParams(window.location.search).get('previewUpdate') === '1';
|
||||
@@ -16,6 +18,20 @@
|
||||
$: latestRelease = $releaseNotes.releases[0] ?? null;
|
||||
$: updateAvailable = latestRelease !== null && (previewUpdate || versionTagIsNewer(latestRelease.tagName, currentAppVersion));
|
||||
|
||||
function updatedDateTime(value: string | null): string {
|
||||
if (!value) return '시각 미등록';
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return value;
|
||||
return new Intl.DateTimeFormat('ko-KR', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
function publishedDate(value: string): string {
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return value;
|
||||
@@ -27,25 +43,22 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $releaseNotes.status === 'available' && latestRelease}
|
||||
<aside class:available={updateAvailable} class="update-notice" aria-live="polite">
|
||||
<aside class:available={updateAvailable} class="update-notice" aria-live="polite">
|
||||
<div class="notice-header">
|
||||
<div class="version-summary">
|
||||
<span class="label">현재 버전</span>
|
||||
<strong>v{currentAppVersion.replace(/^v/i, '')}</strong>
|
||||
<span class="label">최근 업데이트</span>
|
||||
<strong><time datetime={updateHistoryDocument.updatedAt ?? undefined}>{updatedDateTime(updateHistoryDocument.updatedAt)}</time></strong>
|
||||
{#if previewUpdate}
|
||||
<span class="status"><b>업데이트 알림 미리보기</b></span>
|
||||
{:else if updateAvailable}
|
||||
<span class="status"><b>{latestRelease.tagName}</b> 업데이트를 사용할 수 있습니다.</span>
|
||||
{:else}
|
||||
<span class="status">최신 버전입니다.</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<a class="action" href={RELEASES_PAGE_URL} target="_blank" rel="noreferrer">업데이트 내역 확인 <span aria-hidden="true">→</span></a>
|
||||
<a class="action" href="/updates">업데이트 내역 확인 <span aria-hidden="true">→</span></a>
|
||||
</div>
|
||||
|
||||
{#if updateAvailable}
|
||||
{#if updateAvailable && latestRelease}
|
||||
<section class="latest-note" aria-labelledby="latest-release-title">
|
||||
<header>
|
||||
<div>
|
||||
@@ -62,8 +75,7 @@
|
||||
{/if}
|
||||
</section>
|
||||
{/if}
|
||||
</aside>
|
||||
{/if}
|
||||
</aside>
|
||||
|
||||
<style>
|
||||
.update-notice { margin-bottom: 12px; border: 1px solid #2c3440; border-radius: 12px; background: #151a22; overflow: hidden; }
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { parseUpdateHistoryDocument } from '../update-history-document.ts';
|
||||
|
||||
test('업데이트 Markdown의 최근 업데이트 시각을 본문과 분리한다', () => {
|
||||
const document = parseUpdateHistoryDocument('---\nupdatedAt: 2026-08-31T14:15:17+09:00\n---\n\n## 업데이트');
|
||||
|
||||
assert.equal(document.updatedAt, '2026-08-31T14:15:17+09:00');
|
||||
assert.equal(document.body, '\n## 업데이트');
|
||||
});
|
||||
|
||||
test('메타데이터가 없는 Markdown도 그대로 표시한다', () => {
|
||||
const markdown = '## 업데이트';
|
||||
assert.deepEqual(parseUpdateHistoryDocument(markdown), { updatedAt: null, body: markdown });
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
export type UpdateHistoryDocument = {
|
||||
updatedAt: string | null;
|
||||
body: string;
|
||||
};
|
||||
|
||||
export function parseUpdateHistoryDocument(markdown: string): UpdateHistoryDocument {
|
||||
const frontmatter = markdown.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
||||
if (!frontmatter) return { updatedAt: null, body: markdown };
|
||||
|
||||
const updatedAt = frontmatter[1]
|
||||
.split(/\r?\n/)
|
||||
.map((line) => line.match(/^updatedAt:\s*(.+?)\s*$/)?.[1] ?? null)
|
||||
.find((value): value is string => value !== null) ?? null;
|
||||
|
||||
return {
|
||||
updatedAt,
|
||||
body: markdown.slice(frontmatter[0].length)
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user