Commit 0f64aa0

HPCesia <me@hpcesia.com>
2025-08-13 06:30:01
feat: cache recent comments
1 parent 6f528a5
Changed files (5)
packages
i18n
src
components
packages/i18n/src/en/web/index.ts
@@ -4,6 +4,7 @@ const en_web = {
   common: {
     open: 'Open',
     close: 'Close',
+    refresh: 'Refresh',
   },
   navigation: {
     title: 'Navigation',
packages/i18n/src/zh-CN/web/index.ts
@@ -4,6 +4,7 @@ const zh_CN_web = {
   common: {
     open: '打开',
     close: '关闭',
+    refresh: '刷新',
   },
   navigation: {
     title: '导航',
packages/i18n/src/zh-TW/web/index.ts
@@ -4,6 +4,7 @@ const zh_TW_web = {
   common: {
     open: '打開',
     close: '關閉',
+    refresh: '刷新',
   },
   navigation: {
     title: '導航',
packages/i18n/src/i18n-types.ts
@@ -183,6 +183,10 @@ export type NamespaceWebTranslation = {
 		 * C​l​o​s​e
 		 */
 		close: string
+		/**
+		 * R​e​f​r​e​s​h
+		 */
+		refresh: string
 	}
 	navigation: {
 		/**
@@ -583,6 +587,10 @@ export type TranslationFunctions = {
 			 * Close
 			 */
 			close: () => LocalizedString
+			/**
+			 * Refresh
+			 */
+			refresh: () => LocalizedString
 		}
 		navigation: {
 			/**
src/components/aside/RecentCommentsCard.vue
@@ -10,8 +10,68 @@ import { onMounted, ref } from 'vue';
 const comments = ref<CommentData[]>([]);
 const loading = ref(true);
 
-// 根据评论系统类型加载不同的评论数据
+const cacheKey = 'recent-comments-cache';
+const cacheExpireTime = 30 * 60 * 1000; // 30 min
+
+interface CacheData {
+  data: CommentData[];
+  timestamp: number;
+  provider: string;
+}
+
+function getFromCache(): CommentData[] | null {
+  try {
+    const cached = localStorage.getItem(cacheKey);
+    if (!cached) return null;
+
+    const cacheData: CacheData = JSON.parse(cached);
+
+    // 检查缓存是否过期
+    const isExpired = Date.now() - cacheData.timestamp > cacheExpireTime;
+    if (isExpired) {
+      localStorage.removeItem(cacheKey);
+      return null;
+    }
+
+    // 检查评论系统是否变更
+    if (cacheData.provider !== commentConfig.provider) {
+      localStorage.removeItem(cacheKey);
+      return null;
+    }
+
+    // 恢复 Date 对象(JSON 序列化会将 Date 转为字符串)
+    return cacheData.data.map((comment) => ({
+      ...comment,
+      time: new Date(comment.time),
+    }));
+  } catch (error) {
+    console.warn('Failed to read from cache:', error);
+    localStorage.removeItem(cacheKey);
+    return null;
+  }
+}
+
+function saveToCache(data: CommentData[]): void {
+  try {
+    const cacheData: CacheData = {
+      data,
+      timestamp: Date.now(),
+      provider: commentConfig.provider,
+    };
+    localStorage.setItem(cacheKey, JSON.stringify(cacheData));
+  } catch (error) {
+    console.warn('Failed to save to cache:', error);
+  }
+}
+
 async function loadComments() {
+  const cachedComments = getFromCache();
+  if (cachedComments) {
+    comments.value = cachedComments;
+    loading.value = false;
+    return;
+  }
+
   const provider = (() => {
     switch (commentConfig.provider) {
       case 'twikoo':
@@ -28,7 +88,9 @@ async function loadComments() {
   })();
 
   try {
-    comments.value = await provider.setup();
+    const data = await provider.setup();
+    comments.value = data;
+    saveToCache(data);
   } catch (error) {
     console.error('Failed to load recent comments:', error);
     comments.value = [];
@@ -37,6 +99,12 @@ async function loadComments() {
   }
 }
 
+function refreshComments() {
+  localStorage.removeItem(cacheKey);
+  loading.value = true;
+  loadComments();
+}
+
 onMounted(() => {
   loadComments();
 });
@@ -45,8 +113,17 @@ onMounted(() => {
 <template>
   <div id="recent-comments-card" class="card border-base-300 bg-base-200/25 border">
     <div class="card-body px-4 py-2">
-      <div class="card-title">
-        {{ t.info.recentComments() }}
+      <div class="card-title flex justify-between">
+        <span>{{ t.info.recentComments() }}</span>
+        <button
+          @click="refreshComments"
+          class="btn btn-ghost btn-sm btn-square text-base"
+          :disabled="loading"
+          :title="t.common.refresh()"
+          :aria-label="t.common.refresh()"
+        >
+          ↻
+        </button>
       </div>
       <ul class="list">
         <template v-if="!loading">