From 147d44f957fbe8301ff0654923e477c37e4063b9 Mon Sep 17 00:00:00 2001 From: AmintaCCCP Date: Mon, 27 Apr 2026 22:28:24 +0800 Subject: [PATCH 1/5] chore: add v0.5.5 to version-info.xml Co-Authored-By: Claude Opus 4.7 --- versions/version-info.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/versions/version-info.xml b/versions/version-info.xml index f2cf8ec5..74ffa680 100644 --- a/versions/version-info.xml +++ b/versions/version-info.xml @@ -279,4 +279,19 @@ https://github.com/AmintaCCCP/GithubStarsManager/releases/tag/v0.5.4 + + 0.5.5 + 2026-04-27 + + Update Dialog Portal Fix: Fixed the update dialog backdrop mask being trapped inside the overflow container. The dialog now renders via React Portal at the document body level with proper z-index stacking. Added click-on-mask-to-dismiss behavior for better UX. (PR #112) + Banner Layout Improvements: Fixed banner button wrapping on narrow viewports. Removed invalid Tailwind classes and improved responsive layout with proper flex behavior. (PR #112) + Electron Focus Hijacking Fix: Replaced native alert()/confirm() dialogs with custom React components (Toast and ConfirmDialog), resolving focus trapping issues in Electron. (PR #117) + Category Lock Toggle Visibility: Improved the category lock toggle color differentiation in dark mode. Unlocked state now uses gray-600, locked state uses amber. Both states are clearly distinguishable. (PR #118) + AI Connection Test Enhancements: AI connection test now returns detailed, user-facing results including status and guidance messages. (PR #114) + AI Configuration Error Handling: AI analysis now properly blocks execution when API keys are empty or cannot be decrypted. Bulk AI config import now skips entries without usable keys and logs reasons. (PR #114) + Unified Error Messages and UI Polish: Consolidated AI configuration error messages and improved markdown image/GitHub link dark-mode styling. (PR #114) + Special thanks to @SummerRay160 for their outstanding contributions to this release! + + https://github.com/AmintaCCCP/GithubStarsManager/releases/tag/v0.5.5 + From cf2c6359b8d9d448affaeb2883996749899dd9c9 Mon Sep 17 00:00:00 2001 From: AmintaCCCP Date: Thu, 30 Apr 2026 17:17:47 +0800 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=E5=AE=8C=E5=96=84=20Release=20?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=9C=BA=E5=88=B6=20(#119)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 主要改动: - 移除 Header 同步按钮的 Release 获取逻辑,仅同步仓库列表 - 重构 getMultipleRepositoryReleases 支持增量同步和 pre-release 过滤 - 新增 fetchAllReleasesForRepo 分页拉取最多30条 - 添加速率限制检测,剩余配额不足时自动等待重置 - 支持 includePreRelease 开关,默认开启,状态持久化 - 老用户迁移:已有 release 数据的仓库标记为已同步,走增量 - 刷新失败仓库汇总提示,不再静默跳过 Co-Authored-By: Claude Opus 4.7 --- src/components/Header.tsx | 8 +- src/components/ReleaseTimeline.tsx | 101 +++++++++----- src/services/githubApi.ts | 217 +++++++++++++++++++++++------ src/store/useAppStore.ts | 26 +++- src/types/index.ts | 4 + 5 files changed, 273 insertions(+), 83 deletions(-) diff --git a/src/components/Header.tsx b/src/components/Header.tsx index fff25ced..6eed0ebd 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -99,10 +99,8 @@ export const Header: React.FC = () => { setRepositories(mergedRepositories); - // 3. 获取Release信息 - console.log('Fetching releases...'); - const releases = await githubApi.getMultipleRepositoryReleases(mergedRepositories.slice(0, 20)); - setReleases(releases); + // Note: Release fetching is now handled by the Refresh button in Release Timeline + // Header sync only syncs the starred repos list setLastSync(new Date().toISOString()); console.log('Sync completed successfully'); @@ -367,7 +365,7 @@ export const Header: React.FC = () => { onClick={handleSync} disabled={isLoading} className="p-1 rounded hover:bg-light-surface dark:hover:bg-white/5 transition-colors disabled:opacity-50" - title={t('同步仓库', 'Sync repositories')} + title={t('同步星标仓库列表(不包含Release)', 'Sync starred repos list (excludes Release)')} > diff --git a/src/components/ReleaseTimeline.tsx b/src/components/ReleaseTimeline.tsx index 37cccb4e..6fd3dc94 100644 --- a/src/components/ReleaseTimeline.tsx +++ b/src/components/ReleaseTimeline.tsx @@ -36,6 +36,8 @@ export const ReleaseTimeline: React.FC = () => { setReleaseSearchQuery, toggleReleaseExpandedRepository, setReleaseIsRefreshing, + includePreRelease, + setIncludePreRelease, } = useAppStore(); const { toast, confirm } = useDialog(); @@ -301,6 +303,7 @@ export const ReleaseTimeline: React.FC = () => { setReleaseIsRefreshing(true); try { const githubApi = new GitHubApiService(githubToken); + // Only fetch releases for repos that are subscribed to releases const subscribedRepos = repositories.filter(repo => releaseSubscriptions.has(repo.id)); if (subscribedRepos.length === 0) { @@ -308,49 +311,46 @@ export const ReleaseTimeline: React.FC = () => { return; } - const allNewReleases: Release[] = []; - - const latestReleaseTime = releases.length > 0 - ? releases.reduce((max, r) => Math.max(max, new Date(r.published_at).getTime()), 0) - : 0; - const sinceTimestamp = latestReleaseTime > 0 ? new Date(latestReleaseTime).toISOString() : undefined; + // Use the new getMultipleRepositoryReleases with options + const { releases: newReleases, failedRepos } = await githubApi.getMultipleRepositoryReleases( + subscribedRepos, + { includePreRelease } + ); + // Update repository sync metadata + const now = new Date().toISOString(); for (const repo of subscribedRepos) { - const [owner, name] = repo.full_name.split('/'); - - const hasExistingReleases = releases.some(r => r.repository.id === repo.id); - - let repoReleases: Release[]; - if (!hasExistingReleases) { - repoReleases = await githubApi.getRepositoryReleases(owner, name, 1, 10); - } else { - repoReleases = await githubApi.getIncrementalRepositoryReleases(owner, name, sinceTimestamp, 10); - } - - repoReleases.forEach(release => { - release.repository.id = repo.id; + updateRepository({ + ...repo, + has_fetched_releases: true, + last_release_fetch_time: now, }); - - allNewReleases.push(...repoReleases); - - await new Promise(resolve => setTimeout(resolve, 200)); } + // Filter out existing releases and add new ones const existingIds = new Set(useAppStore.getState().releases.map(r => r.id)); - const actuallyNewCount = allNewReleases.filter(r => !existingIds.has(r.id)).length; + const actuallyNewReleases = newReleases.filter(r => !existingIds.has(r.id)); + const actuallyNewCount = actuallyNewReleases.length; - if (allNewReleases.length > 0) { - addReleases(allNewReleases); + if (actuallyNewReleases.length > 0) { + addReleases(actuallyNewReleases); } - const now = new Date().toISOString(); setLastRefreshTime(now); - const message = language === 'zh' - ? `刷新完成!发现 ${actuallyNewCount} 个新Release。` - : `Refresh completed! Found ${actuallyNewCount} new releases.`; + // Build success message with failed repos info + let message: string; + if (failedRepos.length > 0) { + message = language === 'zh' + ? `刷新完成!发现 ${actuallyNewCount} 个新Release,${failedRepos.length} 个仓库刷新失败。` + : `Refresh completed! Found ${actuallyNewCount} new releases, ${failedRepos.length} repos failed.`; + } else { + message = language === 'zh' + ? `刷新完成!发现 ${actuallyNewCount} 个新Release。` + : `Refresh completed! Found ${actuallyNewCount} new releases.`; + } - toast(message, 'success'); + toast(message, actuallyNewCount > 0 ? 'success' : 'info'); } catch (error) { console.error('Refresh failed:', error); const errorMessage = language === 'zh' @@ -527,19 +527,35 @@ export const ReleaseTimeline: React.FC = () => { }

- {/* 刷新按钮 - 在有订阅仓库时显示 */} + {/* Pre-release toggle + Refresh button */} {subscribedRepoCount > 0 && ( -
+
+ {/* Pre-release toggle */} + + + {/* Refresh button */} {lastRefreshTime && ( -

+

{t('上次刷新:', 'Last refresh:')} {formatDistanceToNow(new Date(lastRefreshTime), { addSuffix: true })}

)} @@ -598,6 +614,21 @@ export const ReleaseTimeline: React.FC = () => { )} + {/* Pre-release toggle */} + + {/* Refresh Button */} {t('包含 Pre-release', 'Include Pre-release')} @@ -616,14 +626,17 @@ export const ReleaseTimeline: React.FC = () => { {/* Pre-release toggle */}