Skip to content

fix: avoid redundant scroll restore after sync#61

Closed
AmintaCCCP wants to merge 5 commits intomainfrom
fix/avoid-scroll-restore-jitter
Closed

fix: avoid redundant scroll restore after sync#61
AmintaCCCP wants to merge 5 commits intomainfrom
fix/avoid-scroll-restore-jitter

Conversation

@AmintaCCCP
Copy link
Owner

@AmintaCCCP AmintaCCCP commented Mar 16, 2026

Problem: During sync visual state restore, scroll is always forced back to the saved position even when the scroll position never changed. This can cause a subtle jitter after sync. Fix: Only restore scroll when window.scrollY actually differs from the saved position, and clear the saved value otherwise. Testing: not run (UI change).

Summary by CodeRabbit

  • New Features

    • Added persistent category selection—your chosen category filters are now saved across sessions.
  • Bug Fixes

    • Improved scroll position restoration when syncing data in the background.
    • Fixed view reset behavior when applying new filters.
  • Style

    • Updated login screen icon from SVG graphic to a custom image.
    • Refreshed favicon and added Apple touch icon support.

@coderabbitai
Copy link

coderabbitai bot commented Mar 16, 2026

📝 Walkthrough

Walkthrough

The pull request integrates category selection into app-wide state management, persisting category preferences and enhancing scroll restoration during background syncs. It updates asset references, adjusts Nginx proxy configuration for resolver-based routing, and refactors component state dependencies.

Changes

Cohort / File(s) Summary
Asset & Infrastructure Updates
index.html, nginx.conf
Replaced SVG favicon with PNG asset, added Apple touch icon. Added Nginx resolver directive and refactored API proxy_pass to use variable-based upstream resolution instead of hardcoded URL.
State Management Layer
src/store/useAppStore.ts, src/types/index.ts
Introduced selectedCategory state property (initialized to 'all'), exposed setSelectedCategory action, and extended persistence configuration to store/rehydrate currentView and selectedCategory.
App Component Integration
src/App.tsx
Removed local useState for selectedCategory, now sources category state from store via useAppStore, and passes setSelectedCategory callback to CategorySidebar for external control.
UI & View Management
src/components/LoginScreen.tsx, src/components/RepositoryList.tsx
Replaced Star SVG icon with PNG image in LoginScreen. Enhanced RepositoryList with scroll restoration during syncs using requestAnimationFrame, added filter reset key to clear visible count on filter changes, and introduced refs for managing scroll position recovery across async operations.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CategorySidebar
    participant App
    participant useAppStore
    participant RepositoryList

    User->>CategorySidebar: Select Category
    CategorySidebar->>App: Call onCategorySelect(category)
    App->>useAppStore: Call setSelectedCategory(category)
    useAppStore->>useAppStore: Update selectedCategory state
    useAppStore->>useAppStore: Persist selectedCategory
    App->>RepositoryList: Pass updated selectedCategory prop
    RepositoryList->>RepositoryList: Detect category change
    RepositoryList->>RepositoryList: Scroll to top
    RepositoryList->>RepositoryList: Reset visibleCount & filters
    RepositoryList->>RepositoryList: Recompute filterResetKey
Loading
sequenceDiagram
    participant RepositoryList
    participant Store as Background Sync
    participant Browser as Browser Engine

    RepositoryList->>Store: Sync starts (isSyncing=true)
    Store->>RepositoryList: handleSyncVisualState() called
    RepositoryList->>RepositoryList: Disable card animations
    RepositoryList->>RepositoryList: Save current scrollY position
    RepositoryList->>RepositoryList: Cancel pending restore frame
    Store->>RepositoryList: Sync completes (isSyncing=false)
    RepositoryList->>RepositoryList: handleSyncVisualState() called again
    RepositoryList->>Browser: requestAnimationFrame `#1`
    Browser->>RepositoryList: Enable card animations
    RepositoryList->>Browser: requestAnimationFrame `#2`
    Browser->>RepositoryList: Restore scrollY position
    RepositoryList->>RepositoryList: Clear saved scroll state
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR #25: Modifies src/components/RepositoryList.tsx with visibleCount-based incremental rendering and scroll handling logic, overlapping with the scroll restoration and filter reset refactoring in this PR.
  • PR #48: Updates src/store/useAppStore.ts and persisted state configuration, directly related to the new selectedCategory persistence changes in this PR.
  • PR #55: Addresses sync visual-state handling and category selection integration across the same code paths touched by this PR's store and component changes.

