Skip to content

uhtml refactor part 1#296

Merged
n-ce merged 13 commits into
mainfrom
uhtml
May 15, 2025
Merged

uhtml refactor part 1#296
n-ce merged 13 commits into
mainfrom
uhtml

Conversation

@n-ce
Copy link
Copy Markdown
Owner

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

Summary by CodeRabbit

  • New Features

    • Enhanced display of reserved collections ("Discover", "History", "Favorites", "Listen Later") with localized labels and icons in relevant lists.
    • Added a new "Watch" option in settings for PWA users, enabling direct video watching.
  • Bug Fixes

    • Improved collection selection logic to better handle reserved collection names.
  • Refactor

    • Simplified the library section by removing direct collection item links and their associated styles.
    • Refined share action handling to support the new "Watch" feature with dynamic component rendering.
    • Replaced previous update prompt component with a streamlined dialog using a new templating approach.
    • Removed navigation side effects related to collections and history for cleaner flow.
    • Simplified audio event handling and collection updates.
  • Chores

    • Upgraded several dependencies for improved stability and performance.
    • Added a new dependency for future enhancements.

mere and others added 4 commits May 9, 2025 09:03
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2025

Walkthrough

The changes remove the collections block and its associated styles from the library section in the HTML and CSS. The handling of reserved collection names is centralized and enhanced, with localized labels and icons added in the item loader. Dependency versions are updated, and the package metadata is adjusted. Some filtering and event handling logic are simplified. A new update prompt component is introduced and integrated using the uhtml library for rendering. A new "watch" option is added to the settings and handled in the main app start logic by dynamically rendering a watch component.

Changes

File(s) Change Summary
index.html, src/stylesheets/library.css Removed the entire <div id="collections"> block and all associated CSS, including styles for collection links, icons, and ARIA-label pseudo-elements.
package.json Removed the version field and the update script. Upgraded versions for several dependencies and devDependencies. Added uhtml as a new dependency.
src/components/ItemsLoader.tsx Added localization and icon mapping for reserved collection names. Rendering logic now checks for reserved collections and uses localized labels and specific icons; otherwise, defaults are used.
src/components/SuperCollectionList.tsx Changed collection filtering logic to explicitly exclude only channels and playlists, removing dependency on an external reserved collections list.
src/lib/utils.ts Updated the superClick function to normalize specific collection names to their internal keys before fetching collections, using a mapping object.
src/scripts/library.ts Removed the unused fetchCollection import and deleted the event listener and DOM element handling for the removed #collections block.
src/components/ActionsMenu.css Added a newline at the end of the file.
src/components/UpdatePrompt.ts Added a new asynchronous component UpdatePrompt that fetches and displays GitHub commit messages in a dialog, with buttons to trigger an update or close the dialog. Uses uhtml for templating and localized strings for labels.
src/main.ts Changed update prompt rendering to use uhtml instead of Solid.js render. Imported uhtml and asynchronously imported the update prompt component for rendering in the service worker update flow.
src/components/UpdatePrompt.tsx Deleted the previous Solid.js UpdatePrompt component that managed update dialogs with reactive signals and fetch logic.
src/components/Settings.tsx Added a new "watch" option to the shareAction selector with localized label.
src/locales/en.json Added localization key "settings_pwa_watch": "Watch".
src/modules/start.ts Refined handling of shareAction in PWA mode: added specific logic for "watch" action to dynamically import and render WatchVideo component; other actions proceed with download logic.
src/lib/libraryUtils.ts Removed the getCollection function. Modified getLocalCollection to conditionally delete low-frequency items in 'discover' only when pagination is enabled.
src/modules/setDiscoveries.ts Removed unused imports and navigation logic related to the "discover" collection.
src/scripts/audioEvents.ts Removed unused imports and simplified history update logic by eliminating navigation on audio playing.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ItemsLoader
    participant i18n
    participant UI

    User->>ItemsLoader: Loads collection items
    ItemsLoader->>ItemsLoader: Check if item is reserved collection
    alt Reserved collection
        ItemsLoader->>i18n: Get localized label
        ItemsLoader->>UI: Render with reserved icon and localized label
    else Other collection
        ItemsLoader->>UI: Render with default icon and name
    end
