diff --git a/.claude/settings.local.json b/.claude/settings.local.json
new file mode 100644
index 00000000..8090089a
--- /dev/null
+++ b/.claude/settings.local.json
@@ -0,0 +1,9 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(git push *)",
+ "Bash(git add *)",
+ "Bash(git commit *)"
+ ]
+ }
+}
diff --git a/dist/index.html b/dist/index.html
deleted file mode 100644
index 3933ddf3..00000000
--- a/dist/index.html
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
-
-
-
0 ? searchResults : repositories}
+}) => {
+ const endMeasure = measureRender('RepositoriesView');
+ useEffect(() => {
+ endMeasure();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ return (
+
+
+
+
+
+ }>
+ 0 ? searchResults : repositories}
+ selectedCategory={selectedCategory}
+ />
+
+
+
-
-));
+ );
+});
RepositoriesView.displayName = 'RepositoriesView';
-const ReleasesView = React.memo(() =>
);
+const ReleasesView = React.memo(() => (
+
+ }>
+
+
+
+));
ReleasesView.displayName = 'ReleasesView';
-const SettingsView = React.memo(() =>
);
+const SettingsView = React.memo(() => (
+
+ }>
+
+
+
+));
SettingsView.displayName = 'SettingsView';
+const DiscoveryViewWrapper = React.memo(() => (
+
+ }>
+
+
+
+));
+DiscoveryViewWrapper.displayName = 'DiscoveryViewWrapper';
+
+const VIEW_COMPONENTS = {
+ repositories: RepositoriesView,
+ releases: ReleasesView,
+ subscription: DiscoveryViewWrapper,
+ settings: SettingsView,
+} as const;
+
function App() {
const {
isAuthenticated,
@@ -60,6 +112,8 @@ function App() {
setSelectedCategory,
} = useAppStore();
+ const [isInitialized, setIsInitialized] = useState(false);
+
useAutoUpdateCheck();
useEffect(() => {
@@ -85,6 +139,10 @@ function App() {
}
} catch (err) {
console.error('Failed to initialize backend:', err);
+ } finally {
+ if (!cancelled) {
+ setIsInitialized(true);
+ }
}
};
@@ -98,36 +156,50 @@ function App() {
};
}, []);
+ useEffect(() => {
+ if (currentView === 'releases') {
+ import('./components/ReleaseTimeline');
+ } else if (currentView === 'subscription') {
+ import('./components/DiscoveryView');
+ } else if (currentView === 'settings') {
+ import('./components/SettingsPanel');
+ }
+ }, [currentView]);
+
const handleCategorySelect = useCallback((category: string) => {
setSelectedCategory(category);
}, [setSelectedCategory]);
const currentViewContent = useMemo(() => {
- switch (currentView) {
- case 'repositories':
- return (
-
- );
- case 'releases':
- return
;
- case 'subscription':
- return
;
- case 'settings':
- return
;
- default:
- return null;
+ if (currentView === 'repositories') {
+ return (
+
+ );
}
+
+ const ViewComponent = VIEW_COMPONENTS[currentView];
+ if (!ViewComponent) return null;
+
+ return
;
}, [currentView, repositories, searchResults, selectedCategory, handleCategorySelect]);
if (!isAuthenticated) {
return
;
}
+ if (!isInitialized) {
+ return (
+
+
+
+ );
+ }
+
return (
diff --git a/src/components/BackToTop.tsx b/src/components/BackToTop.tsx
index 0fba21c3..1a174d94 100644
--- a/src/components/BackToTop.tsx
+++ b/src/components/BackToTop.tsx
@@ -1,10 +1,12 @@
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { ArrowUp } from 'lucide-react';
import { useAppStore } from '../store/useAppStore';
+import { useModalVisibility } from '../hooks/useModalVisibility';
export const BackToTop: React.FC = () => {
const [isVisible, setIsVisible] = useState(false);
const [isBouncing, setIsBouncing] = useState(false);
+ const isModalOpen = useModalVisibility();
const language = useAppStore(state => state.language);
const bounceTimeoutRef = useRef
| null>(null);
@@ -23,7 +25,6 @@ export const BackToTop: React.FC = () => {
});
}, []);
- // 触发跳跃动画
const triggerBounce = useCallback(() => {
if (bounceTimeoutRef.current) {
clearTimeout(bounceTimeoutRef.current);
@@ -42,7 +43,6 @@ export const BackToTop: React.FC = () => {
};
}, [toggleVisibility]);
- // 监听全局跳跃动画事件
useEffect(() => {
const handleBounceEvent = () => {
if (isVisible) {
@@ -59,6 +59,8 @@ export const BackToTop: React.FC = () => {
};
}, [isVisible, triggerBounce]);
+ const shouldShow = isVisible && !isModalOpen;
+
return (