From d7a0810778151e3809b9a4fe414f0ac3a0d44a92 Mon Sep 17 00:00:00 2001 From: Soham Patil <57618502+Soham456@users.noreply.github.com> Date: Mon, 20 Jan 2025 23:08:07 +0530 Subject: [PATCH] Undo Feature For Remove From Playlist. (#5885) * changed logic to work with Custom as well. * updated removeVideos * linted * resolving conflict * updated changes --- src/constants.js | 1 + src/datastores/handlers/base.js | 4 +- src/datastores/handlers/electron.js | 4 +- src/main/index.js | 9 ++-- src/renderer/store/modules/playlists.js | 11 ++-- src/renderer/store/modules/settings.js | 4 ++ src/renderer/views/Playlist/Playlist.js | 69 +++++++++++++++++++------ static/locales/en-US.yaml | 1 + 8 files changed, 76 insertions(+), 27 deletions(-) diff --git a/src/constants.js b/src/constants.js index 573d72e080357..bc08d42fbbcd0 100644 --- a/src/constants.js +++ b/src/constants.js @@ -109,6 +109,7 @@ const SyncEvents = { UPSERT_VIDEO: 'sync-playlists-upsert-video', UPSERT_VIDEOS: 'sync-playlists-upsert-videos', DELETE_VIDEO: 'sync-playlists-delete-video', + DELETE_VIDEOS: 'sync-playlists-delete-videos', }, SUBSCRIPTION_CACHE: { diff --git a/src/datastores/handlers/base.js b/src/datastores/handlers/base.js index d25963376f641..5063814b162a2 100644 --- a/src/datastores/handlers/base.js +++ b/src/datastores/handlers/base.js @@ -201,10 +201,10 @@ class Playlists { } } - static deleteVideoIdsByPlaylistId(_id, videoIds) { + static deleteVideoIdsByPlaylistId(_id, playlistItemIds) { return db.playlists.updateAsync( { _id }, - { $pull: { videos: { videoId: { $in: videoIds } } } }, + { $pull: { videos: { playlistItemId: { $in: playlistItemIds } } } }, { upsert: true } ) } diff --git a/src/datastores/handlers/electron.js b/src/datastores/handlers/electron.js index 7a178174f0db0..bba658582dd50 100644 --- a/src/datastores/handlers/electron.js +++ b/src/datastores/handlers/electron.js @@ -183,12 +183,12 @@ class Playlists { ) } - static deleteVideoIdsByPlaylistId(_id, videoIds) { + static deleteVideoIdsByPlaylistId(_id, playlistItemIds) { return ipcRenderer.invoke( IpcChannels.DB_PLAYLISTS, { action: DBActions.PLAYLISTS.DELETE_VIDEO_IDS, - data: { _id, videoIds } + data: { _id, playlistItemIds } } ) } diff --git a/src/main/index.js b/src/main/index.js index f8bfc8877f2f4..25434ea1a8ae8 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -1354,9 +1354,12 @@ function runApp() { return null case DBActions.PLAYLISTS.DELETE_VIDEO_IDS: - await baseHandlers.playlists.deleteVideoIdsByPlaylistId(data._id, data.videoIds) - // TODO: Syncing (implement only when it starts being used) - // syncOtherWindows(IpcChannels.SYNC_PLAYLISTS, event, { event: '_', data }) + await baseHandlers.playlists.deleteVideoIdsByPlaylistId(data._id, data.playlistItemIds) + syncOtherWindows( + IpcChannels.SYNC_PLAYLISTS, + event, + { event: SyncEvents.PLAYLISTS.DELETE_VIDEOS, data } + ) return null case DBActions.PLAYLISTS.DELETE_ALL_VIDEOS: diff --git a/src/renderer/store/modules/playlists.js b/src/renderer/store/modules/playlists.js index 528e6b46b9f5b..d0780eaa74b1f 100644 --- a/src/renderer/store/modules/playlists.js +++ b/src/renderer/store/modules/playlists.js @@ -417,8 +417,8 @@ const actions = { async removeVideos({ commit }, payload) { try { - const { _id, videoIds } = payload - await DBPlaylistHandlers.deleteVideoIdsByPlaylistId(_id, videoIds) + const { _id, playlistItemIds } = payload + await DBPlaylistHandlers.deleteVideoIdsByPlaylistId(_id, playlistItemIds) commit('removeVideos', payload) } catch (errMessage) { console.error(errMessage) @@ -484,10 +484,13 @@ const mutations = { } }, - removeVideos(state, { _id, videoId }) { + removeVideos(state, { _id, playlistItemIds }) { const playlist = state.playlists.find(playlist => playlist._id === _id) if (playlist) { - playlist.videos = playlist.videos.filter(video => videoId.indexOf(video) === -1) + playlist.videos = playlist.videos.filter(video => { + const playlistItemIdMatches = playlistItemIds.includes(video.playlistItemId) + return !playlistItemIdMatches + }) } }, diff --git a/src/renderer/store/modules/settings.js b/src/renderer/store/modules/settings.js index 8577e6e259cf7..79166523790a6 100644 --- a/src/renderer/store/modules/settings.js +++ b/src/renderer/store/modules/settings.js @@ -573,6 +573,10 @@ const customActions = { commit('removeVideo', data) break + case SyncEvents.PLAYLISTS.DELETE_VIDEOS: + commit('removeVideos', data) + break + default: console.error('playlists: invalid sync event received') } diff --git a/src/renderer/views/Playlist/Playlist.js b/src/renderer/views/Playlist/Playlist.js index 42b42d59605fa..c3edb557ee193 100644 --- a/src/renderer/views/Playlist/Playlist.js +++ b/src/renderer/views/Playlist/Playlist.js @@ -72,7 +72,7 @@ export default defineComponent({ userPlaylistVisibleLimit: 100, continuationData: null, isLoadingMore: false, - getPlaylistInfoDebounce: function() {}, + getPlaylistInfoDebounce: function () { }, playlistInEditMode: false, forceListView: false, alreadyShownNotice: false, @@ -80,6 +80,9 @@ export default defineComponent({ videoSearchQuery: '', promptOpen: false, + deletedVideoIds: [], + deletedPlaylistItemIds: [], + isUndoToast: false } }, computed: { @@ -101,7 +104,7 @@ export default defineComponent({ currentLocale: function () { return this.$i18n.locale }, - playlistId: function() { + playlistId: function () { return this.$route.params.id }, listType: function () { @@ -126,7 +129,7 @@ export default defineComponent({ return [] } }, - selectedUserPlaylistVideoCount: function() { + selectedUserPlaylistVideoCount: function () { return this.selectedUserPlaylistVideos.length }, @@ -242,25 +245,25 @@ export default defineComponent({ }, }, watch: { - $route () { + $route() { // react to route changes... this.getPlaylistInfoDebounce() }, - userPlaylistsReady () { + userPlaylistsReady() { // Fetch from local store when playlist data ready if (!this.isUserPlaylistRequested) { return } this.getPlaylistInfoDebounce() }, - selectedUserPlaylist () { + selectedUserPlaylist() { // Fetch from local store when current user playlist changed this.getPlaylistInfoDebounce() }, - selectedUserPlaylistLastUpdatedAt () { + selectedUserPlaylistLastUpdatedAt() { // Re-fetch from local store when current user playlist updated this.getPlaylistInfoDebounce() }, - selectedUserPlaylistVideoCount () { + selectedUserPlaylistVideoCount() { // Monitoring `selectedUserPlaylistVideos` makes this function called // Even when the same array object is returned // So length is monitored instead @@ -586,14 +589,48 @@ export default defineComponent({ removeVideoFromPlaylist: function (videoId, playlistItemId) { try { - this.removeVideo({ - _id: this.playlistId, - videoId: videoId, - playlistItemId: playlistItemId, + const playlistItems = [].concat(this.playlistItems) + const tempPlaylistItems = [].concat(this.playlistItems) + let isUndoClicked = false + + const videoIndex = this.playlistItems.findIndex((video) => { + return video.videoId === videoId && video.playlistItemId === playlistItemId }) - // Update playlist's `lastUpdatedAt` - this.updatePlaylist({ _id: this.playlistId }) - showToast(this.$t('User Playlists.SinglePlaylistView.Toast.Video has been removed')) + + if (videoIndex !== -1) { + this.deletedVideoIds.push(this.playlistItems[videoIndex].videoId) + this.deletedPlaylistItemIds.push(this.playlistItems[videoIndex].playlistItemId) + playlistItems.splice(videoIndex, 1) + this.playlistItems = playlistItems + + // Only show toast when no existing toast shown + if (!this.isUndoToast) { + this.isUndoToast = true + showToast( + this.$t('User Playlists.SinglePlaylistView.Toast["Video has been removed. Click here to undo."]'), + 5000, + () => { + this.playlistItems = tempPlaylistItems + isUndoClicked = true + this.isUndoToast = false + this.deletedVideoIds = [] + this.deletedPlaylistItemIds = [] + } + ) + setTimeout(() => { + if (!isUndoClicked) { + this.removeVideos({ + _id: this.playlistId, + videoIds: this.deletedVideoIds, + playlistItemIds: this.deletedPlaylistItemIds, + }) + this.deletedVideoIds = [] + this.deletedPlaylistItemIds = [] + this.isUndoToast = false + } + }, 5000) + } + } } catch (e) { showToast(this.$t('User Playlists.SinglePlaylistView.Toast.There was a problem with removing this video')) console.error(e) @@ -648,7 +685,7 @@ export default defineComponent({ 'updateSubscriptionDetails', 'updatePlaylist', 'updateUserPlaylistSortOrder', - 'removeVideo', + 'removeVideos', ]), ...mapMutations([ diff --git a/static/locales/en-US.yaml b/static/locales/en-US.yaml index 1aa09c602390c..8086e769748e8 100644 --- a/static/locales/en-US.yaml +++ b/static/locales/en-US.yaml @@ -214,6 +214,7 @@ User Playlists: This video cannot be moved up.: This video cannot be moved up. This video cannot be moved down.: This video cannot be moved down. Video has been removed: Video has been removed + Video has been removed. Click here to undo.: Video has been removed. Click here to undo. There was a problem with removing this video: There was a problem with removing this video This playlist is already being used for quick bookmark.: This playlist is already being used for quick bookmark.