Loading
sequenceDiagram
    participant ServiceWorker
    participant MainApp
    participant UpdatePrompt
    participant GitHubAPI
    participant uhtml

    ServiceWorker->>MainApp: onNeedRefresh event
    MainApp->>GitHubAPI: Fetch latest commits
    GitHubAPI-->>UpdatePrompt: Return commit messages
    MainApp->>uhtml: Render UpdatePrompt dialog with commit info
    UpdatePrompt->>User: Display update dialog
    User->>UpdatePrompt: Click update button
    UpdatePrompt->>MainApp: Trigger update callback
Loading
sequenceDiagram
    participant User
    participant MainApp
    participant WatchVideo

    User->>MainApp: Trigger shareAction 'watch' in PWA
    MainApp->>MainApp: Set store.actionsMenu.id
    MainApp->>WatchVideo: Dynamically import WatchVideo component
    MainApp->>WatchVideo: Render WatchVideo in document body
Loading

Poem

A change in the warren, collections hop away,
Their links and their styles have gone astray.
Now icons and names, with language anew,
Greet every bunny in a localized view.
With code refreshed and dependencies spry,
The library leaps forward—oh my, oh my!
🐇✨

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.


Note

⚡️ Faster reviews with caching

CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.
Enjoy the performance boost—your workflow just got faster.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between d6d57f4 and e320108.

