diff --git a/src/constants.js b/src/constants.js index 7c61599a0ea7b..c2997f3395da7 100644 --- a/src/constants.js +++ b/src/constants.js @@ -66,6 +66,7 @@ const DBActions = { HISTORY: { UPDATE_WATCH_PROGRESS: 20, UPDATE_PLAYLIST: 21, + UNSET_PLAYLIST: 22, }, PROFILES: { diff --git a/src/datastores/handlers/base.js b/src/datastores/handlers/base.js index 24f7765cb2c50..b21d356b49021 100644 --- a/src/datastores/handlers/base.js +++ b/src/datastores/handlers/base.js @@ -124,6 +124,17 @@ class History { return db.history.updateAsync({ videoId }, { $set: { lastViewedPlaylistId, lastViewedPlaylistType, lastViewedPlaylistItemId } }, { upsert: true }) } + static unsetLastViewedPlaylist(videoIds, playlistId) { + return db.history.updateAsync( + { + videoId: { $in: videoIds }, + lastViewedPlaylistId: playlistId + }, + { $unset: { lastViewedPlaylistId: '', lastViewedPlaylistType: '', lastViewedPlaylistItemId: '' } }, + { multi: true } + ) + } + static delete(videoId) { return db.history.removeAsync({ videoId }) } diff --git a/src/datastores/handlers/electron.js b/src/datastores/handlers/electron.js index 5e4357c04dde4..5da18342d9ef1 100644 --- a/src/datastores/handlers/electron.js +++ b/src/datastores/handlers/electron.js @@ -37,6 +37,10 @@ class History { ) } + static unsetLastViewedPlaylist(videoIds, playlistId) { + return window.ftElectron.dbHistory(DBActions.HISTORY.UNSET_PLAYLIST, { videoIds, playlistId }) + } + static delete(videoId) { return window.ftElectron.dbHistory(DBActions.GENERAL.DELETE, videoId) } diff --git a/src/main/index.js b/src/main/index.js index 52b9de779fd17..df6fdcd6d2519 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -1725,6 +1725,15 @@ function runApp() { ) return null + case DBActions.HISTORY.UNSET_PLAYLIST: + await baseHandlers.history.unsetLastViewedPlaylist(data.videoIds, data.playlistId) + syncOtherWindows( + IpcChannels.SYNC_HISTORY, + event, + { event: SyncEvents.HISTORY.UNSET_PLAYLIST, data } + ) + return null + case DBActions.GENERAL.DELETE: await baseHandlers.history.delete(data) syncOtherWindows( diff --git a/src/renderer/store/modules/history.js b/src/renderer/store/modules/history.js index dfd7c6831d0a1..557769d5c3b17 100644 --- a/src/renderer/store/modules/history.js +++ b/src/renderer/store/modules/history.js @@ -101,6 +101,15 @@ const actions = { console.error(errMessage) } }, + + async unsetLastViewedPlaylist({ commit }, { videoIds, playlistId }) { + try { + await DBHistoryHandlers.unsetLastViewedPlaylist(videoIds, playlistId) + commit('unsetRecordsLastViewedPlaylistIdInHistoryCache', { videoIds, playlistId }) + } catch (errMessage) { + console.error(errMessage) + } + }, } const mutations = { @@ -153,6 +162,22 @@ const mutations = { } }, + unsetRecordsLastViewedPlaylistIdInHistoryCache(state, { videoIds, playlistId }) { + for (const videoId of videoIds) { + // historyCacheById and historyCacheSorted reference the same object instances, + // so modifying an existing object in one of them will update both. + + const record = state.historyCacheById[videoId] + + // Don't unset if the item is not part of the watch history or if the playlist does not match + if (record && record.lastViewedPlaylistId === playlistId) { + delete record.lastViewedPlaylistId + delete record.lastViewedPlaylistType + delete record.lastViewedPlaylistItemId + } + } + }, + removeFromHistoryCacheById(state, videoId) { for (let i = 0; i < state.historyCacheSorted.length; i++) { if (state.historyCacheSorted[i].videoId === videoId) { diff --git a/src/renderer/store/modules/playlists.js b/src/renderer/store/modules/playlists.js index 04926de6104e2..e5cfc141e4a1b 100644 --- a/src/renderer/store/modules/playlists.js +++ b/src/renderer/store/modules/playlists.js @@ -369,8 +369,15 @@ const actions = { } }, - async removeAllPlaylists({ commit }) { + async removeAllPlaylists({ commit, dispatch, getters }) { try { + const playlists = getters.getAllPlaylists() + for (const playlist of playlists) { + const videoIds = playlist.videos.map(video => video.videoId) + const playlistId = playlist._id + await dispatch('unsetLastViewedPlaylist', { videoIds, playlistId }) + } + await DBPlaylistHandlers.deleteAll() commit('removeAllPlaylists') } catch (errMessage) { @@ -378,8 +385,12 @@ const actions = { } }, - async removeAllVideos({ commit }, _id) { + async removeAllVideos({ commit, dispatch, getters }, _id) { try { + const playlist = getters.getPlaylist(_id) + const videoIds = playlist.videos.map(video => video.videoId) + await dispatch('unsetLastViewedPlaylist', { videoIds, playlistId: _id }) + await DBPlaylistHandlers.deleteAllVideosByPlaylistId(_id) commit('removeAllVideos', _id) } catch (errMessage) { @@ -387,8 +398,12 @@ const actions = { } }, - async removePlaylist({ commit }, playlistId) { + async removePlaylist({ commit, dispatch, getters }, playlistId) { try { + const playlist = getters.getPlaylist(playlistId) + const videoIds = playlist.videos.map(video => video.videoId) + await dispatch('unsetLastViewedPlaylist', { videoIds, playlistId }) + await DBPlaylistHandlers.delete(playlistId) commit('removePlaylist', playlistId) } catch (errMessage) { @@ -396,8 +411,14 @@ const actions = { } }, - async removePlaylists({ commit }, playlistIds) { + async removePlaylists({ commit, dispatch, getters }, playlistIds) { try { + for (const playlistId of playlistIds) { + const playlist = getters.getPlaylist(playlistId) + const videoIds = playlist.videos.map(video => video.videoId) + await dispatch('unsetLastViewedPlaylist', { videoIds, playlistId }) + } + await DBPlaylistHandlers.deleteMultiple(playlistIds) commit('removePlaylists', playlistIds) } catch (errMessage) { @@ -405,10 +426,12 @@ const actions = { } }, - async removeVideo({ commit }, payload) { + async removeVideo({ commit, dispatch }, payload) { try { const { _id, videoId, playlistItemId } = payload + await dispatch('unsetLastViewedPlaylist', { videoIds: [videoId], playlistId: _id }) + const lastUpdatedAt = Date.now() await DBPlaylistHandlers.deleteVideoIdByPlaylistId(_id, lastUpdatedAt, videoId, playlistItemId) @@ -421,9 +444,11 @@ const actions = { } }, - async removeVideos({ commit }, payload) { + async removeVideos({ commit, dispatch }, payload) { try { - const { _id, playlistItemIds } = payload + const { _id, playlistItemIds, videoIds } = payload + + await dispatch('unsetLastViewedPlaylist', { videoIds, playlistId: _id }) const lastUpdatedAt = Date.now() diff --git a/src/renderer/views/Playlist/Playlist.vue b/src/renderer/views/Playlist/Playlist.vue index 90a8d10bfbe71..b5d6f4ef6e705 100644 --- a/src/renderer/views/Playlist/Playlist.vue +++ b/src/renderer/views/Playlist/Playlist.vue @@ -242,6 +242,8 @@ const videoSearchQuery = ref('') const promptOpen = ref(false) /** @type {import('vue').Ref} */ const toBeDeletedPlaylistItemIds = ref([]) +/** @type {import('vue').Ref} */ +const videosWithPlaylistToUnset = ref([]) /** @type {AbortController | null} */ let undoToastAbortController = null @@ -851,6 +853,7 @@ function removeVideoFromPlaylist(videoId, playlistItemId) { if (foundVideo) { toBeDeletedPlaylistItemIds.value.push(playlistItemId) + videosWithPlaylistToUnset.value.push(videoId) // Only show toast when no existing toast shown if (undoToastAbortController == null) { @@ -867,6 +870,7 @@ function removeVideoFromPlaylist(videoId, playlistItemId) { () => { clearTimeout(actualRemoveVideosTimeout) toBeDeletedPlaylistItemIds.value = [] + videosWithPlaylistToUnset.value = [] undoToastAbortController = null }, undoToastAbortController.signal, @@ -885,11 +889,13 @@ async function removeToBeDeletedVideosSometimes() { if (toBeDeletedPlaylistItemIds.value.length > 0) { await store.dispatch('removeVideos', { _id: playlistId.value, - // Create a new non-reactive array to avoid Electron erroring about Proxy objects not being clonable + // Create new non-reactive arrays to avoid Electron erroring about Proxy objects not being clonable playlistItemIds: [...toBeDeletedPlaylistItemIds.value], + videoIds: [...videosWithPlaylistToUnset.value], }) toBeDeletedPlaylistItemIds.value = [] + videosWithPlaylistToUnset.value = [] undoToastAbortController?.abort() undoToastAbortController = null }