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.latestReleaseNotes') }}

+
+ +
+
+ +
+
+ + {{ t('pages.settings.update.loadingReleaseNotes') }} +
+
+
{{ releaseNotes }}
+
+
+ {{ 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; +}