📒 Files selected for processing (1)
  • src/locales/ro.json (7 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/locales/ro.json

🪧 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: 1

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f6c4ede and d866334.

📒 Files selected for processing (8)
  • index.html (1 hunks)
  • package.json (1 hunks)
  • src/components/ActionsMenu.css (1 hunks)
  • src/components/ItemsLoader.tsx (2 hunks)
  • src/components/SuperCollectionList.tsx (2 hunks)
  • src/lib/utils.ts (1 hunks)
  • src/scripts/library.ts (1 hunks)
  • src/stylesheets/library.css (0 hunks)
💤 Files with no reviewable changes (1)
  • src/stylesheets/library.css
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/components/ItemsLoader.tsx (2)
src/lib/libraryUtils.ts (1)
  • reservedCollections (5-5)
src/lib/utils.ts (1)
  • i18n (40-45)
🔇 Additional comments (12)
src/components/ActionsMenu.css (1)

59-59: Good formatting: Added newline at end of file.

Adding a newline at the end of file follows best coding practices and is consistent with styling standards.

package.json (4)

7-7: Script cleanup looks good.

The "update" script has been removed, simplifying the scripts section to only include essential development commands.


10-14: Dependencies updated and uhtml added.

The addition of the uhtml dependency aligns with the PR objective of porting components to uhtml. The version updates for hls.js and solid-js ensure compatibility with the latest features.


16-25: DevDependencies updated to latest versions.

All development dependencies have been updated to their latest compatible versions, which is good practice for security and new features.


30-30: Package.json metadata updated.

The version field has been removed from the package metadata. This is acceptable if versioning is managed elsewhere or through git tags.

src/scripts/library.ts (1)

2-2: Removed unused fetchCollection import.

Good cleanup by removing the unused fetchCollection import. This change aligns with the removal of collection-related DOM elements and event handlers mentioned in the AI summary.

src/components/SuperCollectionList.tsx (1)

2-2: Simplified import by removing reservedCollections.

Good refactoring by removing the unused reservedCollections import, which is consistent with the changes in the filtering logic below.

index.html (1)

1-344: LGTM - Collections block has been successfully removed

The removal of the hardcoded collections block appears to be intentional and aligns with the PR objectives to port components to uhtml. Instead of having static HTML elements for collections, they will now be dynamically rendered through components.

src/lib/utils.ts (1)

182-199: Good implementation of collection name normalization

The addition of the rcn mapping object and the conditional logic in fetchCollection call effectively centralizes the handling of reserved collection names. This approach allows displaying user-friendly, localized names in the UI while using consistent internal keys for data operations.

src/components/ItemsLoader.tsx (3)

3-3: Appropriate import addition

Adding the i18n import is necessary for the localization of collection names.


9-14: Well-structured mapping for reserved collections

The reservedCollections object provides a clear mapping between collection keys and their corresponding icon classes and localization keys. This structure makes the code more maintainable and supports the centralized handling of reserved collection names.


32-41: Clean implementation of conditional rendering for collections

The conditional rendering logic using Solid.js's <Show> component elegantly handles both reserved and custom collections. For reserved collections, it correctly uses the mapping to display appropriate icons and localized labels, while maintaining a simple fallback for custom collections.

Comment thread src/components/SuperCollectionList.tsx Outdated
return keys.length ?
keys
.filter(v => !reservedCollections.includes(v))
.filter(v => v !== 'channels' && v !== 'playlists')

This comment was marked as outdated.

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

🧹 Nitpick comments (5)
src/components/UpdatePrompt.ts (3)

5-9: Add error handling for component initialization.

The component initializes variables but doesn't handle potential errors if the dialog reference isn't set correctly. Consider adding proper error handling mechanisms.


28-35: Extract event handler and improve button semantics.

The close handler is embedded directly in the template, making it harder to read. Consider extracting it to a separate function. Also, add appropriate types to the buttons for better semantics.

+  const handleClose = () => {
+    dialog.close();
+    dialog.remove();
+  };
+
  const template = html`
    <dialog
      /* attributes */
    >
      <!-- dialog content -->
      <span>
-        <button @click=${handleUpdate} autofocus>${i18n('updater_update')}</button>
-        <button @click=${() => {
-      dialog.close();
-      dialog.remove();
-    }}>${i18n('updater_later')}</button>
+        <button type="button" @click=${handleUpdate} autofocus>${i18n('updater_update')}</button>
+        <button type="button" @click=${handleClose}>${i18n('updater_later')}</button>
      </span>
    </dialog>`;

7-7: Avoid non-null assertion operator.

The non-null assertion operator (!) on the dialog variable is potentially unsafe, as it tells TypeScript to assume it will always be assigned, which might not be true.

-  let dialog!: HTMLDialogElement;
+  let dialog: HTMLDialogElement | undefined;

Then update references to check if it's defined before using:

-      dialog.close();
-      dialog.remove();
+      if (dialog) {
+        dialog.close();
+        dialog.remove();
+      }
src/main.ts (2)

32-34: Consider more explicit error handling in the dynamic import.

The dynamic import and subsequent rendering doesn't include explicit error handling. If the import fails or the component throws an error during rendering, it could lead to a broken update experience.

-          import('./components/UpdatePrompt').then(async mod =>
-            uhtml(document.body, await mod.default(handleUpdate)
-            ));
+          import('./components/UpdatePrompt')
+            .then(async mod => {
+              try {
+                const promptTemplate = await mod.default(handleUpdate);
+                uhtml(document.body, promptTemplate);
+              } catch (error) {
+                console.error('Failed to render update prompt:', error);
+                // Fallback rendering or notification
+              }
+            })
+            .catch(error => {
+              console.error('Failed to load update prompt component:', error);
+            });

8-9: Consider adding TypeScript type definitions for imported renders.

Adding type annotations to the imported render functions can help prevent potential type errors and improve development experience.

-import { render } from 'solid-js/web';
-import { render as uhtml } from 'uhtml';
+import { render as solidRender } from 'solid-js/web';
+import { render as uhtml } from 'uhtml';

Then update all references to render to use solidRender for clarity:

-  render(stngs.default, settingsContainer);
+  solidRender(stngs.default, settingsContainer);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d866334 and 4438f77.

⛔ Files ignored due to path filters (2)
  • public/logo512.png is excluded by !**/*.png
  • public/logo512old.png is excluded by !**/*.png
📒 Files selected for processing (2)
  • src/components/UpdatePrompt.ts (1 hunks)
  • src/main.ts (2 hunks)

Comment thread src/components/UpdatePrompt.ts
Comment thread src/components/UpdatePrompt.ts Outdated
Comment on lines +17 to +27
const template = html`
<dialog
id="changelog"
ref=${dialog}
open
>
<ul>
${list}
<hr />
<li @click={()=>open(${commitsLink})}>{i18n('updater_changelog_full')}</li>
</ul>
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

Fix event binding syntax and enhance accessibility.

There are issues with the event binding syntax and accessibility improvements needed:

  1. The @click syntax in line 26 uses curly braces {} instead of the template string format ${} that uhtml expects
  2. The dialog lacks proper accessibility attributes for screen readers
  const template = html`
    <dialog
      id="changelog"
      ref=${dialog}
      open
+     aria-labelledby="changelog-title"
+     role="dialog"
    >
+     <h2 id="changelog-title">Update Available</h2>
      <ul>
        ${list}
        <hr />
-        <li @click={()=>open(${commitsLink})}>{i18n('updater_changelog_full')}</li>
+        <li @click=${()=>window.open(commitsLink, '_blank')}>${i18n('updater_changelog_full')}</li>
      </ul>
📝 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 template = html`
<dialog
id="changelog"
ref=${dialog}
open
>
<ul>
${list}
<hr />
<li @click={()=>open(${commitsLink})}>{i18n('updater_changelog_full')}</li>
</ul>
const template = html`
<dialog
id="changelog"
ref=${dialog}
open
aria-labelledby="changelog-title"
role="dialog"
>
<h2 id="changelog-title">Update Available</h2>
<ul>
${list}
<hr />
<li @click=${() => window.open(commitsLink, '_blank')}>${i18n('updater_changelog_full')}</li>
</ul>

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 (2)
src/main.ts (2)

32-43: Consider improving dialog accessibility.

The dialog implementation works functionally, but lacks some accessibility attributes that would make it more compliant with WCAI guidelines.

const dialog = document.createElement('dialog') as HTMLDialogElement;
dialog.id = 'changelog';
+dialog.setAttribute('aria-modal', 'true');
+dialog.setAttribute('aria-labelledby', 'changelog-title');
dialog.open = true;
dialog.addEventListener('click', (e) => {
  const elm = e.target as HTMLButtonElement;
  if (elm.matches('#updateBtn'))
    handleUpdate();
  if (elm.matches('#laterBtn')) {
    dialog.close();
    dialog.remove();
  }
})

35-43: Event handling could be more specific.

The current event delegation approach works but could potentially trigger if clicking on child elements within elements with these IDs. Consider using more specific selectors.

dialog.addEventListener('click', (e) => {
  const elm = e.target as HTMLButtonElement;
-  if (elm.matches('#updateBtn'))
+  if (elm.id === 'updateBtn' || elm.closest('#updateBtn'))
    handleUpdate();
-  if (elm.matches('#laterBtn')) {
+  if (elm.id === 'laterBtn' || elm.closest('#laterBtn')) {
    dialog.close();
    dialog.remove();
  }
})
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3a904fb and fb082de.

📒 Files selected for processing (2)
  • src/components/UpdatePrompt.ts (1 hunks)
  • src/main.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/UpdatePrompt.ts
🔇 Additional comments (1)
src/main.ts (1)

9-9: Good addition of uhtml renderer alongside Solid.js.

The aliasing of the uhtml render function prevents naming conflicts with the existing Solid.js render function, allowing both libraries to coexist during the migration process.

Comment thread src/main.ts Outdated
Comment on lines +45 to +47
import('./components/UpdatePrompt')
.then(async mod =>
uhtml(dialog, await mod.default()));
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 for component import.

The current implementation doesn't handle potential errors if the import fails. Consider adding error handling.

import('./components/UpdatePrompt')
  .then(async mod =>
    uhtml(dialog, await mod.default()))
+  .catch(error => {
+    console.error('Failed to load update prompt:', error);
+    dialog.textContent = 'An update is available. Please refresh the page.';
+    // Add a basic refresh button
+    const refreshBtn = document.createElement('button');
+    refreshBtn.textContent = 'Update Now';
+    refreshBtn.onclick = () => handleUpdate();
+    dialog.appendChild(refreshBtn);
+  });
📝 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
import('./components/UpdatePrompt')
.then(async mod =>
uhtml(dialog, await mod.default()));
import('./components/UpdatePrompt')
.then(async mod =>
uhtml(dialog, await mod.default()))
.catch(error => {
console.error('Failed to load update prompt:', error);
dialog.textContent = 'An update is available. Please refresh the page.';
// Add a basic refresh button
const refreshBtn = document.createElement('button');
refreshBtn.textContent = 'Update Now';
refreshBtn.onclick = () => handleUpdate();
dialog.appendChild(refreshBtn);
});

@n-ce n-ce changed the title Port components to uhtml uhtml refactor part 1 May 15, 2025
@n-ce n-ce merged commit 9cbe8ef into main May 15, 2025
2 checks passed
@n-ce n-ce deleted the uhtml branch May 15, 2025 06:05
@coderabbitai coderabbitai Bot mentioned this pull request Jun 2, 2025
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