Skip to content

Cloud Sync v2 & Unified LocalStorage State Handling#306

Merged
n-ce merged 25 commits into
mainfrom
cloud-sync-v2
May 31, 2025
Merged

Cloud Sync v2 & Unified LocalStorage State Handling#306
n-ce merged 25 commits into
mainfrom
cloud-sync-v2

Conversation

@n-ce
Copy link
Copy Markdown
Owner

@n-ce n-ce commented May 31, 2025

Summary by CodeRabbit

  • New Features

    • Added a cloud sync button in the Library header for importing and exporting library data with cloud storage.
    • Visual sync status indicators on the sync button for loading, success, error, import, and export states.
  • Improvements

    • Unified and centralized state management for settings, replacing scattered persistence with a reactive state object.
    • Settings changes now trigger user notifications prompting reloads instead of automatic page reloads.
    • Reorganized and updated icon sets, adding new cloud and download icons.
    • Replaced dynamic landing page fetch with a static informational display on startup.
    • Enhanced audio playback with prefetching and improved codec selection based on state.
    • Streamlined UI element creation by replacing utility functions with native DOM methods.
    • Simplified routing and queue management by relying solely on centralized state values.
    • Improved settings import/export and emergency reset with simplified localStorage handling.
    • Replaced multiple localStorage interactions with centralized state updates across components.
  • Bug Fixes

    • Improved settings import/export and emergency reset reliability with simplified localStorage handling.
  • Style

    • Enhanced Library and Settings UI with sticky headers, inline header elements, and improved icon tooltips.
    • Added inline display for header paragraphs and refined padding in list and library headers.
  • Chores

    • Updated dependencies for improved performance and compatibility.
  • Localization

    • Added a new English message prompting users to reload the page after changing settings.
    • Updated French translations for clarity and consistency.
    • Added new French Creole locale translations.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 31, 2025

"""

Walkthrough

This update centralizes application settings and feature flags into a single reactive state object, replacing scattered localStorage access and utility functions with direct state management via state and setState. All settings, toggles, and preferences now read from and write to this unified state, streamlining persistence and synchronization. Additional changes include a new cloud synchronization module for the library, UI adjustments for sync controls, and minor CSS and icon updates.

Changes

File(s) / Path(s) Change Summary
src/lib/store.ts, src/lib/utils.ts, src/scripts/audioEvents.ts, src/scripts/router.ts, src/scripts/search.ts, src/scripts/theme.ts, src/scripts/library.ts, src/scripts/queue.ts, src/scripts/i18n.ts, src/components/ActionsMenu.ts, src/components/ListItem.ts, src/components/Settings/app.ts, src/components/Settings/index.ts, src/components/Settings/library.ts, src/components/Settings/parental.ts, src/components/Settings/personalize.ts, src/components/Settings/playback.ts, src/components/Settings/search.ts, src/components/StreamItem.ts, src/components/SuperCollectionList.ts, src/components/WatchVideo.ts, src/modules/audioErrorHandler.ts, src/modules/miniPlayer.ts, src/modules/partsManager.ts, src/modules/setAudioStreams.ts, src/modules/setMetadata.ts, src/modules/hls.ts, src/modules/start.ts, src/lib/player.ts Refactored to use a centralized state object and setState for all settings and feature flags, replacing previous localStorage utilities (getSaved, save, removeSaved) and related imports.
src/components/Settings/index.ts Removed restoreSettings and extractSettings; settings import/export now directly overwrite/read the 'store' key in localStorage.
src/modules/cloudSync.ts, src/scripts/library.ts Introduced new cloud sync module for library; replaced inline sync logic with dynamic import and modular approach.
src/components/Settings/app.ts, src/components/Settings/library.ts, src/components/Settings/parental.ts, src/components/Settings/personalize.ts, src/components/Settings/playback.ts, src/components/Settings/search.ts All settings toggles/selectors now use state and setState; removed explicit reloads in favor of user notifications.
src/components/ActionsMenu.ts, src/components/ListItem.ts, src/components/StreamItem.ts, src/components/SuperCollectionList.ts, src/components/WatchVideo.ts, src/components/Settings/index.ts, src/components/Settings/app.ts, src/components/Settings/library.ts, src/components/Settings/parental.ts, src/components/Settings/personalize.ts, src/components/Settings/playback.ts, src/components/Settings/search.ts, src/modules/partsManager.ts, src/scripts/queue.ts Replaced utility DOM creation ($) with native document.createElement.
src/modules/setAudioStreams.ts Function is now async; codec/HQ preferences sourced from state.
src/modules/extractColorFromImage.ts Changed from named to default export; now always creates new canvas element.
src/components/Settings/Settings.css Made settings header sticky at the top of the scrollable container.
src/stylesheets/header.css, src/stylesheets/list.css Adjusted header and list padding styles.
src/stylesheets/library.css Added sync button and tooltip styles for cloud sync feature.
src/stylesheets/remixicon.css Added new icon classes for cloud sync and download; reorganized icon definitions.
src/locales/en.json Added new localization string: "settings_reload".
package.json Updated dependencies: hls.js, @netlify/blobs, @types/node.
netlify/edge-functions/landing.ts Removed legacy landing page edge function.
index.html Added cloud sync icon/button to library header.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI
    participant state
    participant setState
    participant localStorage

    User->>UI: Toggle a setting or change preference
    UI->>state: Read current value
    UI->>setState: setState(key, newValue)
    setState->>state: Update value
    setState->>localStorage: Persist entire state as JSON
    UI->>User: Show notification if reload is needed
Loading
sequenceDiagram
    participant User
    participant UI
    participant cloudSync
    participant localStorage
    participant Cloud

    User->>UI: Click sync button
    UI->>cloudSync: Initiate sync (with dbhash)
    cloudSync->>localStorage: Read library data
    cloudSync->>Cloud: POST library data (save to cloud)
    Cloud-->>cloudSync: Respond with success/error
    cloudSync->>UI: Update sync icon/status
    Note over UI,User: User can import from cloud if local library is empty
Loading

Possibly related PRs

  • n-ce/ytify#304: Adds the French Creole locale file src/locales/frc.json, directly related as both PRs add or update localization resources for the same language.
  • n-ce/ytify#297: Refactors UI components including ActionsMenu to replace Solid.js and utility functions with native DOM and centralized state, related to this PR's similar refactors.
  • n-ce/ytify#302: Related refactor of theming and personalization settings, sharing modifications in src/components/Settings/personalize.ts and src/scripts/theme.ts.

Poem

🐇
Settings now hop in a single state field,
No more scattered carrots for the code to wield.
Cloud sync appears with a puffy white cloud,
Buttons and icons are feeling quite proud.
With one little nibble, your preferences stay—
The rabbit says: "All tidy, hooray!"
☁️✨
"""


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0dfc1e and 545ea96.

📒 Files selected for processing (2)
  • src/modules/audioErrorHandler.ts (3 hunks)
  • src/scripts/audioEvents.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/scripts/audioEvents.ts
  • src/modules/audioErrorHandler.ts
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@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: 7

🔭 Outside diff range comments (5)
src/scripts/queue.ts (4)

53-63: ⚠️ Potential issue

Fix the toggle logic - state and UI are out of sync.

The toggle logic is inverted and will cause state to be opposite of the UI. Currently:

  1. setState('shuffle', btn.classList.contains('on')) saves the current state
  2. btn.classList.toggle('on') then changes the UI

This means if the button shows "on" and the user clicks to turn it "off", the state will be saved as true but the UI will show "off".

Apply this fix to synchronize state with UI:

-    setState('shuffle', btn.classList.contains('on'));
-
     btn.classList.toggle('on');
+    setState('shuffle', btn.classList.contains('on'));

85-95: ⚠️ Potential issue

Fix the toggle logic - state and UI are out of sync.

Same issue as the shuffle toggle - the state is saved before the UI is updated, causing them to be out of sync.

Apply this fix:

-    setState('filterLT10', btn.classList.contains('on'));
-
     btn.classList.toggle('on');