Poem

🐰 A hop through the state, a scroll through the view,
Categories persist, both old and brand new,
Sync restores position with frame-perfect grace,
Nginx resolves upstream in its rightful place,
PNG favicons gleam, the app's prettiest face!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix being implemented—avoiding redundant scroll restore after sync—which aligns with the primary change in RepositoryList.tsx.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/avoid-scroll-restore-jitter
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can use TruffleHog to scan for secrets in your code with verification capabilities.

Add a TruffleHog config file (e.g. trufflehog-config.yml, trufflehog.yml) to your project to customize detectors and scanning behavior. The tool runs only when a config file is present.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/store/useAppStore.ts (1)

150-168: Consider validating selectedCategory during rehydration.

The normalizePersistedState function doesn't validate selectedCategory. If a user had a custom category selected that was later deleted, the persisted value could reference a non-existent category, potentially causing UI inconsistencies.

🛡️ Suggested defensive validation
   return {
     ...currentState,
     ...safePersisted,
     repositories,
     releases,
     searchResults: repositories,
     releaseSubscriptions: normalizeNumberSet(safePersisted.releaseSubscriptions),
     readReleases: normalizeNumberSet(safePersisted.readReleases),
     searchFilters: {
       ...initialSearchFilters,
       sortBy: savedSortBy,
       sortOrder: savedSortOrder,
     },
     webdavConfigs: Array.isArray(safePersisted.webdavConfigs) ? safePersisted.webdavConfigs : [],
     customCategories: Array.isArray(safePersisted.customCategories) ? safePersisted.customCategories : [],
     assetFilters: Array.isArray(safePersisted.assetFilters) ? safePersisted.assetFilters : [],
     language: safePersisted.language || 'zh',
     isAuthenticated: !!(safePersisted.user && safePersisted.githubToken),
+    selectedCategory: typeof safePersisted.selectedCategory === 'string' ? safePersisted.selectedCategory : 'all',
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/store/useAppStore.ts` around lines 150 - 168, During state rehydration
ensure the persisted selectedCategory is validated and reset if it no longer
exists: in the normalizePersistedState / rehydration path where safePersisted is
merged into the returned state, check safePersisted.selectedCategory against the
resolved customCategories (Array.isArray(safePersisted.customCategories) ?
safePersisted.customCategories : []) and initialSearchFilters categories, and if
it’s missing set selectedCategory to a sane default (e.g.,
initialSearchFilters.selectedCategory or null). Update the returned object (the
block merging safePersisted into currentState) to use this validated
selectedCategory instead of blindly using safePersisted.selectedCategory to
avoid referencing deleted categories in the UI.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/LoginScreen.tsx`:
- Around line 70-73: In LoginScreen.tsx replace the fragile string path on the
img element by importing the asset as a module (e.g., import icon from
'src/assets/icon.png') and bind the imported variable to the img src (the <img
... /> in the JSX) so Vite will bundle and cache-bust the asset; update the top
of the file to add the import and change the img src attribute to use the
imported identifier.

---

Nitpick comments:
In `@src/store/useAppStore.ts`:
- Around line 150-168: During state rehydration ensure the persisted
selectedCategory is validated and reset if it no longer exists: in the
normalizePersistedState / rehydration path where safePersisted is merged into
the returned state, check safePersisted.selectedCategory against the resolved
customCategories (Array.isArray(safePersisted.customCategories) ?
safePersisted.customCategories : []) and initialSearchFilters categories, and if
it’s missing set selectedCategory to a sane default (e.g.,
initialSearchFilters.selectedCategory or null). Update the returned object (the
block merging safePersisted into currentState) to use this validated
selectedCategory instead of blindly using safePersisted.selectedCategory to
avoid referencing deleted categories in the UI.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cff13f0e-4f78-47e7-bdf6-b2ce0331a9b8

📥 Commits

Reviewing files that changed from the base of the PR and between 310e2fe and 87c59e7.

📒 Files selected for processing (7)
  • index.html
  • nginx.conf
  • src/App.tsx
  • src/components/LoginScreen.tsx
  • src/components/RepositoryList.tsx
  • src/store/useAppStore.ts
  • src/types/index.ts

Comment on lines +70 to +73
<div className="flex items-center justify-center w-16 h-16 bg-white rounded-2xl mx-auto mb-4 shadow-lg ring-1 ring-blue-100 overflow-hidden">
<img
src="./icon.png"
alt="GitHub Stars Manager"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify where icon.png actually exists and how it's referenced.
fd -a 'icon\.png$'
rg -n --type=tsx 'src="\./icon\.png"'
rg -n --type=tsx "from ['\"].*icon\.png['\"]"

Repository: AmintaCCCP/GithubStarsManager

Length of output: 187


🏁 Script executed:

cd /home/jailuser/git && find . -name "LoginScreen.tsx" -type f

Repository: AmintaCCCP/GithubStarsManager

Length of output: 105


🏁 Script executed:

cd /home/jailuser/git && head -100 src/components/LoginScreen.tsx | tail -40

Repository: AmintaCCCP/GithubStarsManager

Length of output: 1791


🏁 Script executed:

cd /home/jailuser/git && sed -n '1,10p' src/components/LoginScreen.tsx

Repository: AmintaCCCP/GithubStarsManager

Length of output: 562


🏁 Script executed:

cd /home/jailuser/git && sed -n '65,80p' src/components/LoginScreen.tsx

Repository: AmintaCCCP/GithubStarsManager

Length of output: 772


🏁 Script executed:

cd /home/jailuser/git && rg 'icon\.png' --no-filename

Repository: AmintaCCCP/GithubStarsManager

Length of output: 250


🏁 Script executed:

cd /home/jailuser/git && find . -maxdepth 2 -name "vite.config.*" -o -name "webpack.config.*" -o -name "tsconfig.json" -o -name "package.json" | head -10

Repository: AmintaCCCP/GithubStarsManager

Length of output: 165


🏁 Script executed:

cd /home/jailuser/git && cat package.json | grep -A 5 -B 5 "build\|vite\|webpack" | head -30

Repository: AmintaCCCP/GithubStarsManager

Length of output: 1034


🏁 Script executed:

cd /home/jailuser/git && ls -la assets/ public/ 2>/dev/null | head -20

Repository: AmintaCCCP/GithubStarsManager

Length of output: 521


🏁 Script executed:

cd /home/jailuser/git && rg "from.*assets" src/ --type tsx | head -10

Repository: AmintaCCCP/GithubStarsManager

Length of output: 100


Import the icon as a bundled asset instead of using a relative string path.

Line 72 uses src="./icon.png", which is fragile in routed paths. In a Vite app, relative paths resolve to the current route location, not the project root. Since icon.png exists in src/assets/, import it as a module and bind src to that URL for proper bundling and cache-busting.

Suggested fix
+import icon from '../assets/icon.png';
...
            <img
-             src="./icon.png"
+             src={icon}
              alt="GitHub Stars Manager"
              className="w-full h-full object-cover"
            />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/LoginScreen.tsx` around lines 70 - 73, In LoginScreen.tsx
replace the fragile string path on the img element by importing the asset as a
module (e.g., import icon from 'src/assets/icon.png') and bind the imported
variable to the img src (the <img ... /> in the JSX) so Vite will bundle and
cache-bust the asset; update the top of the file to add the import and change
the img src attribute to use the imported identifier.

@AmintaCCCP
Copy link
Owner Author

Closing per request: fix should go into PR #60 branch.

@AmintaCCCP AmintaCCCP closed this Mar 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants