From 692b40241ca490e73e9a32fd6bb7f33e7e719f7e Mon Sep 17 00:00:00 2001
From: Kuingsmile <96409857+Kuingsmile@users.noreply.github.com>
Date: Fri, 29 Aug 2025 13:53:22 +0800
Subject: [PATCH] :sparkles: Feature(custom): add release note page
---
src/renderer/i18n/locales/en.json | 15 ++-
src/renderer/i18n/locales/zh-CN.json | 15 ++-
src/renderer/i18n/locales/zh-TW.json | 15 ++-
src/renderer/pages/PicGoSetting.vue | 114 +++++++++++++++++
src/renderer/pages/css/PicgoSetting.css | 159 ++++++++++++++++++++++++
5 files changed, 315 insertions(+), 3 deletions(-)
diff --git a/src/renderer/i18n/locales/en.json b/src/renderer/i18n/locales/en.json
index ee8caffe..e99cf35a 100644
--- a/src/renderer/i18n/locales/en.json
+++ b/src/renderer/i18n/locales/en.json
@@ -339,7 +339,20 @@
"newestVersion": "Newest Version: {version}",
"getting": "Getting...",
"hasNewVersion": "PicList has been updated, please click OK to restart and trigger the update",
- "networkError": "Network error, please check your network connection"
+ "networkError": "Network error, please check your network connection",
+ "releaseNotes": "Release Notes",
+ "releaseNotesDescription": "View the latest changes and updates",
+ "latestReleaseNotes": "Latest Release Notes",
+ "refresh": "Refresh",
+ "retry": "Retry",
+ "loadingReleaseNotes": "Loading release notes...",
+ "releaseNotesError": "Failed to load release notes. Please check your network connection.",
+ "noReleaseNotes": "No release notes available",
+ "lastUpdated": "Last updated",
+ "justNow": "just now",
+ "minutesAgo": "{minutes} minutes ago",
+ "hoursAgo": "{hours} hours ago",
+ "daysAgo": "{days} days ago"
}
},
"plugin": {
diff --git a/src/renderer/i18n/locales/zh-CN.json b/src/renderer/i18n/locales/zh-CN.json
index ec8fc259..4c689eb1 100644
--- a/src/renderer/i18n/locales/zh-CN.json
+++ b/src/renderer/i18n/locales/zh-CN.json
@@ -334,7 +334,20 @@
"newestVersion": "最新版本",
"getting": "正在获取中...",
"hasNewVersion": "PicList 更新啦,请点击确定重启触发更新",
- "networkError": "网络错误,请检查网络连接"
+ "networkError": "网络错误,请检查网络连接",
+ "releaseNotes": "版本说明",
+ "releaseNotesDescription": "查看最新的更改和更新内容",
+ "latestReleaseNotes": "最新版本说明",
+ "refresh": "刷新",
+ "retry": "重试",
+ "loadingReleaseNotes": "正在加载版本说明...",
+ "releaseNotesError": "加载版本说明失败,请检查网络连接。",
+ "noReleaseNotes": "暂无版本说明",
+ "lastUpdated": "最后更新",
+ "justNow": "刚刚",
+ "minutesAgo": "{minutes} 分钟前",
+ "hoursAgo": "{hours} 小时前",
+ "daysAgo": "{days} 天前"
}
},
"plugin": {
diff --git a/src/renderer/i18n/locales/zh-TW.json b/src/renderer/i18n/locales/zh-TW.json
index f1a0075b..182b403c 100644
--- a/src/renderer/i18n/locales/zh-TW.json
+++ b/src/renderer/i18n/locales/zh-TW.json
@@ -334,7 +334,20 @@
"newestVersion": "最新版本",
"getting": "正在獲取中...",
"hasNewVersion": "PicList 更新啦,請點擊確定重啟觸發更新",
- "networkError": "網絡錯誤,請檢查網絡連接"
+ "networkError": "網絡錯誤,請檢查網絡連接",
+ "releaseNotes": "版本說明",
+ "releaseNotesDescription": "查看最新的更改和更新內容",
+ "latestReleaseNotes": "最新版本說明",
+ "refresh": "刷新",
+ "retry": "重試",
+ "loadingReleaseNotes": "正在載入版本說明...",
+ "releaseNotesError": "載入版本說明失敗,請檢查網絡連接。",
+ "noReleaseNotes": "暫無版本說明",
+ "lastUpdated": "最後更新",
+ "justNow": "剛剛",
+ "minutesAgo": "{minutes} 分鐘前",
+ "hoursAgo": "{hours} 小時前",
+ "daysAgo": "{days} 天前"
}
},
"plugin": {
diff --git a/src/renderer/pages/PicGoSetting.vue b/src/renderer/pages/PicGoSetting.vue
index dd52f545..6a96709d 100644
--- a/src/renderer/pages/PicGoSetting.vue
+++ b/src/renderer/pages/PicGoSetting.vue
@@ -650,6 +650,53 @@
+
+
+
+
{{ t('pages.settings.update.releaseNotes') }}
+
{{ t('pages.settings.update.releaseNotesDescription') }}
+
+
+
+
+
+
+
+ {{ t('pages.settings.update.loadingReleaseNotes') }}
+
+
+
+ {{ releaseNotesError }}
+
+
+
+ {{ t('pages.settings.update.noReleaseNotes') }}
+
+
+
+
+
+
@@ -1401,6 +1448,8 @@ const addWatch = () => {
watch(currentLanguage, newVal => {
if (newVal) {
handleLanguageChange(newVal)
+ // Fetch release notes when language changes
+ fetchReleaseNotes(true)
}
})
@@ -1531,6 +1580,12 @@ function confirmSyncSetting() {
const version = pkg.version
const latestVersion = ref('')
+const releaseNotes = ref('')
+const releaseNotesError = ref('')
+const releaseNotesLastFetch = ref(null)
+const fetchingReleaseNotes = ref(false)
+
+const RELEASE_NOTES_CACHE_DURATION = 30 * 60 * 1000
const needUpdate = computed(() => {
if (latestVersion.value) {
@@ -1589,6 +1644,9 @@ async function initData() {
formOfSetting.value.logFileSizeLimit = enforceNumber(settings.logFileSizeLimit) || 10
addProxyWatch()
addWatch()
+
+ // Fetch release notes on initialization
+ fetchReleaseNotes()
}
function initArray(arrayT: string | string[], defaultValue: string[]) {
@@ -1703,6 +1761,62 @@ function compareVersion2Update(current: string, latest: string): boolean {
return compare(current, latest, '<')
}
+function formatLastFetchTime(date: Date): string {
+ const now = new Date()
+ const diffInMinutes = Math.floor((now.getTime() - date.getTime()) / (1000 * 60))
+
+ if (diffInMinutes < 1) {
+ return t('pages.settings.update.justNow')
+ } else if (diffInMinutes < 60) {
+ return t('pages.settings.update.minutesAgo', { minutes: diffInMinutes })
+ } else {
+ const hours = Math.floor(diffInMinutes / 60)
+ if (hours < 24) {
+ return t('pages.settings.update.hoursAgo', { hours })
+ } else {
+ const days = Math.floor(hours / 24)
+ return t('pages.settings.update.daysAgo', { days })
+ }
+ }
+}
+
+async function fetchReleaseNotes(forceRefresh = false): Promise {
+ if (!forceRefresh && releaseNotesLastFetch.value) {
+ const timeSinceLastFetch = Date.now() - releaseNotesLastFetch.value.getTime()
+ if (timeSinceLastFetch < RELEASE_NOTES_CACHE_DURATION) {
+ return
+ }
+ }
+
+ try {
+ fetchingReleaseNotes.value = true
+ releaseNotesError.value = ''
+
+ const isEnglish = currentLanguage.value === 'en'
+ const fileName = isEnglish ? 'currentVersion_en.md' : 'currentVersion.md'
+ const url = `https://raw.githubusercontent.com/Kuingsmile/piclist/dev/${fileName}`
+
+ const response = await fetch(url)
+ if (response.ok) {
+ const content = await response.text()
+ releaseNotes.value = content
+ releaseNotesLastFetch.value = new Date()
+ releaseNotesError.value = ''
+ } else {
+ throw new Error(`HTTP ${response.status}`)
+ }
+ } catch (error) {
+ console.error('Failed to fetch release notes:', error)
+ releaseNotesError.value = t('pages.settings.update.releaseNotesError')
+ } finally {
+ fetchingReleaseNotes.value = false
+ }
+}
+
+async function fetchReleaseNotesManually(): Promise {
+ await fetchReleaseNotes(true)
+}
+
async function checkUpdate() {
checkUpdateVisible.value = true
latestVersion.value = (await getLatestVersion()) || t('pages.settings.update.networkError')
diff --git a/src/renderer/pages/css/PicgoSetting.css b/src/renderer/pages/css/PicgoSetting.css
index 7ba78b76..fa3f7d9a 100644
--- a/src/renderer/pages/css/PicgoSetting.css
+++ b/src/renderer/pages/css/PicgoSetting.css
@@ -809,3 +809,162 @@ small {
background: var(--color-blue-common);
border-color: rgba(255, 255, 255, 0.15);
}
+
+/* Update info and Release Notes Styles */
+.update-info {
+ margin-bottom: 1.5rem;
+ padding: 1rem;
+ background: var(--color-background);
+ border-radius: 8px;
+ border: 1px solid var(--color-border);
+}
+
+.update-info > div {
+ margin-bottom: 0.5rem;
+ font-size: 0.925rem;
+}
+
+.update-info > div:last-child {
+ margin-bottom: 0;
+}
+
+.update-notice {
+ color: var(--color-success) !important;
+ font-weight: 500;
+}
+
+/* Release Notes Card */
+.release-notes-card {
+ background: var(--color-surface);
+ border: 1px solid var(--color-border);
+ border-radius: 12px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.release-notes-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem 1.5rem;
+ background: var(--color-background);
+ border-bottom: 1px solid var(--color-border);
+}
+
+.release-notes-header h3 {
+ margin: 0;
+ font-size: 1rem;
+ font-weight: 600;
+ color: var(--color-text-primary);
+}
+
+.release-notes-actions {
+ display: flex;
+ gap: 0.5rem;
+}
+
+.btn-sm {
+ padding: 0.375rem 0.75rem;
+ font-size: 0.875rem;
+ min-height: auto;
+}
+
+.btn-link {
+ background: transparent;
+ border: none;
+ color: var(--color-accent);
+ text-decoration: underline;
+ padding: 0.25rem;
+}
+
+.btn-link:hover {
+ color: var(--color-accent-hover);
+ background: transparent;
+}
+
+.release-notes-content {
+ background: var(--color-background);
+ max-height: 400px;
+ overflow-y: auto;
+ scrollbar-width: thin;
+ scrollbar-color: var(--color-accent) transparent;
+}
+
+.release-notes-content::-webkit-scrollbar {
+ width: 6px;
+}
+
+.release-notes-content::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.release-notes-content::-webkit-scrollbar-thumb {
+ background: var(--color-accent);
+ border-radius: 3px;
+}
+
+.release-notes-content::-webkit-scrollbar-thumb:hover {
+ background: var(--color-accent-hover);
+}
+
+.release-notes-loading,
+.release-notes-error,
+.release-notes-empty {
+ padding: 2rem;
+ text-align: center;
+ color: var(--color-text-secondary);
+ font-size: 0.925rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+}
+
+.release-notes-error {
+ color: var(--color-error);
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.release-notes-text {
+ padding: 0;
+}
+
+.release-notes-pre {
+ margin: 0;
+ padding: 1.5rem;
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Consolas', monospace;
+ font-size: 0.875rem;
+ line-height: 1.6;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ color: var(--color-text-primary);
+ background: transparent;
+ border: none;
+}
+
+.release-notes-footer {
+ padding: 0.75rem 1.5rem;
+ background: var(--color-background-secondary);
+ border-top: 1px solid var(--color-border);
+ text-align: center;
+}
+
+.release-notes-footer small {
+ color: var(--color-text-secondary);
+ font-size: 0.75rem;
+}
+
+/* Rotation animation for loading icons */
+@keyframes rotate {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+.rotate {
+ animation: rotate 1s linear infinite;
+}