+    setState('filterLT10', btn.classList.contains('on'));

103-113: ⚠️ Potential issue

Fix the toggle logic - state and UI are out of sync.

Same toggle logic issue affecting the allowDuplicates setting.

Apply this fix:

-    setState('allowDuplicates', btn.classList.contains('on'));
-
     btn.classList.toggle('on');
+    setState('allowDuplicates', btn.classList.contains('on'));

121-132: ⚠️ Potential issue

Fix the toggle logic - state and UI are out of sync.

Same toggle logic issue affecting the enqueueRelatedStreams setting.

Apply this fix:

-    setState('enqueueRelatedStreams', btn.classList.contains('on'));
-
     btn.classList.toggle('on');
+    setState('enqueueRelatedStreams', btn.classList.contains('on'));
src/components/Settings/library.ts (1)

31-60: ⚠️ Potential issue

Fix race condition in async hash creation.

The setState('dbsync', hash) call at line 60 executes before the hash is generated asynchronously. This will always set an empty string to the state.

Move the setState call inside the hash creation callback:

-            location.reload();
-          });
-      }
-      if (!state.dbsync) {
+            setState('dbsync', hash);
+            location.reload();
+          });
+      }
+      if (!state.dbsync) {
  
         const termsAccepted = confirm('Data will be automatically deleted after one week of inactivity.\nytify is not responsible for data loss.\n\nI Understand');
         if (termsAccepted) {
           const username = prompt('Enter Username :');
           if (username) {
             const password = prompt('Enter Password :');
             const confirmpw = prompt('Confirm Password :');
             if (password && password === confirmpw)
               hashCreator(username + password);
             else alert('Incorrect Information!');
           }
         }
         e.preventDefault();
+      } else {
+        setState('dbsync', '');
       }
-      setState('dbsync', hash);
🧹 Nitpick comments (4)
src/stylesheets/library.css (1)

4-33: LGTM! Well-structured CSS for cloud sync feature.

The CSS additions properly support the new cloud sync functionality with:

  • Inline display for header elements
  • Comprehensive icon state management
  • User-friendly hover tooltips
  • Proper loading and error states

Consider accessibility improvements for the tooltips:

      &.ri-cloud-fill:hover::after {
        position: fixed;
        content: 'Import from cloud';
        font-size: small;
+       z-index: 1000;
+       background: var(--bg);
+       border: var(--border);
+       padding: 0.25rem;
+       border-radius: var(--roundness);
      }

      &.ri-cloud-off-fill:hover::after {
        position: fixed;
        content: 'Save to cloud';
        font-size: small;
+       z-index: 1000;
+       background: var(--bg);
+       border: var(--border);
+       padding: 0.25rem;
+       border-radius: var(--roundness);
      }
src/scripts/library.ts (1)

81-85: Add error handling for the dynamic cloud sync import.

The cloud sync implementation looks good, but consider adding error handling for the dynamic import to gracefully handle potential module loading failures.

-if (dbsync) import('../modules/cloudSync').then(mod => mod.default(dbsync, syncBtn));
+if (dbsync) import('../modules/cloudSync')
+  .then(mod => mod.default(dbsync, syncBtn))
+  .catch(err => console.warn('Cloud sync module failed to load:', err));
 else syncBtn.remove();
src/modules/start.ts (1)

127-127: Add context to the TODO comment.

The TODO comment doesn't explain what needs to be implemented or fixed.

Please add a descriptive comment explaining what work is needed here, for example:

-      // TODO
+      // TODO: Add logic to handle specific edge cases when clicking outside details
src/lib/store.ts (1)

4-46: Consider using more conventional property naming and add JSDoc documentation.

The state object contains properties with spaces in their names (e.g., 'part Reserved Collections'), which is unconventional and could be error-prone. Consider using camelCase or kebab-case naming conventions instead.

- 'part Reserved Collections': true,
- 'part Navigation Library': true,
+ partReservedCollections: true,
+ partNavigationLibrary: true,

Additionally, consider adding JSDoc documentation to describe what each setting does, especially for properties that aren't self-explanatory.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 568dbef and a06baf5.

