From 1b571387df277ca425ee3aa3b31884ddc80ab224 Mon Sep 17 00:00:00 2001 From: aramesh-site deploy Date: Mon, 28 Sep 2026 11:27:59 +0000 Subject: [PATCH] =?UTF-8?q?perf(home):=20=D9=85=D8=AA=D9=86=20=D9=87=DB=8C?= =?UTF-8?q?=D8=B1=D9=88=20=D8=A8=D9=84=D8=A7=D9=81=D8=A7=D8=B5=D9=84=D9=87?= =?UTF-8?q?=20=D8=AF=DB=8C=D8=AF=D9=87=20=D8=B4=D9=88=D8=AF=20=E2=80=94=20?= =?UTF-8?q?=D8=AA=D8=A7=D8=AE=DB=8C=D8=B1=D9=87=D8=A7=DB=8C=20=DB=B2.?= =?UTF-8?q?=DB=B1-=DB=B2.=DB=B6=20=D8=AB=D8=A7=D9=86=DB=8C=D9=87=20=D8=AD?= =?UTF-8?q?=D8=B0=D9=81=20=D9=88=20=D8=AF=D8=B1=D8=AE=D9=88=D8=A7=D8=B3?= =?UTF-8?q?=D8=AA=E2=80=8C=D9=87=D8=A7=DB=8C=20=D8=AF=D8=A7=D8=AF=D9=87=20?= =?UTF-8?q?=D9=85=D9=88=D8=A7=D8=B2=DB=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - انیمیشن ورود هیرو تاخیر ۲.۱ تا ۲.۶ ثانیه داشت (بعد از بارگذاری JS+API هم متن ۳ ثانیه نامرئی بود)؛ حالا ورود نرم با تاخیر ۰.۰۵-۰.۳۶ ثانیه - SiteProvider: /api/site و /api/admin/articles پشت سر هم fetch می‌شدند و setData منتظر هر دو می‌ماند؛ حالا همزمان شروع می‌شوند --- frontend/src/api.tsx | 43 +++++++++++++++------------ frontend/src/components/home/Hero.tsx | 10 +++---- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/frontend/src/api.tsx b/frontend/src/api.tsx index a98f447..36263b7 100644 --- a/frontend/src/api.tsx +++ b/frontend/src/api.tsx @@ -249,6 +249,24 @@ export function SiteProvider({ children }: { children: ReactNode }) { setError(false) const controller = new AbortController() const timeoutId = window.setTimeout(() => controller.abort(), SITE_TIMEOUT_MS) + // درخواست‌های سایت و پیش‌نویس‌های ادمین «همزمان» شروع می‌شوند — + // پشت سر هم بودنشان یعنی ادمین دو برابر دیرتر محتوا را می‌بیند. + const draftsPromise: Promise = getToken() + ? fetch(`${API_URL}/api/admin/articles`, { + headers: { Authorization: `Bearer ${getToken()}` }, + signal: controller.signal, + }) + .then(async (res) => { + if (res.status === 401) { + // توکن منقضی/نامعتبر — پاک می‌شود تا بارگذاری‌های بعدی درخواست بی‌فایده نفرستند + setToken(null) + return null + } + return res.ok ? ((await res.json()) as Article[]) : null + }) + .catch(() => null) + : Promise.resolve(null) + fetch(`${API_URL}/api/site`, { signal: controller.signal }) .then((res) => { if (!res.ok) throw new Error(`HTTP ${res.status}`) @@ -256,25 +274,12 @@ export function SiteProvider({ children }: { children: ReactNode }) { }) .then(async (json) => { if (cancelled) return - // برای ادمین واردشده، پیش‌نویس مقاله‌ها از انتهای مدیریتی خوانده و ادغام می‌شوند — - // بک‌اند در GET /api/site فقط مقاله‌های منتشرشده را برمی‌گرداند - if (getToken()) { - try { - const res = await fetch(`${API_URL}/api/admin/articles`, { - headers: { Authorization: `Bearer ${getToken()}` }, - }) - if (res.status === 401) { - // توکن منقضی/نامعتبر — پاک می‌شود تا بارگذاری‌های بعدی درخواست بی‌فایده نفرستند - setToken(null) - } else if (res.ok) { - const all = (await res.json()) as Article[] - const published = new Set(json.articles.map((a) => a.id)) - const drafts = all.filter((a) => a.status === 'draft' && !published.has(a.id)) - json.articles = [...json.articles, ...drafts] - } - } catch { - /* بدون پیش‌نویس‌ها ادامه بده — بارگذاری سایت هرگز وابسته به پنل مدیریتی نیست */ - } + // بک‌اند در GET /api/site فقط مقاله‌های منتشرشده را برمی‌گرداند — + // پیش‌نویس‌ها (برای ادمین) از درخواست موازی ادغام می‌شوند + const drafts = await draftsPromise + if (drafts) { + const published = new Set(json.articles.map((a) => a.id)) + json.articles = [...json.articles, ...drafts.filter((a) => a.status === 'draft' && !published.has(a.id))] } if (!cancelled) { if (json.brand && json.hero && json.footer && json.nav) setData(json) diff --git a/frontend/src/components/home/Hero.tsx b/frontend/src/components/home/Hero.tsx index c3a0dd2..62c66d4 100644 --- a/frontend/src/components/home/Hero.tsx +++ b/frontend/src/components/home/Hero.tsx @@ -51,7 +51,7 @@ export function Hero() { saveHero('kicker', v)} /> @@ -61,7 +61,7 @@ export function Hero() { saveHero('title', v)} aria-label={t('heroTitleAria')} /> @@ -71,7 +71,7 @@ export function Hero() { saveHero('subtitle', v)} /> @@ -81,7 +81,7 @@ export function Hero() { {/* دکمه اصلی: کپسولی شیشه‌ای دودی با دایره کهربایی اختصاصی برای آیکون */} @@ -113,7 +113,7 @@ export function Hero() {