From 1d27127b521ee4150a995ea1253415df17216a3c Mon Sep 17 00:00:00 2001 From: Denozordec Date: Sat, 3 Oct 2026 21:59:55 +0700 Subject: [PATCH] =?UTF-8?q?fix(releases):=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=8F=D1=82=D1=8C=20=D0=BC=D0=B0=D0=BD=D0=B8=D1=84=D0=B5?= =?UTF-8?q?=D1=81=D1=82=20=D0=B8=20=D0=B4=D0=BE=D1=81=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D1=81=D0=B2=D0=B5?= =?UTF-8?q?=D0=B4=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=BE=20=D1=81=D0=B1=D0=BE?= =?UTF-8?q?=D1=80=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(main)/releases/page.tsx | 37 ++++++++++++++------------- components/release-notes-modal.tsx | 20 +++++++++------ docs/ux-ui-plan.md | 2 +- docs/ux-ui-progress.md | 10 +++++++- docs/ux-ui-stage-11b.md | 4 +++ lib/app-version.ts | 3 ++- lib/release-manifest.ts | 41 +++++------------------------- lib/release-metadata.test.ts | 19 ++++++++++++++ lib/release-metadata.ts | 31 ++++++++++++++++++++++ tests/e2e/releases-layout.spec.ts | 32 +++++++++++++++++++++++ 10 files changed, 135 insertions(+), 64 deletions(-) create mode 100644 lib/release-metadata.test.ts create mode 100644 lib/release-metadata.ts create mode 100644 tests/e2e/releases-layout.spec.ts diff --git a/app/(main)/releases/page.tsx b/app/(main)/releases/page.tsx index 0be3001e..532ddf57 100644 --- a/app/(main)/releases/page.tsx +++ b/app/(main)/releases/page.tsx @@ -6,7 +6,6 @@ import { formatAppVersionLabel, getAppVersion, getReleaseUrl } from "@/lib/app-v import { formatCommitSubject, getReleaseCommitPreview, - RELEASE_COMMIT_PREVIEW_LIMIT, } from "@/lib/release-commits" import { readReleaseManifest } from "@/lib/release-manifest" import { cn } from "@/lib/utils" @@ -16,23 +15,23 @@ export const dynamic = "force-dynamic" function formatPublishedAt(value: string | null, isProdBuild: boolean): string { if (value) { - return new Intl.DateTimeFormat("ru-RU", { + return "Дата манифеста: " + new Intl.DateTimeFormat("ru-RU", { dateStyle: "long", timeStyle: "short", }).format(new Date(value)) } - return isProdBuild ? "Прод-сборка" : "Локальная сборка" + return isProdBuild ? "Дата манифеста не указана" : "Локальная сборка" } export default function ReleasesPage() { const manifest = readReleaseManifest() const version = getAppVersion() const isProdBuild = !version.endsWith("-dev") - const releaseUrl = getReleaseUrl() || manifest.releaseUrl || null - const commits = getReleaseCommitPreview(manifest.commits) + const releaseUrl = getReleaseUrl() || manifest?.releaseUrl || null + const commits = getReleaseCommitPreview(manifest?.commits ?? []) return ( -
+
- - Открыть в Gitea + + Открыть релиз ) : null } /> -
+
- {formatAppVersionLabel(version)} - {formatPublishedAt(manifest.publishedAt, isProdBuild)} + {formatAppVersionLabel(version)} + {isProdBuild ? formatPublishedAt(manifest?.version === version ? manifest.publishedAt : null, true) : "Локальная сборка"}

Версия формируется автоматически от базы v1.0.0 по Conventional Commits.

+ {!manifest ?

Метаданные релиза недоступны или повреждены. Дата, ссылка и список коммитов не подтверждены.

: manifest.version !== version && isProdBuild ?

Манифест относится к {formatAppVersionLabel(manifest.version)}; версия запущенной сборки отличается.

: null} + 0 - ? `Последние ${RELEASE_COMMIT_PREVIEW_LIMIT} коммитов в сборке ${formatAppVersionLabel(manifest.version)}.` - : "Для локальной разработки список коммитов пуст. В прод-сборке он заполняется CI." + ? `Коммиты манифеста ${formatAppVersionLabel(manifest!.version)}: ${commits.length}.` + : manifest ? "В манифесте нет коммитов. При публикации список формируется CI." : "Список коммитов не подтверждён." } contentClassName="px-5 pb-5" > {commits.length === 0 ? ( -

Коммитов для отображения пока нет.

+

{manifest ? "Коммитов для отображения пока нет." : "Нет подтверждённых метаданных."}

) : (
    {commits.map((commit) => ( -
  • +
  • {commit.type} {commit.shortSha} {commit.author}
    -

    {formatCommitSubject(commit.subject)}

    +

    {formatCommitSubject(commit.subject)}

  • ))}
diff --git a/components/release-notes-modal.tsx b/components/release-notes-modal.tsx index 336b3109..b8187146 100644 --- a/components/release-notes-modal.tsx +++ b/components/release-notes-modal.tsx @@ -16,9 +16,8 @@ import { formatAppVersionLabel, getAppVersion } from "@/lib/app-version" import { formatCommitSubject, getReleaseCommitPreview, - RELEASE_COMMIT_PREVIEW_LIMIT, } from "@/lib/release-commits" -import type { ReleaseManifest } from "@/lib/release-manifest" +import {parseReleaseManifest,shouldShowReleaseNotes,type ReleaseManifest} from "@/lib/release-metadata" import { hasSeenReleaseModal, markReleaseModalSeen } from "@/lib/release-modal-storage" export function ReleaseNotesModal() { @@ -27,26 +26,30 @@ export function ReleaseNotesModal() { React.useEffect(() => { let cancelled = false + const controller=new AbortController() + const timeout=setTimeout(()=>controller.abort(),8000) async function loadManifest() { try { - const response = await fetch("/release-manifest.json", { cache: "no-store" }) + const response = await fetch("/release-manifest.json", { cache: "no-store", signal:controller.signal }) if (!response.ok) return - const data = (await response.json()) as ReleaseManifest - if (cancelled || !data.commits?.length || hasSeenReleaseModal(data.tag)) return + const data = parseReleaseManifest(await response.json()) + if (cancelled || controller.signal.aborted || !data || !shouldShowReleaseNotes(data,getAppVersion()) || hasSeenReleaseModal(data.tag)) return setManifest(data) setOpen(true) } catch { // Локальная сборка или недоступный manifest — модалку не показываем. - } + } finally {clearTimeout(timeout)} } void loadManifest() return () => { cancelled = true + clearTimeout(timeout) + controller.abort() } }, []) @@ -68,7 +71,7 @@ export function ReleaseNotesModal() { Что нового в {versionLabel} - Последние {RELEASE_COMMIT_PREVIEW_LIMIT} коммитов текущей сборки MikrotikManager. + Коммиты текущей сборки MikrotikManager: {commits.length}. @@ -78,7 +81,7 @@ export function ReleaseNotesModal() { {commits.map((commit) => (
  • @@ -99,6 +102,7 @@ export function ReleaseNotesModal() {