⛔ Files ignored due to path filters (1)
  • public/remixicon.woff2 is excluded by !**/*.woff2
📒 Files selected for processing (39)
  • index.html (1 hunks)
  • netlify/edge-functions/landing.ts (0 hunks)
  • package.json (1 hunks)
  • src/components/ActionsMenu.ts (6 hunks)
  • src/components/ListItem.ts (2 hunks)
  • src/components/Settings/app.ts (6 hunks)
  • src/components/Settings/index.ts (2 hunks)
  • src/components/Settings/library.ts (3 hunks)
  • src/components/Settings/parental.ts (4 hunks)
  • src/components/Settings/personalize.ts (5 hunks)
  • src/components/Settings/playback.ts (3 hunks)
  • src/components/Settings/search.ts (2 hunks)
  • src/components/StreamItem.ts (3 hunks)
  • src/components/SuperCollectionList.ts (2 hunks)
  • src/components/WatchVideo.ts (4 hunks)
  • src/lib/player.ts (4 hunks)
  • src/lib/store.ts (3 hunks)
  • src/lib/utils.ts (6 hunks)
  • src/modules/audioErrorHandler.ts (2 hunks)
  • src/modules/cloudSync.ts (1 hunks)
  • src/modules/extractColorFromImage.ts (1 hunks)
  • src/modules/hls.ts (2 hunks)
  • src/modules/miniPlayer.ts (1 hunks)
  • src/modules/partsManager.ts (2 hunks)
  • src/modules/setAudioStreams.ts (2 hunks)
  • src/modules/setMetadata.ts (2 hunks)
  • src/modules/start.ts (4 hunks)
  • src/scripts/audioEvents.ts (4 hunks)
  • src/scripts/i18n.ts (1 hunks)
  • src/scripts/library.ts (4 hunks)
  • src/scripts/queue.ts (7 hunks)
  • src/scripts/router.ts (3 hunks)
  • src/scripts/search.ts (5 hunks)
  • src/scripts/theme.ts (4 hunks)
  • src/stylesheets/global.css (1 hunks)
  • src/stylesheets/header.css (0 hunks)
  • src/stylesheets/library.css (2 hunks)
  • src/stylesheets/list.css (1 hunks)
  • src/stylesheets/remixicon.css (2 hunks)
💤 Files with no reviewable changes (2)
  • src/stylesheets/header.css
  • netlify/edge-functions/landing.ts
🧰 Additional context used
🧬 Code Graph Analysis (24)
src/modules/setMetadata.ts (1)
src/lib/store.ts (1)
  • state (4-46)
src/modules/miniPlayer.ts (2)
src/lib/dom.ts (1)
  • img (1-1)
src/lib/store.ts (1)
  • state (4-46)
src/scripts/i18n.ts (1)
src/lib/store.ts (1)
  • state (4-46)
src/scripts/search.ts (2)
src/lib/store.ts (1)
  • state (4-46)
src/lib/dom.ts (1)
  • searchFilters (15-15)
src/modules/audioErrorHandler.ts (1)
src/lib/store.ts (1)
  • state (4-46)
src/components/ListItem.ts (2)
src/lib/store.ts (1)
  • state (4-46)
src/lib/utils.ts (1)
  • hostResolver (28-32)
src/scripts/router.ts (2)
src/lib/store.ts (1)
  • state (4-46)
src/lib/utils.ts (1)
  • goTo (12-12)
src/components/Settings/search.ts (1)
src/lib/store.ts (2)
  • state (4-46)
  • setState (56-60)
src/modules/setAudioStreams.ts (3)
src/lib/dom.ts (1)
  • title (3-3)
src/scripts/i18n.ts (1)
  • i18n (44-49)
src/lib/store.ts (2)
  • state (4-46)
  • store (63-158)
src/modules/partsManager.ts (2)
src/lib/store.ts (2)
  • state (4-46)
  • setState (56-60)
src/scripts/i18n.ts (1)
  • i18n (44-49)
src/components/WatchVideo.ts (2)
src/lib/store.ts (2)
  • state (4-46)
  • setState (56-60)
netlify/edge-functions/fallback.ts (1)
  • _ (3-44)
src/scripts/library.ts (1)
src/lib/store.ts (1)
  • state (4-46)
src/lib/utils.ts (1)
src/lib/store.ts (1)
  • state (4-46)
src/modules/cloudSync.ts (2)
src/lib/libraryUtils.ts (2)
  • saveDB (10-13)
  • getDB (8-8)
src/lib/utils.ts (1)
  • notify (57-65)
src/components/Settings/personalize.ts (2)
src/lib/store.ts (2)
  • state (4-46)
  • setState (56-60)
src/scripts/i18n.ts (1)
  • i18n (44-49)
src/components/Settings/parental.ts (1)
src/lib/store.ts (2)
  • state (4-46)
  • setState (56-60)
src/scripts/audioEvents.ts (1)
src/lib/store.ts (2)
  • state (4-46)
  • setState (56-60)
src/components/SuperCollectionList.ts (1)
src/lib/store.ts (2)
  • state (4-46)
  • setState (56-60)
src/components/Settings/library.ts (3)
src/lib/store.ts (2)
  • state (4-46)
  • setState (56-60)
src/lib/libraryUtils.ts (2)
  • getDB (8-8)
  • saveDB (10-13)
src/scripts/i18n.ts (1)
  • i18n (44-49)
src/scripts/queue.ts (1)
src/lib/store.ts (2)
  • state (4-46)
  • setState (56-60)
src/scripts/theme.ts (1)
src/lib/store.ts (2)
  • state (4-46)
  • store (63-158)
src/components/Settings/index.ts (1)
src/lib/store.ts (1)
  • params (2-2)
src/lib/player.ts (1)
src/lib/store.ts (2)
  • state (4-46)
  • store (63-158)
src/modules/hls.ts (1)
src/lib/store.ts (1)
  • state (4-46)
🪛 Biome (1.9.4)
src/components/Settings/library.ts

[error] 74-74: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 94-94: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

🔇 Additional comments (104)
package.json (1)

17-17: LGTM! Routine dependency update.

This patch version update for Node.js type definitions is appropriate and helps maintain compatibility with the latest type definitions.

src/stylesheets/global.css (1)

226-226: LGTM! Minor formatting improvement.

Adding a newline for consistency is good practice and improves code readability.

src/stylesheets/list.css (1)

6-9: LGTM! CSS supports new sync icon layout.

The padding-right: 2rem on direct child <p> elements provides appropriate spacing for the new sync icon added to the library header. The selector is specific and won't interfere with other elements.

index.html (1)

211-211: LGTM! Sync icon properly positioned in library header.

The sync icon placement in the library section header is logical and appropriate for cloud sync functionality. The syncNow ID and ri-cloud-fill class clearly indicate its purpose and provide the necessary hooks for JavaScript interaction.

src/modules/hls.ts (2)

1-1: LGTM! Proper import for centralized state management.

Adding state to the import aligns with the refactoring to centralize state management throughout the application.


12-12: LGTM! Consistent state refactoring.

The change from store.player.hq to state.hq correctly implements the centralized state management pattern without altering the underlying logic. The state.hq boolean flag maintains the same functionality for determining HLS quality level selection.

src/modules/setMetadata.ts (2)

32-32: LGTM! Consistent state management refactoring.

The change from store.loadImage to state.loadImage aligns with the unified state handling approach and maintains the same boolean logic for conditional image loading.


3-3:

Details

✅ Verification successful

Verify both imports are needed.

The import now includes both state and store from the same module. Please ensure both are actually used in this file to avoid unused imports.


🏁 Script executed:

#!/bin/bash
# Check if both state and store are used in the file
echo "Checking usage of 'state' in setMetadata.ts:"
rg "\bstate\." src/modules/setMetadata.ts

echo -e "\nChecking usage of 'store' in setMetadata.ts:"
rg "\bstore\." src/modules/setMetadata.ts

Length of output: 421


No Unused Imports Detected

Both state and store are actively used in src/modules/setMetadata.ts (state.loadImage, store.stream, store.actionsMenu), so no imports need to be removed. Approval granted.

src/scripts/i18n.ts (2)

1-1: Good refactoring to centralized state management.

The import change from getSaved to state aligns with the unified state handling approach.


4-4: LGTM! Maintains fallback logic correctly.

The change from getSaved('language') to state.language preserves the existing fallback behavior while using the centralized state object. The logical OR operation ensures proper defaults are maintained.

src/components/ListItem.ts (2)

2-2: Consistent with state management refactoring.

The import change from store to state aligns with the unified state handling approach across the codebase.


27-27: LGTM! Consistent image loading logic.

Both changes from store.loadImage to state.loadImage maintain consistent behavior:

  • Line 27: Controls the 'ravel' CSS class based on image loading preference
  • Line 34: Controls conditional rendering of the image element

The logic remains the same while using the centralized state object.

Also applies to: 34-34

src/modules/miniPlayer.ts (2)

3-3: Consistent import change for unified state management.

The import change from store to state aligns with the broader refactoring to centralize state management.


7-7: LGTM! Maintains image loading logic correctly.

The change from store.loadImage to state.loadImage preserves the functionality where the image element is removed when image loading is disabled. The negation logic (!state.loadImage) remains correct.

src/modules/audioErrorHandler.ts (2)

2-2: LGTM: Import updated for centralized state management

The import change aligns with the broader refactor to use centralized state management.


11-11: LGTM: State access updated correctly

The change from getSaved('custom_instance') to state.customInstance maintains the same logic while using the new centralized state approach. The property exists in the state object with appropriate default value.

src/components/StreamItem.ts (2)

2-2: LGTM: Import updated for centralized state

The import change correctly switches from store to state for the new centralized state management.


47-47: LGTM: Consistent state property access

All references to store.loadImage have been correctly updated to state.loadImage. The conditional logic for image loading and CSS class handling remains functionally identical.

Also applies to: 54-54, 66-66

src/components/WatchVideo.ts (3)

4-4: LGTM: Import updated for centralized state management

The import correctly includes setState and state from the new centralized store module, replacing the previous getSaved and save functions.


20-20: LGTM: State property access updated correctly

Both state.watchMode and state.hq correctly replace the previous getSaved calls, accessing the same properties from the centralized state object.

Also applies to: 48-48


163-163: LGTM: State update method correctly implemented

The change from save('watchMode', ...) to setState('watchMode', ...) correctly uses the new centralized state update function that both updates the state object and persists to localStorage.

src/scripts/router.ts (2)

3-3: LGTM: Import updated for centralized state

The import correctly switches to the new centralized state management system.


109-109:

Details

✅ Verification successful

LGTM: Fallback logic simplified through state defaults

The changes correctly remove the explicit fallback to '/search' since state.startupTab has '/search' as its default value in the state object definition. This simplifies the code while maintaining the same behavior.

Let me verify that the state initialization properly handles edge cases:

Also applies to: 118-118


🏁 Script executed:

#!/bin/bash
# Verify that state.startupTab always has a valid default value
ast-grep --pattern 'startupTab: $_'

Length of output: 37


I couldn’t find any startupTab declarations with the first pattern. Let’s locate every occurrence of startupTab (definitions & references) to confirm its default value:


🏁 Script executed:

#!/bin/bash
# List all occurrences of "startupTab" to find its initialization
rg -n "startupTab" .

# Look for possible initialState objects that might include startupTab
rg -n "initialState" .

Length of output: 510


Fallback logic is safe via default state

Confirmed that in src/lib/store.ts (around line 19) the startupTab property is initialized to '/search'. This ensures the removed explicit fallback in the router still resolves correctly.

• src/lib/store.ts:19 — startupTab: '/search'

src/scripts/search.ts (3)

3-4: LGTM! Clean import refactoring.

The import changes correctly replace utility functions with centralized state management from the store module.


83-83: LGTM! Proper DOM API usage.

Good replacement of the utility function with native document.createElement().


156-160: LGTM! Correct state-based initialization.

The logic correctly removes the suggestions element when disabled and sets the search filter value from state.

src/modules/extractColorFromImage.ts (2)

2-2: LGTM! Clean export pattern.

Converting to default export is consistent with the module's single responsibility.


6-6: LGTM! Proper DOM API usage.

Good replacement of the utility function with native document.createElement(), maintaining the same functionality while removing dependencies.

src/lib/player.ts (4)

2-3: LGTM! Clean import refactoring.

Properly updated imports to use centralized state management instead of scattered utility functions.


12-21: LGTM! Consistent state access pattern.

The watch mode check and dialog creation properly use the centralized state and native DOM APIs.


71-73: LGTM! Proper feature flag handling.

Correctly uses the centralized state for the enqueueRelatedStreams feature flag.


92-98: LGTM! Consistent discover feature handling.

Properly uses the centralized state for the discover feature flag check.

src/modules/setAudioStreams.ts (4)

2-2: LGTM! Proper state management import.

Correctly imports the centralized state management objects.


6-7: LGTM! Necessary async conversion.

Making the function async is required to properly await the opus support detection in the codec preference logic.


11-11: LGTM! Improved codec selection logic.

The codec preference logic correctly uses the centralized state and properly handles the async opus support detection when codec is set to 'any'.


38-38: LGTM! Consistent HQ preference handling.

Properly uses the centralized state for the HQ preference check.

src/scripts/audioEvents.ts (4)

3-4: LGTM! Clean import refactoring.

The import changes correctly remove unused localStorage utility functions and add the necessary centralized state management imports.


205-205: LGTM! Simplified volume persistence.

The refactoring correctly simplifies volume persistence by always using setState instead of conditional save/remove logic. This is more consistent and cleaner.


214-218: LGTM! Consistent state initialization.

The volume initialization correctly uses the centralized state object instead of direct localStorage access. The logic remains the same while being more consistent with the new state management approach.


57-57:

Details

✅ Verification successful

Verify the boolean logic for history state.

The logic changed from getSaved('history') === 'off' to !state.history. This suggests the state type changed from a string ('off'/'on') to a boolean. Ensure this is consistent with the state definition and other usages.


🏁 Script executed:

#!/bin/bash
# Description: Verify history state usage consistency across the codebase
# Expected: All history state checks should use boolean logic

# Search for history state usage patterns
rg -A 2 -B 2 "state\.history|getSaved.*history|save.*history" --type ts

Length of output: 604


History state logic is correctly implemented as boolean

Both usages of state.history now operate on a boolean value driven by the settings checkbox in src/components/Settings/library.ts (checked: state.history) and the guard in src/scripts/audioEvents.ts (if (!state.history) return;). There are no remaining string-based comparisons such as getSaved('history') === 'off'. Everything is consistent.

src/modules/partsManager.ts (4)

1-1: LGTM! Correct import refactoring.

The import change properly replaces localStorage utility functions with centralized state management.


8-27: LGTM! Consistent state access pattern.

All state checks correctly use direct boolean access from the centralized state object, maintaining the same functionality while improving consistency.


85-89: LGTM! Type-safe state handler.

The rewritten lsHandler function correctly uses type-safe state key access and setState for persistence. The implementation is clean and maintains the toggle functionality.


96-98: LGTM! Consistent PIN verification.

The PIN verification correctly uses the centralized state instead of direct localStorage access, maintaining the same security logic while being more consistent.

src/components/Settings/search.ts (3)

4-4: LGTM! Correct import for centralized state management.

The import change properly replaces localStorage utility functions with the centralized state management imports.


17-24: LGTM! Consistent search filter toggle logic.

The refactoring correctly:

  • Uses state object for checked state determination
  • Maintains the toggle behavior between empty string and 'music_songs'
  • Preserves navigation to '/search' after state change
  • Uses type-safe setState function

30-34: LGTM! Consistent search suggestions toggle.

The refactoring properly uses the centralized state for both checking the current value and updating it via setState, while preserving the page reload functionality.

src/lib/utils.ts (4)

4-4: LGTM: Import statement updated correctly.

The import has been updated to use the centralized state and store objects instead of the getSaved utility function, aligning with the unified state management approach.


42-44: LGTM: State access pattern migrated correctly.

The getSaved calls have been properly replaced with direct access to state properties. This maintains the same logic while using the centralized state management.


58-58: LGTM: Explicit DOM element creation.

The $ utility function has been replaced with explicit document.createElement calls, making the code more explicit and removing dependency on the utility shorthand.

Also applies to: 182-182


85-85: LGTM: Consistent state property access.

These getSaved calls have been correctly migrated to use state properties, maintaining consistency with the centralized state management pattern.

Also applies to: 203-203

src/components/Settings/personalize.ts (5)

5-5: LGTM: Import statement updated for centralized state management.

The import has been correctly updated to use setState and state from the centralized store instead of the old utility functions.


19-24: LGTM: Toggle switch logic correctly migrated.

The load image toggle now uses state.loadImage for the initial checked state and setState('loadImage', !state.loadImage) for toggling, which correctly inverts the boolean value.


31-36: LGTM: Roundness selector properly migrated.

Both the event handler and mount logic correctly use the centralized state pattern for managing the roundness setting.


49-64: LGTM: Custom theme logic correctly updated.

The custom theme toggle logic has been properly migrated to use state.customTheme for checking existence and setState for updates. The prompt logic for color input remains intact.


71-75: LGTM: Theme selector migration is correct.

The theme selector correctly uses state.theme for the initial value and setState for updates when the selection changes.

src/scripts/queue.ts (3)

1-9: LGTM: Import statements updated correctly.

The imports have been properly updated to use the centralized state management pattern, removing unused utilities and adding the required setState and state imports.


194-194: LGTM: Improved destructuring syntax.

The destructuring assignment for extracting the id from the dataset is more concise and readable.


213-213: LGTM: State access correctly migrated.

The shuffle check correctly uses state.shuffle instead of the old getSaved function.

src/components/Settings/playback.ts (6)

5-6: LGTM: Import statements updated correctly.

The imports have been properly updated to use centralized state management and include the quickSwitch utility for applying changes immediately.


19-24: LGTM: High quality audio toggle correctly implemented.

The toggle correctly uses state.hq for the initial checked state and setState('hq', !state.hq) for proper boolean toggling, followed by quickSwitch() to apply the change.


47-62: LGTM: Audio settings toggles correctly implemented.

Both the stable volume and enforce proxy toggles correctly use the centralized state pattern with proper boolean inversion for toggling.


68-73: LGTM: HLS toggle with page reload.

The HLS toggle correctly updates the state and triggers a page reload, which is appropriate for this type of fundamental playback setting change.


78-85: LGTM: Watch mode toggle logic is correct.

The watch mode toggle uses a conditional assignment pattern that sets the state to either an empty string (off) or '144p' (on), which correctly replaces the previous save/remove pattern.


26-42:

Details

✅ Verification successful

Verify the codec option value change.

The codec selector has been updated to use centralized state, which looks good. However, the empty string option value was changed to "any".

Please verify that this change from value="" to value="any" is intentional and aligns with how codec preferences are handled elsewhere in the codebase:


🏁 Script executed:

#!/bin/bash
# Search for codec handling in the codebase to verify the "any" value usage
rg -A 3 -B 3 "codec.*any|any.*codec" --type ts

Length of output: 791


No action needed: “any” codec option is consistent with existing logic

The codebase already defines the default codec as 'any' in src/lib/store.ts, and src/modules/setAudioStreams.ts correctly treats state.codec === 'any' as “choose best available codec.” This update from value="" to value="any" aligns with how codec preferences are handled elsewhere.

src/scripts/library.ts (3)

3-3: LGTM: Centralized state import aligns with refactoring objectives.

The addition of state and store imports from the centralized store module is consistent with the PR's goal of unifying localStorage state handling.


52-52: LGTM: Direct DOM API usage simplifies dependencies.

Replacing utility function calls with direct document.createElement is consistent with the broader refactoring pattern seen across the codebase.


61-61: LGTM: Direct localStorage access for library cleanup.

Using localStorage.removeItem('library') directly is appropriate for the clean library operation and aligns with removing utility function dependencies.

src/scripts/theme.ts (7)

3-3: LGTM: Centralized state import consistent with refactoring.

The import change aligns perfectly with the unified state management approach.


89-89: LGTM: Direct state access replaces utility function.

Using state.theme instead of getSaved('theme') is consistent with the centralized state management refactoring.


112-112: LGTM: Custom theme access updated consistently.

The change to state.customTheme maintains the same functionality while using the centralized state.


114-114: LGTM: Load image setting accessed from centralized state.

The conditional logic correctly uses state.loadImage for determining whether to load images.


116-116: LGTM: Default export access for color extraction module.

The change from named export to default export access is appropriate for the module structure.


128-129: LGTM: Optimized CSS variable setting.

The conditional check prevents unnecessary CSS variable setting when the roundness value matches the default, which is a good performance optimization.


131-131: LGTM: Consistent state access for image loading flag.

Using state.loadImage is consistent with the centralized state management pattern.

src/components/SuperCollectionList.ts (3)

7-7: LGTM: Centralized state management imports.

The import of setState and state aligns with the unified state handling approach.


10-10: LGTM: Direct state access for default super collection.

Using state.defaultSuperCollection directly replaces the previous getSaved call and maintains type safety with the cast to SuperCollection.


30-32: LGTM: Simplified state management in event listener.

The refactored logic using setState('defaultSuperCollection', elm.value) is much cleaner than the previous conditional save/remove pattern and maintains the same functionality.

src/stylesheets/remixicon.css (2)

10-10: LGTM: Improved font-face declaration.

The explicit format("woff2") specification and consistent double quotes improve font loading behavior and maintain CSS consistency.


221-243: LGTM: New icon classes support cloud sync functionality.

The addition of cloud sync icons (.ri-cloud-off-fill, .ri-cloud-fill) and reorganization of existing icons properly supports the new cloud sync feature implemented in the library component. The .ri-download-2-fill replacement also aligns with updated icon usage across components.

src/components/Settings/parental.ts (4)

4-4: LGTM: Clean import update for centralized state management.

The import change correctly replaces the old localStorage utility functions with the new centralized state management system.


12-16: LGTM: Consistent state-based initialization.

The parental controls initialization correctly uses the new state.partsManagerPIN instead of the old localStorage-based approach. The boolean check properly determines if parental controls are enabled.


29-52: LGTM: Proper PIN validation and state updates.

The toggle handler correctly:

  • Uses state.partsManagerPIN for PIN validation
  • Iterates through all state keys to disable parts starting with 'part '
  • Uses setState to clear the PIN and update individual parts
  • Maintains the same UX flow with prompts and page reload

The logic is sound and consistent with the new state management approach.


58-58: LGTM: Correct state key mapping for part toggles.

The toggle switch correctly uses the new state key format 'part ' + item.name instead of the old localStorage key format. The negation logic is preserved to maintain the same UI behavior.

src/modules/cloudSync.ts (5)

4-8: LGTM: Clean module structure and helper functions.

The module exports a well-structured function that takes the necessary parameters. The hashpoint URL construction and cls helper function for icon states are appropriately designed.


9-28: LGTM: Robust sync function with proper error handling.

The sync function includes:

  • Proper loading state management
  • Correct HTTP POST with JSON content type
  • Appropriate error handling with visual feedback
  • Prevention of redundant sync operations

The implementation handles both success and error cases appropriately.


30-34: LGTM: Clean fetch function implementation.

The fetchFromCloud function is concise and correctly:

  • Fetches data from the cloud endpoint
  • Parses JSON response
  • Saves to local database with 'cloud' source tag to prevent sync loops

36-45: LGTM: Smart debounced sync with cloud change detection.

The event listener implementation is well thought out:

  • Ignores changes originating from cloud to prevent sync loops
  • Uses debounced timeout to batch rapid changes
  • Properly clears previous timeouts
  • Updates UI to indicate pending sync

61-70: LGTM: Thoughtful initialization with user choice.

The initialization logic appropriately:

  • Checks if local database is empty
  • Prompts user for import decision
  • Handles both import and existing data scenarios
  • Provides proper error messaging for missing cloud data
  • Sets appropriate final icon state
src/components/ActionsMenu.ts (8)

4-5: LGTM: Clean import update for centralized state management.

The import changes correctly replace the old localStorage utilities and DOM selector utility with the new centralized state management system.


48-56: LGTM: Consistent state-based conditional rendering.

The conditional check correctly uses state['part Start Radio'] instead of the old !getSaved(...) pattern, maintaining the same functionality with the new state system.


62-62: LGTM: Native DOM API usage.

The change from $('a') to document.createElement('a') is consistent with removing the DOM utility in favor of native APIs.


70-70: LGTM: Icon style update.

The change from "ri-download-2-line" to "ri-download-2-fill" appears to be part of a design update to use filled icon variants.


74-96: LGTM: Consistent state-based conditional rendering.

The conditional check correctly uses state['part View Author'] instead of the old localStorage-based approach, maintaining the same functionality.


104-114: LGTM: Improved dialog element management.

The change to document.createElement('dialog') with proper cleanup (adding dialog.remove() in the event listener) is a good improvement over the old DOM utility approach.


120-138: LGTM: Consistent state usage for watch functionality.

The changes correctly:

  • Use state['part Watch On'] for conditional rendering
  • Use state.linkHost instead of getSaved('linkHost')
  • Maintain the same logic flow with improved state management

145-154: LGTM: Consistent native DOM API usage.

The change to document.createElement('dialog') with proper event handling maintains the same functionality while using native DOM APIs consistently.

src/components/Settings/app.ts (6)

5-5: LGTM: Clean import update for centralized state management.

The import change correctly replaces the old localStorage utility functions with the new centralized state management system.


19-36: LGTM: Simplified custom instance handling with proper state management.

The custom instance toggle correctly:

  • Uses state.customInstance for checked state
  • Sets empty string instead of removing saved data (consistent with state approach)
  • Uses setState for persistence
  • Maintains the same user interaction flow

38-52: LGTM: Consistent language selector implementation.

The language selector correctly uses setState for persistence and maintains the same functionality with the new state system.


54-75: LGTM: Proper link host handling with dual updates.

The link host selector correctly:

  • Updates both store.linkHost (for immediate use) and state.linkHost (for persistence)
  • Uses conditional logic to handle empty vs. selected values
  • Maintains the same initialization behavior using state.linkHost

77-94: LGTM: Consistent download format selector implementation.

The download format selector correctly uses state.dlFormat for both initialization and updates via setState.


96-111: LGTM: Consistent share action selector implementation.

The share action selector correctly uses state.shareAction for both initialization and updates via setState. The removal of the "always ask" option appears to be an intentional simplification.

src/lib/store.ts (1)

138-140: LGTM! Good integration with existing store.

The integration of the new state object with existing store properties looks clean and maintains backward compatibility while enabling centralized state management.

Also applies to: 157-157

Comment thread src/scripts/search.ts Outdated
Comment thread src/scripts/search.ts Outdated
Comment thread src/modules/cloudSync.ts
Comment thread src/components/Settings/library.ts
Comment on lines 52 to 60
async function importSettings(e: Event) {
e.preventDefault();
const newSettings = JSON.parse(
await (
(e.target as HTMLInputElement).files as FileList
)[0].text()
);

if (confirm('This will merge your current settings with the imported settings, continue?')) {
for (const key in newSettings)
save(key, newSettings[key]);

location.reload();
}
const newSettings = await (
(e.target as HTMLInputElement).files as FileList
)[0].text();

if (confirm('This will overwrite your current settings with the imported settings, continue?'))
localStorage.setItem('store', newSettings);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add JSON validation for imported settings.

The imported settings are directly saved to localStorage without validation. This could break the application if the JSON is malformed or contains unexpected data.

Add validation:

 async function importSettings(e: Event) {
   e.preventDefault();
   const newSettings = await (
     (e.target as HTMLInputElement).files as FileList
   )[0].text();
 
-  if (confirm('This will overwrite your current settings with the imported settings, continue?'))
-    localStorage.setItem('store', newSettings);
+  if (confirm('This will overwrite your current settings with the imported settings, continue?')) {
+    try {
+      const parsed = JSON.parse(newSettings);
+      // Validate that it's an object
+      if (typeof parsed === 'object' && parsed !== null) {
+        localStorage.setItem('store', newSettings);
+        location.reload();
+      } else {
+        alert('Invalid settings file format');
+      }
+    } catch (error) {
+      alert('Failed to parse settings file: ' + error.message);
+    }
+  }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function importSettings(e: Event) {
e.preventDefault();
const newSettings = JSON.parse(
await (
(e.target as HTMLInputElement).files as FileList
)[0].text()
);
if (confirm('This will merge your current settings with the imported settings, continue?')) {
for (const key in newSettings)
save(key, newSettings[key]);
location.reload();
}
const newSettings = await (
(e.target as HTMLInputElement).files as FileList
)[0].text();
if (confirm('This will overwrite your current settings with the imported settings, continue?'))
localStorage.setItem('store', newSettings);
}
async function importSettings(e: Event) {
e.preventDefault();
const newSettings = await (
(e.target as HTMLInputElement).files as FileList
)[0].text();
if (confirm('This will overwrite your current settings with the imported settings, continue?')) {
try {
const parsed = JSON.parse(newSettings);
// Validate that it's an object
if (typeof parsed === 'object' && parsed !== null) {
localStorage.setItem('store', newSettings);
location.reload();
} else {
alert('Invalid settings file format');
}
} catch (error) {
alert('Failed to parse settings file: ' + (error instanceof Error ? error.message : error));
}
}
}
🤖 Prompt for AI Agents
In src/components/Settings/index.ts around lines 52 to 60, the imported settings
JSON is saved directly to localStorage without validation, risking application
errors if the JSON is malformed or invalid. Modify the importSettings function
to parse the imported text as JSON inside a try-catch block, validate its
structure if needed, and only save it to localStorage if parsing succeeds. If
parsing fails, show an error message to the user and do not overwrite the
current settings.

Comment thread src/lib/store.ts
Comment on lines +56 to +60
export function setState<K extends keyof AppSettings>(key: K, val: AppSettings[K]) {
state[key] = val;
const str = JSON.stringify(state);
localStorage.setItem('store', str);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and consider performance optimizations for setState.

The setState function lacks error handling for localStorage operations and could be inefficient for frequent updates.

export function setState<K extends keyof AppSettings>(key: K, val: AppSettings[K]) {
  state[key] = val;
-  const str = JSON.stringify(state);
-  localStorage.setItem('store', str);
+  try {
+    const str = JSON.stringify(state);
+    localStorage.setItem('store', str);
+  } catch (error) {
+    console.error('Failed to save state to localStorage:', error);
+    // Could implement fallback strategy or notify user
+  }
}

For performance optimization, consider implementing debounced saves for frequent updates:

let saveTimeout: number;

export function setState<K extends keyof AppSettings>(key: K, val: AppSettings[K]) {
  state[key] = val;
  
  // Debounce localStorage writes
  clearTimeout(saveTimeout);
  saveTimeout = setTimeout(() => {
    try {
      localStorage.setItem('store', JSON.stringify(state));
    } catch (error) {
      console.error('Failed to save state to localStorage:', error);
    }
  }, 100);
}
🤖 Prompt for AI Agents
In src/lib/store.ts around lines 56 to 60, the setState function currently
writes to localStorage immediately without error handling and can cause
performance issues with frequent updates. Modify setState to debounce the
localStorage writes by using a timeout to delay saving for 100ms after the last
update. Wrap the localStorage.setItem call in a try-catch block to handle and
log any errors during saving. This will improve performance and add robustness
to the function.

Comment thread src/lib/store.ts
Comment on lines +51 to +53
const savedStore = localStorage.getItem('store');
if (savedStore)
state = JSON.parse(savedStore);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add validation and error handling for localStorage state loading.

The current implementation directly assigns the parsed localStorage value without validation, which could cause runtime errors if the stored state has a different structure or invalid data.

const savedStore = localStorage.getItem('store');
-if (savedStore)
-  state = JSON.parse(savedStore);
+if (savedStore) {
+  try {
+    const parsed = JSON.parse(savedStore);
+    // Validate and merge with default state to handle schema changes
+    state = { ...state, ...parsed };
+  } catch (error) {
+    console.warn('Failed to parse stored state, using defaults:', error);
+    // Optionally clear invalid localStorage data
+    localStorage.removeItem('store');
+  }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const savedStore = localStorage.getItem('store');
if (savedStore)
state = JSON.parse(savedStore);
const savedStore = localStorage.getItem('store');
if (savedStore) {
try {
const parsed = JSON.parse(savedStore);
// Validate and merge with default state to handle schema changes
state = { ...state, ...parsed };
} catch (error) {
console.warn('Failed to parse stored state, using defaults:', error);
// Optionally clear invalid localStorage data
localStorage.removeItem('store');
}
}
🤖 Prompt for AI Agents
In src/lib/store.ts around lines 51 to 53, the code directly parses and assigns
the localStorage item to state without validation, risking runtime errors if the
data is invalid or malformed. Modify the code to wrap the JSON.parse call in a
try-catch block to handle parsing errors gracefully. Additionally, validate the
parsed object to ensure it matches the expected state structure before assigning
it to state, and fallback to a default state if validation fails.

Copy link
Copy Markdown
Contributor

@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: 0

🔭 Outside diff range comments (1)
src/components/Settings/library.ts (1)

31-62: ⚠️ Potential issue

Critical: Fix async hash creation logic.

There's a critical issue with the dbsync handler. The setState('dbsync', hash) call on line 61 executes immediately, but hash is only set inside the async Promise callback of crypto.subtle.digest(). This means the state will always be set to an empty string, not the computed hash.

Move the setState call inside the Promise callback:

  function hashCreator(text: string) {
    const msgBuffer = new TextEncoder().encode(text);
    crypto.subtle.digest('SHA-256', msgBuffer)
      .then(hashBuffer => {
        hash = Array
          .from(new Uint8Array(hashBuffer))
          .map(b => b.toString(16).padStart(2, '0'))
          .join('');
+       setState('dbsync', hash);
        notify(i18n('settings_reload'));
      });
  }
  if (!state.dbsync) {
    // ... existing logic
  }
- setState('dbsync', hash);
🧹 Nitpick comments (1)
src/components/Settings/library.ts (1)

75-75: Consider replacing delete operator for better performance.

Static analysis correctly flags the use of delete operator which can impact performance. Consider using undefined assignment instead.

Apply these changes for better performance:

- delete db.discover;
+ db.discover = undefined;
- delete db.history;
+ db.history = undefined;

Also applies to: 95-95

🧰 Tools
🪛 Biome (1.9.4)

[error] 75-75: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a06baf5 and 27a8fda.

📒 Files selected for processing (11)
  • package.json (1 hunks)
  • src/components/Settings/app.ts (6 hunks)
  • src/components/Settings/library.ts (3 hunks)
  • src/components/Settings/parental.ts (4 hunks)
  • src/components/Settings/personalize.ts (5 hunks)
  • src/components/Settings/playback.ts (3 hunks)
  • src/components/Settings/search.ts (2 hunks)
  • src/locales/en.json (1 hunks)
  • src/modules/cloudSync.ts (1 hunks)
  • src/scripts/queue.ts (7 hunks)
  • src/scripts/search.ts (5 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/scripts/search.ts
  • src/modules/cloudSync.ts
  • package.json
  • src/scripts/queue.ts
  • src/components/Settings/parental.ts
  • src/components/Settings/app.ts
  • src/components/Settings/personalize.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/components/Settings/search.ts (3)
src/lib/store.ts (2)
  • state (4-46)
  • setState (56-60)
src/lib/utils.ts (1)
  • notify (57-65)
src/scripts/i18n.ts (1)
  • i18n (44-49)
src/components/Settings/library.ts (4)
src/lib/store.ts (2)
  • state (4-46)
  • setState (56-60)
src/lib/utils.ts (1)
  • notify (57-65)
src/scripts/i18n.ts (1)
  • i18n (44-49)
src/lib/libraryUtils.ts (2)
  • getDB (8-8)
  • saveDB (10-13)
🪛 Biome (1.9.4)
src/components/Settings/library.ts

[error] 75-75: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 95-95: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

🔇 Additional comments (10)
src/components/Settings/search.ts (3)

4-6: Excellent refactoring to centralized state management.

The migration from scattered localStorage calls to the unified state and setState API is well-executed. The imports are clean and the code now follows the centralized state management pattern consistently.


18-25: Toggle logic correctly implemented.

The searchFilter toggle correctly reads from state.searchFilter and updates using setState(). The logic properly toggles between an empty string and 'music_songs' while navigating to /search afterward.


31-36: Improved user experience with notification instead of forced reload.

The searchSuggestions toggle correctly uses the reactive state and replaces the previous forced page reload with a user-friendly notification. This is a better UX pattern that allows users to control when they want to reload.

src/components/Settings/playback.ts (4)

5-6: Clean import structure for centralized state management.

The imports correctly bring in the unified state management (setState, state) and utility functions (notify, quickSwitch), establishing a clean foundation for the refactored component.


19-24: Audio quality toggle correctly refactored.

The HQ audio toggle properly reads from state.hq and updates via setState(). The quickSwitch() call ensures immediate application of the setting change.


40-40: Good alignment with state defaults.

Changing the codec option value from an empty string to "any" aligns with the default value in the centralized state object (line 27 in src/lib/store.ts), ensuring consistency across the application.


68-73: Improved UX by replacing forced reload with notification.

The HLS toggle now uses a notification to inform users about the need to reload instead of forcing an immediate page reload. This gives users control over when to apply the change and is a better user experience pattern.

src/components/Settings/library.ts (3)

4-6: Excellent migration to centralized state management.

The imports correctly establish the unified state management pattern, replacing the previous scattered localStorage approach with a clean, centralized system.


19-25: startupTab toggle logic is now consistent.

The startupTab comparison logic is correct and consistent - both the checked property (line 19) and the handler comparison (line 22) properly use /library with the leading slash. This appears to address the previous review concern.


68-82: Discover and history toggles correctly implemented.

Both toggles properly integrate with the centralized state management and handle the data clearing logic appropriately. The user confirmation flow and state updates are well-structured.

Also applies to: 88-101

🧰 Tools
🪛 Biome (1.9.4)

[error] 75-75: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

Copy link
Copy Markdown
Contributor

@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: 0

🧹 Nitpick comments (2)
src/components/Settings/Settings.css (2)

5-6: Simplify padding declarations for clarity.

You can consolidate padding: 5% and padding-top: 0 into a single shorthand (for example, padding: 0 5% 5% 5%) to avoid overriding values and make the intent clearer.


15-23: Refine sticky header styling for readability and consistency.

  • Add spacing around the child combinator (> h3> h3) to improve selector readability.
  • Consider using fixed units (e.g., rem or px) instead of % for vertical padding so the header height remains consistent across different screen sizes.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 27a8fda and a3fd83c.

📒 Files selected for processing (3)
  • src/components/Settings/Settings.css (2 hunks)
  • src/components/Settings/app.ts (5 hunks)
  • src/lib/utils.ts (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/lib/utils.ts
  • src/components/Settings/app.ts

Copy link
Copy Markdown
Contributor

@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/modules/setAudioStreams.ts (1)

10-12: Consider more robust prefetch detection

The prefetch detection logic !receiver.parentNode could be fragile. Consider making the prefetch parameter explicit rather than inferring it from DOM state.

-export default async function(audioStreams: AudioStream[],
-  isLive = false,
-  receiver: HTMLAudioElement = audio
-) {
-  const prefetch = !receiver.parentNode;
+export default async function(audioStreams: AudioStream[],
+  isLive = false,
+  receiver: HTMLAudioElement = audio,
+  prefetch = false
+) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a3fd83c and c8ebcdf.

📒 Files selected for processing (4)
  • src/lib/player.ts (5 hunks)
  • src/lib/utils.ts (6 hunks)
  • src/modules/setAudioStreams.ts (2 hunks)
  • src/scripts/audioEvents.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/lib/player.ts
  • src/lib/utils.ts
  • src/scripts/audioEvents.ts
🔇 Additional comments (4)
src/modules/setAudioStreams.ts (4)

2-2: LGTM: State centralization import

Good addition of the centralized state import, aligning with the broader refactoring objective.


41-41: LGTM: State centralization for HQ preference

Good change to use the centralized state.hq instead of store.player.hq.


47-47: LGTM: Receiver parameter usage with prefetch flag

Proper use of the receiver parameter and passing the prefetch flag to proxyHandler.


6-9:

Details

❓ Verification inconclusive

Verify backward compatibility for async function change

The function signature change from synchronous to asynchronous is a breaking change that could affect existing callers.

Please run this script to identify all callers and ensure they properly await this function:


🏁 Script executed:

#!/bin/bash
# Search for all calls to setAudioStreams function
ast-grep --pattern 'setAudioStreams($$$)'

Length of output: 43


Let's broaden our search for callers using ripgrep to catch all invocation sites:


🏁 Script executed:

#!/bin/bash
# Search for all occurrences of setAudioStreams( in .ts/.tsx files
rg -n 'setAudioStreams\(' --glob '*.ts' --glob '*.tsx'

Length of output: 56


No internal call sites detected for setAudioStreams – please verify external callers expect async behavior

I searched the entire codebase for setAudioStreams( in TS/TSX files and found no invocations. Since converting this export to async is a breaking change for any existing consumers, please:

  • Confirm whether this function is imported or called by other modules, scripts, or external repositories not covered by our search.
  • If so, ensure all callers now properly await the returned promise or handle the async nature.

title.textContent = i18n('player_audiostreams_setup');

const preferedCodec = store.player.codec;
const preferedCodec = state.codec === 'any' ? ((await store.player.supportsOpus) ? 'opus' : 'aac') : state.codec;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for async codec detection

The codec detection logic lacks error handling and could fail if store.player.supportsOpus is undefined or throws an error.

-  const preferedCodec = state.codec === 'any' ? ((await store.player.supportsOpus) ? 'opus' : 'aac') : state.codec;
+  let preferedCodec: string;
+  if (state.codec === 'any') {
+    try {
+      preferedCodec = (await store.player.supportsOpus) ? 'opus' : 'aac';
+    } catch (error) {
+      console.warn('Failed to detect opus support, defaulting to aac:', error);
+      preferedCodec = 'aac';
+    }
+  } else {
+    preferedCodec = state.codec;
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const preferedCodec = state.codec === 'any' ? ((await store.player.supportsOpus) ? 'opus' : 'aac') : state.codec;
let preferedCodec: string;
if (state.codec === 'any') {
try {
preferedCodec = (await store.player.supportsOpus) ? 'opus' : 'aac';
} catch (error) {
console.warn('Failed to detect opus support, defaulting to aac:', error);
preferedCodec = 'aac';
}
} else {
preferedCodec = state.codec;
}
🤖 Prompt for AI Agents
In src/modules/setAudioStreams.ts at line 14, the async codec detection using
store.player.supportsOpus lacks error handling. Wrap the await call in a
try-catch block to handle potential errors or undefined values gracefully. In
the catch block, set a default codec or fallback value to ensure the code
continues to function without crashing.

Copy link
Copy Markdown
Contributor

@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: 0

🔭 Outside diff range comments (1)
src/components/Settings/library.ts (1)

31-61: ⚠️ Potential issue

Fix logic error and improve security in dbsync handler.

The current implementation has several issues:

  1. Logic error: The hash variable is only populated when !state.dbsync is true, but setState('dbsync', hash) is called unconditionally, potentially setting an empty string when toggling off.

  2. Security concern: Concatenating username+password directly without salt is not a secure hashing practice.

  3. Unclear flow: The conditional hash creation makes the logic hard to follow.

Apply this diff to fix the logic:

   handler: async e => {
-    let hash = '';
-    async function hashCreator(text: string) {
-      const msgBuffer = new TextEncoder().encode(text);
-      const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
-
-      hash = Array
-        .from(new Uint8Array(hashBuffer))
-        .map(b => b.toString(16).padStart(2, '0'))
-        .join('');
-
-      notify(i18n('settings_reload'));
-    }
-    if (!state.dbsync) {
+    if (!state.dbsync) {
+      // Enabling sync
+      let hash = '';
       
       const termsAccepted = confirm('Data will be automatically deleted after one week of inactivity.\nytify is not responsible for data loss.\n\nI Understand');
       if (termsAccepted) {
         const username = prompt('Enter Username :');
         if (username) {
           const password = prompt('Enter Password :');
           const confirmpw = prompt('Confirm Password :');
           if (password && password === confirmpw) {
-            await hashCreator(username + password);
+            const msgBuffer = new TextEncoder().encode(username + password);
+            const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
+            hash = Array
+              .from(new Uint8Array(hashBuffer))
+              .map(b => b.toString(16).padStart(2, '0'))
+              .join('');
+            setState('dbsync', hash);
+            notify(i18n('settings_reload'));
           } else alert('Incorrect Information!');
         }
       }
       e.preventDefault();
+    } else {
+      // Disabling sync
+      setState('dbsync', '');
+      notify(i18n('settings_reload'));
     }
-    setState('dbsync', hash);
   }

Security recommendation: Consider adding a salt to the hash for better security:

const salt = crypto.getRandomValues(new Uint8Array(16));
const combined = username + password + Array.from(salt).join('');
🧹 Nitpick comments (2)
src/components/Settings/library.ts (2)

67-82: Replace delete operator for better performance.

The static analysis correctly flags the delete operator usage which can impact performance.

Apply this diff to improve performance:

       if (confirm(i18n("settings_clear_discoveries", count.toString()))) {
-        delete db.discover;
+        db.discover = undefined;
         saveDB(db);
🧰 Tools
🪛 Biome (1.9.4)

[error] 74-74: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


87-101: Replace delete operator for better performance.

Similar to the discover toggle, the delete operator should be replaced for better performance.

Apply this diff to improve performance:

       if (confirm(i18n("settings_clear_history", count.toString()))) {
-        delete db.history;
+        db.history = undefined;
         saveDB(db);
🧰 Tools
🪛 Biome (1.9.4)

[error] 94-94: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c8ebcdf and c556150.

📒 Files selected for processing (1)
  • src/components/Settings/library.ts (3 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/components/Settings/library.ts

[error] 74-74: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 94-94: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

🔇 Additional comments (2)
src/components/Settings/library.ts (2)

4-6: LGTM! Clean import refactor for centralized state management.

The imports correctly bring in the new centralized state management (setState, state) and notification utilities, aligning with the broader refactor to replace scattered localStorage access.


19-26: LGTM! Consistent state-based toggle implementation.

The startupTab toggle now correctly uses the centralized state for both checked status and state updates. The string comparison consistency issue from the previous review has been properly addressed.

Copy link
Copy Markdown
Contributor

@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

♻️ Duplicate comments (1)
src/components/Settings/index.ts (1)

55-60: ⚠️ Potential issue

Add JSON validation for imported settings.

The imported settings are still directly saved to localStorage without validation, which could break the application if the JSON is malformed or contains unexpected data. This issue was previously identified and remains unaddressed.

The validation should be added as suggested in the previous review to ensure robust error handling during settings import.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f00f29 and fabed54.

📒 Files selected for processing (4)
  • src/components/Settings/index.ts (4 hunks)
  • src/components/Settings/parental.ts (3 hunks)
  • src/lib/libraryUtils.ts (1 hunks)
  • src/modules/partsManager.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/Settings/parental.ts
  • src/modules/partsManager.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/components/Settings/index.ts (1)
src/lib/store.ts (1)
  • params (2-2)
src/lib/libraryUtils.ts (1)
src/lib/store.ts (1)
  • store (63-158)
🔇 Additional comments (5)
src/components/Settings/index.ts (5)

4-4: LGTM: Centralized state management import.

The import of params from the store module aligns with the centralized state management approach mentioned in the AI summary.


13-13: LGTM: Async function conversion for parental component.

The conversion to an async function and awaiting the parental() component is correct and necessary for handling the async parental component properly.

Also applies to: 15-15, 26-26


47-47: LGTM: Simplified export settings implementation.

The direct DOM element creation and simplified localStorage access using the centralized 'store' key is a good improvement that aligns with the unified state management approach.

Also applies to: 49-49


67-67: LGTM: Simplified emergency reset.

The change to only remove the 'store' key instead of clearing all localStorage (except 'library') is more precise and aligns with the centralized state management approach.


75-78: LGTM: Simplified restore button handler.

The inline event handler that directly removes the 'store' key and reloads the page is cleaner and more straightforward than the previous multi-step approach.

Comment thread src/lib/libraryUtils.ts
});

store.list.id = collection;
store.list.id = decodeURI(collection);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Good consistency improvement, but verify usage across codebase.

This change improves consistency by storing the decoded collection ID in store.list.id, which aligns with how the function uses decodeURI(collection) for database lookup on line 151. Storing data in its decoded form is generally preferable for internal state management.

Please verify that other parts of the codebase don't expect store.list.id to be URI-encoded:


🏁 Script executed:

#!/bin/bash
# Description: Check if other code expects store.list.id to be URI-encoded
# Expected: No usage that applies URI encoding to store.list.id or treats it as encoded

# Search for usage of store.list.id
rg -A 3 -B 3 "store\.list\.id"

# Search for any URI encoding operations on store.list
rg -A 3 -B 3 "encodeUR.*store\.list|store\.list.*encodeUR"

Length of output: 3354


Ensure all store.list.id assignments use decoded values

To maintain consistency with decodeURI(collection) in libraryUtils.ts, wrap other direct assignments of store.list.id in decodeURI(...).

• In src/modules/fetchList.ts

  • Replace:
    store.list.id = url.slice(type === 'playlist' ? 11 : 9);
    With:
    store.list.id = decodeURI(url.slice(type === 'playlist' ? 11 : 9));
  • Replace:
    store.list.id = id.slice(9);
    With:
    store.list.id = decodeURI(id.slice(9));

• (Optional) In src/modules/supermix.ts, the literal assignment store.list.id = 'supermix' remains safe, but if you ever parameterize it, apply decodeURI as well.

This ensures downstream lookups (e.g., in src/scripts/list.ts, router.ts, and utility calls) consistently operate on decoded IDs.

🤖 Prompt for AI Agents
In src/lib/libraryUtils.ts at line 195, you updated store.list.id to use
decodeURI(collection) for consistency with database lookups. To maintain this
consistency across the codebase, update all other direct assignments to
store.list.id to wrap the assigned value in decodeURI as well. Specifically, in
src/modules/fetchList.ts, replace assignments like store.list.id =
url.slice(...) and store.list.id = id.slice(...) with decodeURI wrapped
versions. Also, optionally consider applying decodeURI to any future
parameterized assignments like in src/modules/supermix.ts. This ensures all
store.list.id values are stored decoded for consistent downstream usage.

@n-ce n-ce merged commit 188b938 into main May 31, 2025
2 checks passed
@n-ce n-ce deleted the cloud-sync-v2 branch May 31, 2025 17:44
@coderabbitai coderabbitai Bot mentioned this pull request Jun 2, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Feb 20, 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