Преглед изворни кода

feat: add done status toggle to video action menus

in the popup menu, in the main view, each video has an actions menu. add an option to mark that video as "done" (if it's anything other than "done"), or to remove the status if it is "done". add this option to the sub-menus of the playlist-specific view as well.

```git-revs
a244bb4  (Base revision)
20b147a  Add toggleVideoDoneStatus method to handle marking videos as done or removing done status
9336e6c  Add isVideoDone helper method and toggleVideoDoneButton handler
655b538  Add toggleVideoDoneButton handler
2301bf3  Add done toggle button to main view more menu
25af88f  Add done toggle button to playlist-specific view more menu
696ac39  Add method to get done button text for CSP compliance
0a822b0  Update HTML to use precomputed doneButtonText for CSP compliance
ed22b09  Update playlist-specific view to use precomputed doneButtonText for CSP compliance
HEAD     Save context window contents for done status toggle feature
```

codemcp-id: 8-feat-add-done-status-toggle-to-video-action-menus
Brandon Wong пре 1 година
родитељ
комит
6dc199914c

+ 45 - 0
2025-05-31-codemcp-feat-add-done-status-toggle-to-video-action-menus.md

@@ -0,0 +1,45 @@
+# 2025-05-31 CodeMCP: Add Done Status Toggle to Video Action Menus
+
+## User Request
+in the popup menu, in the main view, each video has an actions menu. add an option to mark that video as "done" (if it's anything other than "done"), or to remove the status if it is "done". add this option to the sub-menus of the playlist-specific view as well.
+
+## Implementation Summary
+
+Added functionality to toggle the "done" status of videos in both the main playlist view and the individual playlist view. The implementation includes:
+
+### Changes Made
+
+1. **Added `toggleVideoDoneStatus` method** in `popup/popup.js`:
+   - Toggles video status between "done" and undefined (not done)
+   - Updates playlist data in browser storage
+   - Refreshes display after changes
+
+2. **Added `isVideoDone` helper method** in `popup/popup.js`:
+   - Checks if a specific video has "done" status
+
+3. **Added `toggleVideoDoneButton` handler** in `popup/popup.js`:
+   - Click handler that calls `toggleVideoDoneStatus` and closes menus
+   - CSP-compliant event binding
+
+4. **Updated `updatePlaylistsForDisplay` and `updateCurrentPlaylistVideos` methods**:
+   - Pre-compute `doneButtonText` property for CSP compliance
+   - Avoids JavaScript expressions in HTML templates
+
+5. **Updated HTML templates** in `popup/popup.html`:
+   - Added done toggle button to both main view and playlist-specific view more menus
+   - Used pre-computed `doneButtonText` property instead of JavaScript expressions
+   - Maintains CSP compliance by avoiding expressions like `video.status === 'done' ? ... : ...`
+
+### Key Features
+
+- **Toggle Functionality**: Videos can be marked as "done" or have their done status removed
+- **Dual View Support**: Available in both truncated main view and full playlist view
+- **CSP Compliance**: All Alpine.js code follows CSP restrictions with no JavaScript expressions in HTML
+- **Consistent UI**: Integrates seamlessly with existing more menu system
+- **Storage Integration**: Persists changes to browser storage and updates all views
+
+### Technical Details
+
+The done status is stored as a `status: "done"` property on video objects. When removing done status, the property is deleted rather than set to a different value, maintaining consistency with the existing codebase logic that checks for `video.status === "done"`.
+
+The implementation maintains full CSP compliance by pre-computing button text in JavaScript methods rather than using expressions in HTML attributes.

+ 16 - 0
popup/popup.html

@@ -95,6 +95,14 @@
                         :data-playlist-index="video.originalIndex"
                         x-bind="moreMenu"
                       >
+                        <button
+                          :data-playlist-name="playlistData.name"
+                          :data-playlist-index="video.originalIndex"
+                          x-bind="toggleVideoDoneButton"
+                          class="menu-item done-item"
+                          x-text="video.doneButtonText"
+                        >
+                        </button>
                         <button
                           :data-playlist-name="playlistData.name"
                           :data-playlist-index="video.originalIndex"
@@ -211,6 +219,14 @@
                     :data-playlist-index="index"
                     x-bind="moreMenu"
                   >
+                    <button
+                      :data-playlist-name="currentPlaylistName"
+                      :data-playlist-index="index"
+                      x-bind="toggleVideoDoneButton"
+                      class="menu-item done-item"
+                      x-text="video.doneButtonText"
+                    >
+                    </button>
                     <button
                       :data-playlist-name="currentPlaylistName"
                       :data-playlist-index="index"

+ 54 - 1
popup/popup.js

@@ -87,6 +87,7 @@ document.addEventListener("alpine:init", () => {
             .map((video, index) => ({
               ...video,
               originalIndex: currentIndex + index,
+              doneButtonText: video.status === "done" ? "Remove Done Status" : "Mark as Done",
             }));
 
           return {
@@ -107,7 +108,10 @@ document.addEventListener("alpine:init", () => {
       ) {
         this.currentPlaylistVideos = [];
       } else {
-        this.currentPlaylistVideos = this.playlists[this.currentPlaylistName];
+        this.currentPlaylistVideos = this.playlists[this.currentPlaylistName].map((video) => ({
+          ...video,
+          doneButtonText: video.status === "done" ? "Remove Done Status" : "Mark as Done",
+        }));
       }
     },
 
@@ -361,6 +365,40 @@ document.addEventListener("alpine:init", () => {
       }
     },
 
+    async toggleVideoDoneStatus(playlistName, index) {
+      const playlists = JSON.parse(JSON.stringify(this.playlists));
+      const playlist = [...playlists[playlistName]];
+      const video = {...playlist[index]};
+
+      // Toggle the done status
+      if (video.status === "done") {
+        // Remove status property (undefined status means not done)
+        delete video.status;
+      } else {
+        // Set status to done
+        video.status = "done";
+      }
+
+      // Update the video in the playlist
+      playlist[index] = video;
+
+      // Create an updated playlists object
+      const updatedPlaylists = {
+        ...playlists,
+        [playlistName]: playlist,
+      };
+
+      // Update the playlists in storage
+      try {
+        await browser.storage.local.set({ playlists: updatedPlaylists });
+        this.playlists = updatedPlaylists;
+        this.updatePlaylistsForDisplay();
+        this.updateCurrentPlaylistVideos();
+      } catch (error) {
+        console.error("Error toggling video done status:", error);
+      }
+    },
+
     async addCurrentPageToPlaylist() {
       if (
         !this.currentTab ||
@@ -454,6 +492,11 @@ document.addEventListener("alpine:init", () => {
       return index < currentIndex;
     },
 
+    isVideoDone(playlistName, index) {
+      const video = this.playlists[playlistName] && this.playlists[playlistName][index];
+      return video && video.status === "done";
+    },
+
     videoItemClass: {
       [":class"]() {
         const playlistName = this.$el.dataset.playlistName;
@@ -478,6 +521,16 @@ document.addEventListener("alpine:init", () => {
       },
     },
 
+    toggleVideoDoneButton: {
+      ["@click"]() {
+        this.toggleVideoDoneStatus(
+          this.$el.dataset.playlistName,
+          parseInt(this.$el.dataset.playlistIndex),
+        );
+        this.closeAllMenus();
+      },
+    },
+
     moveUpButton: {
       ["@click"]() {
         this.moveVideoUp(