mirror of
https://github.com/AmintaCCCP/GithubStarsManager.git
synced 2025-11-25 02:34:54 +08:00
Compare commits
7 Commits
e095d955e1
...
8c5f71ea77
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c5f71ea77 | ||
|
|
d78bcd75d6 | ||
|
|
e0af19dd2e | ||
|
|
0678fe9b04 | ||
|
|
69f4a0788c | ||
|
|
b2c49460ab | ||
|
|
b7ad4558ef |
3
.github/workflows/build-desktop.yml
vendored
3
.github/workflows/build-desktop.yml
vendored
@@ -409,7 +409,8 @@ jobs:
|
|||||||
icon: 'build/icon.png',
|
icon: 'build/icon.png',
|
||||||
category: 'public.app-category.productivity',
|
category: 'public.app-category.productivity',
|
||||||
hardenedRuntime: true,
|
hardenedRuntime: true,
|
||||||
gatekeeperAssess: false
|
gatekeeperAssess: false,
|
||||||
|
identity: null
|
||||||
};
|
};
|
||||||
packageJson.build.dmg = {
|
packageJson.build.dmg = {
|
||||||
title: 'GitHub Stars Manager',
|
title: 'GitHub Stars Manager',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "github-stars-manager",
|
"name": "github-stars-manager",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.4",
|
"version": "0.1.5",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { Bot, ChevronDown, Pause, Play } from 'lucide-react';
|
import { Bot, ChevronDown, Pause, Play } from 'lucide-react';
|
||||||
import { RepositoryCard } from './RepositoryCard';
|
import { RepositoryCard } from './RepositoryCard';
|
||||||
|
|
||||||
@@ -32,7 +32,6 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
|
|||||||
const [showAISummary, setShowAISummary] = useState(true);
|
const [showAISummary, setShowAISummary] = useState(true);
|
||||||
const [showDropdown, setShowDropdown] = useState(false);
|
const [showDropdown, setShowDropdown] = useState(false);
|
||||||
const [isPaused, setIsPaused] = useState(false);
|
const [isPaused, setIsPaused] = useState(false);
|
||||||
const [searchTime, setSearchTime] = useState<number | undefined>(undefined);
|
|
||||||
|
|
||||||
// 使用 useRef 来管理停止状态,确保在异步操作中能正确访问最新值
|
// 使用 useRef 来管理停止状态,确保在异步操作中能正确访问最新值
|
||||||
const shouldStopRef = useRef(false);
|
const shouldStopRef = useRef(false);
|
||||||
@@ -76,6 +75,43 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Infinite scroll (瀑布流按需加载)
|
||||||
|
const LOAD_BATCH = 50;
|
||||||
|
const [visibleCount, setVisibleCount] = useState(LOAD_BATCH);
|
||||||
|
const sentinelRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
|
const startIndex = filteredRepositories.length === 0 ? 0 : 1;
|
||||||
|
const endIndex = Math.min(visibleCount, filteredRepositories.length);
|
||||||
|
const visibleRepositories = filteredRepositories.slice(0, visibleCount);
|
||||||
|
|
||||||
|
// Reset visible count when filters or data change
|
||||||
|
useEffect(() => {
|
||||||
|
setVisibleCount(LOAD_BATCH);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [selectedCategory, repositories, filteredRepositories.length]);
|
||||||
|
|
||||||
|
// IntersectionObserver to load more on demand
|
||||||
|
useEffect(() => {
|
||||||
|
const node = sentinelRef.current;
|
||||||
|
if (!node) return;
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
const entry = entries[0];
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
setVisibleCount((count) => {
|
||||||
|
if (count >= filteredRepositories.length) return count;
|
||||||
|
return Math.min(count + LOAD_BATCH, filteredRepositories.length);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ root: null, rootMargin: '200px', threshold: 0 }
|
||||||
|
);
|
||||||
|
|
||||||
|
observer.observe(node);
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, [filteredRepositories.length]);
|
||||||
|
|
||||||
const handleAIAnalyze = async (analyzeUnanalyzedOnly: boolean = false, analyzeFailedOnly: boolean = false) => {
|
const handleAIAnalyze = async (analyzeUnanalyzedOnly: boolean = false, analyzeFailedOnly: boolean = false) => {
|
||||||
if (!githubToken) {
|
if (!githubToken) {
|
||||||
alert(language === 'zh' ? 'GitHub token 未找到,请重新登录。' : 'GitHub token not found. Please login again.');
|
alert(language === 'zh' ? 'GitHub token 未找到,请重新登录。' : 'GitHub token not found. Please login again.');
|
||||||
@@ -136,7 +172,7 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
|
|||||||
const concurrency = activeConfig.concurrency || 1;
|
const concurrency = activeConfig.concurrency || 1;
|
||||||
|
|
||||||
// 并发分析函数
|
// 并发分析函数
|
||||||
const analyzeRepository = async (repo: Repository, index: number) => {
|
const analyzeRepository = async (repo: Repository) => {
|
||||||
// 检查是否需要停止
|
// 检查是否需要停止
|
||||||
if (shouldStopRef.current) {
|
if (shouldStopRef.current) {
|
||||||
return false;
|
return false;
|
||||||
@@ -201,9 +237,7 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const batch = targetRepos.slice(i, i + concurrency);
|
const batch = targetRepos.slice(i, i + concurrency);
|
||||||
const promises = batch.map((repo, batchIndex) =>
|
const promises = batch.map((repo) => analyzeRepository(repo));
|
||||||
analyzeRepository(repo, i + batchIndex)
|
|
||||||
);
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|
||||||
@@ -434,7 +468,10 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
|
|||||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
{t(`显示 ${filteredRepositories.length} 个仓库`, `Showing ${filteredRepositories.length} repositories`)}
|
{t(
|
||||||
|
`第 ${startIndex}-${endIndex} / 共 ${filteredRepositories.length} 个仓库`,
|
||||||
|
`Showing ${startIndex}-${endIndex} of ${filteredRepositories.length} repositories`
|
||||||
|
)}
|
||||||
{repositories.length !== filteredRepositories.length && (
|
{repositories.length !== filteredRepositories.length && (
|
||||||
<span className="ml-2 text-blue-600 dark:text-blue-400">
|
<span className="ml-2 text-blue-600 dark:text-blue-400">
|
||||||
{t(`(从 ${repositories.length} 个中筛选)`, `(filtered from ${repositories.length})`)}
|
{t(`(从 ${repositories.length} 个中筛选)`, `(filtered from ${repositories.length})`)}
|
||||||
@@ -462,17 +499,22 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Repository Grid */}
|
{/* Repository Grid with consistent card widths */}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||||
{filteredRepositories.map(repo => (
|
{visibleRepositories.map(repo => (
|
||||||
<RepositoryCard
|
<RepositoryCard
|
||||||
key={repo.id}
|
key={repo.id}
|
||||||
repository={repo}
|
repository={repo}
|
||||||
showAISummary={showAISummary}
|
showAISummary={showAISummary}
|
||||||
searchQuery={useAppStore.getState().searchFilters.query}
|
searchQuery={useAppStore.getState().searchFilters.query}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Sentinel for on-demand loading */}
|
||||||
|
{visibleCount < filteredRepositories.length && (
|
||||||
|
<div ref={sentinelRef} className="h-8" />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -17,7 +17,7 @@ export class UpdateService {
|
|||||||
private static getCurrentVersion(): string {
|
private static getCurrentVersion(): string {
|
||||||
// 在实际应用中,这个版本号应该在构建时注入
|
// 在实际应用中,这个版本号应该在构建时注入
|
||||||
// 这里暂时硬编码,你可以通过构建脚本或环境变量来动态设置
|
// 这里暂时硬编码,你可以通过构建脚本或环境变量来动态设置
|
||||||
return '0.1.4';
|
return '0.1.5';
|
||||||
}
|
}
|
||||||
|
|
||||||
static async checkForUpdates(): Promise<UpdateCheckResult> {
|
static async checkForUpdates(): Promise<UpdateCheckResult> {
|
||||||
|
|||||||
@@ -29,4 +29,14 @@
|
|||||||
</changelog>
|
</changelog>
|
||||||
<downloadUrl>https://github.com/AmintaCCCP/GithubStarsManager/releases/tag/v0.1.4</downloadUrl>
|
<downloadUrl>https://github.com/AmintaCCCP/GithubStarsManager/releases/tag/v0.1.4</downloadUrl>
|
||||||
</version>
|
</version>
|
||||||
|
<version>
|
||||||
|
<number>0.1.5</number>
|
||||||
|
<releaseDate>2025-09-23</releaseDate>
|
||||||
|
<changelog>
|
||||||
|
<item>fix: Fixed the issue where the analysis progress becomes 0 due to switching headers during AI analysis</item>
|
||||||
|
<item>Add drag and no-drag regions for desktop app</item>
|
||||||
|
<item>switch to infinite scroll with consistent card widths</item>
|
||||||
|
</changelog>
|
||||||
|
<downloadUrl>https://github.com/AmintaCCCP/GithubStarsManager/releases/tag/v0.1.5</downloadUrl>
|
||||||
|
</version>
|
||||||
</versions>
|
</versions>
|
||||||
Reference in New Issue
Block a user