Skip to content

Commit

Permalink
Add feature to set a playlist as quick bookmark target (#4518)
Browse files Browse the repository at this point in the history
* + Add feature to set a playlist as quick bookmark target

* Add back button to watch page

* focus behavior style update

* * Different button text when video saved

* ! Fix mutation function `removeVideos` (still unused)

* * Make default generated favorites playlist as default quick bookmark target

* ! Add fix for `deleteVideoIdsByPlaylistId`

* * Update button text

* * Update quick bookmark playlist first allow revert later

* * Update single playlist view to hide fav button when viewing the target playlist

* - Remove unused code
  • Loading branch information
PikachuEXE authored Jan 18, 2024
1 parent ffaf3cf commit 32a2ad9
Show file tree
Hide file tree
Showing 21 changed files with 334 additions and 29 deletions.
4 changes: 2 additions & 2 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const DBActions = {
UPSERT_VIDEOS: 'db-action-playlists-upsert-videos-by-playlist-name',
DELETE_VIDEO_ID: 'db-action-playlists-delete-video-by-playlist-name',
DELETE_VIDEO_IDS: 'db-action-playlists-delete-video-ids',
DELETE_ALL_VIDEOS: 'db-action-playlists-delete-all-videos'
DELETE_ALL_VIDEOS: 'db-action-playlists-delete-all-videos',
}
}

Expand All @@ -66,7 +66,7 @@ const SyncEvents = {

PLAYLISTS: {
UPSERT_VIDEO: 'sync-playlists-upsert-video',
DELETE_VIDEO: 'sync-playlists-delete-video'
DELETE_VIDEO: 'sync-playlists-delete-video',
}
}

Expand Down
24 changes: 17 additions & 7 deletions src/datastores/handlers/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,18 +136,28 @@ class Playlists {
return db.playlists.removeAsync({ _id, protected: { $ne: true } })
}

static deleteVideoIdByPlaylistId(_id, playlistItemId) {
return db.playlists.updateAsync(
{ _id },
{ $pull: { videos: { playlistItemId } } },
{ upsert: true }
)
static deleteVideoIdByPlaylistId({ _id, videoId, playlistItemId }) {
if (playlistItemId != null) {
return db.playlists.updateAsync(
{ _id },
{ $pull: { videos: { playlistItemId } } },
{ upsert: true }
)
} else if (videoId != null) {
return db.playlists.updateAsync(
{ _id },
{ $pull: { videos: { videoId } } },
{ upsert: true }
)
} else {
throw new Error(`Both videoId & playlistItemId are absent, _id: ${_id}`)
}
}

static deleteVideoIdsByPlaylistId(_id, videoIds) {
return db.playlists.updateAsync(
{ _id },
{ $pull: { videos: { $in: videoIds } } },
{ $pull: { videos: { videoId: { $in: videoIds } } } },
{ upsert: true }
)
}
Expand Down
4 changes: 2 additions & 2 deletions src/datastores/handlers/electron.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,12 @@ class Playlists {
)
}

static deleteVideoIdByPlaylistId(_id, playlistItemId) {
static deleteVideoIdByPlaylistId({ _id, videoId, playlistItemId }) {
return ipcRenderer.invoke(
IpcChannels.DB_PLAYLISTS,
{
action: DBActions.PLAYLISTS.DELETE_VIDEO_ID,
data: { _id, playlistItemId }
data: { _id, videoId, playlistItemId }
}
)
}
Expand Down
4 changes: 2 additions & 2 deletions src/datastores/handlers/web.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ class Playlists {
return baseHandlers.playlists.delete(_id)
}

static deleteVideoIdByPlaylistId(_id, playlistItemId) {
return baseHandlers.playlists.deleteVideoIdByPlaylistId(_id, playlistItemId)
static deleteVideoIdByPlaylistId({ _id, videoId, playlistItemId }) {
return baseHandlers.playlists.deleteVideoIdByPlaylistId({ _id, videoId, playlistItemId })
}

static deleteVideoIdsByPlaylistId(_id, videoIds) {
Expand Down
6 changes: 5 additions & 1 deletion src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,11 @@ function runApp() {
return null

case DBActions.PLAYLISTS.DELETE_VIDEO_ID:
await baseHandlers.playlists.deleteVideoIdByPlaylistId(data._id, data.playlistItemId)
await baseHandlers.playlists.deleteVideoIdByPlaylistId({
_id: data._id,
videoId: data.videoId,
playlistItemId: data.playlistItemId,
})
syncOtherWindows(
IpcChannels.SYNC_PLAYLISTS,
event,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ export default defineComponent({
type: Boolean,
default: false,
},
quickBookmarkButtonEnabled: {
type: Boolean,
default: true,
},
canMoveVideoUp: {
type: Boolean,
default: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
:force-list-type="forceListType"
:appearance="appearance"
:always-show-add-to-playlist-button="alwaysShowAddToPlaylistButton"
:quick-bookmark-button-enabled="quickBookmarkButtonEnabled"
:can-move-video-up="canMoveVideoUp"
:can-move-video-down="canMoveVideoDown"
:can-remove-from-playlist="canRemoveFromPlaylist"
Expand Down
83 changes: 83 additions & 0 deletions src/renderer/components/ft-list-video/ft-list-video.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ export default defineComponent({
type: Boolean,
default: false,
},
quickBookmarkButtonEnabled: {
type: Boolean,
default: true,
},
canMoveVideoUp: {
type: Boolean,
default: false,
Expand Down Expand Up @@ -413,6 +417,36 @@ export default defineComponent({
return this.playlistIdTypePairFinal?.playlistItemId
},

quickBookmarkPlaylistId() {
return this.$store.getters.getQuickBookmarkTargetPlaylistId
},
quickBookmarkPlaylist() {
return this.$store.getters.getPlaylist(this.quickBookmarkPlaylistId)
},
isQuickBookmarkEnabled() {
return this.quickBookmarkPlaylist != null
},
isInQuickBookmarkPlaylist: function () {
if (!this.isQuickBookmarkEnabled) { return false }

return this.quickBookmarkPlaylist.videos.some((video) => {
return video.videoId === this.id
})
},
quickBookmarkIconText: function () {
if (!this.isQuickBookmarkEnabled) { return false }

const translationProperties = {
playlistName: this.quickBookmarkPlaylist.playlistName,
}
return this.isInQuickBookmarkPlaylist
? this.$t('User Playlists.Remove from Favorites', translationProperties)
: this.$t('User Playlists.Add to Favorites', translationProperties)
},
quickBookmarkIconTheme: function () {
return this.isInQuickBookmarkPlaylist ? 'base favorite' : 'base'
},

watchPageLinkTo() {
// For `router-link` attribute `to`
return {
Expand Down Expand Up @@ -782,12 +816,61 @@ export default defineComponent({
showToast(this.$t('Channel Unhidden', { channel: channelName }))
},

toggleQuickBookmarked() {
if (!this.isQuickBookmarkEnabled) {
// This should be prevented by UI
return
}

if (this.isInQuickBookmarkPlaylist) {
this.removeFromQuickBookmarkPlaylist()
} else {
this.addToQuickBookmarkPlaylist()
}
},
addToQuickBookmarkPlaylist() {
const videoData = {
videoId: this.id,
title: this.title,
author: this.channelName,
authorId: this.channelId,
description: this.description,
viewCount: this.viewCount,
lengthSeconds: this.data.lengthSeconds,
}

this.addVideos({
_id: this.quickBookmarkPlaylist._id,
videos: [videoData],
})
// Update playlist's `lastUpdatedAt`
this.updatePlaylist({ _id: this.quickBookmarkPlaylist._id })

// TODO: Maybe show playlist name
showToast(this.$t('Video.Video has been saved'))
},
removeFromQuickBookmarkPlaylist() {
this.removeVideo({
_id: this.quickBookmarkPlaylist._id,
// Remove all playlist items with same videoId
videoId: this.id,
})
// Update playlist's `lastUpdatedAt`
this.updatePlaylist({ _id: this.quickBookmarkPlaylist._id })

// TODO: Maybe show playlist name
showToast(this.$t('Video.Video has been removed from your saved list'))
},

...mapActions([
'openInExternalPlayer',
'updateHistory',
'removeFromHistory',
'updateChannelsHidden',
'showAddToPlaylistPromptForManyVideos',
'addVideos',
'updatePlaylist',
'removeVideo',
])
}
})
14 changes: 14 additions & 0 deletions src/renderer/components/ft-list-video/ft-list-video.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@
:size="appearance === `watchPlaylistItem` ? 14 : 18"
@click="togglePlaylistPrompt"
/>
<ft-icon-button
v-if="isQuickBookmarkEnabled && quickBookmarkButtonEnabled"
:title="quickBookmarkIconText"
:icon="['fas', 'star']"
class="quickBookmarkVideoIcon"
:class="{
bookmarked: isInQuickBookmarkPlaylist,
alwaysVisible: alwaysShowAddToPlaylistButton,
}"
:theme="quickBookmarkIconTheme"
:padding="appearance === `watchPlaylistItem` ? 5 : 6"
:size="appearance === `watchPlaylistItem` ? 14 : 18"
@click="toggleQuickBookmarked"
/>
<ft-icon-button
v-if="inUserPlaylist && canMoveVideoUp"
:title="$t('User Playlists.Move Video Up')"
Expand Down
47 changes: 47 additions & 0 deletions src/renderer/components/playlist-info/playlist-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,23 @@ export default defineComponent({
// Cannot delete protected playlist
return !this.hideSharingActions
},

quickBookmarkPlaylistId() {
return this.$store.getters.getQuickBookmarkTargetPlaylistId
},
quickBookmarkPlaylist() {
return this.$store.getters.getPlaylist(this.quickBookmarkPlaylistId)
},
quickBookmarkEnabled() {
return this.quickBookmarkPlaylist != null
},
markedAsQuickBookmarkTarget() {
// Only user playlists can be target
if (this.selectedUserPlaylist == null) { return false }
if (this.quickBookmarkPlaylist == null) { return false }

return this.quickBookmarkPlaylist._id === this.selectedUserPlaylist._id
},
},
watch: {
showDeletePlaylistPrompt(shown) {
Expand Down Expand Up @@ -318,10 +335,40 @@ export default defineComponent({
this.showDeletePlaylistPrompt = false
},

enableQuickBookmarkForThisPlaylist() {
const currentQuickBookmarkTargetPlaylist = this.quickBookmarkPlaylist

this.updateQuickBookmarkTargetPlaylistId(this.id)
if (currentQuickBookmarkTargetPlaylist != null) {
showToast(
this.$t('User Playlists.SinglePlaylistView.Toast["This playlist is now used for quick bookmark instead of {oldPlaylistName}. Click here to undo"]', {
oldPlaylistName: currentQuickBookmarkTargetPlaylist.playlistName,
}),
5000,
() => {
this.updateQuickBookmarkTargetPlaylistId(currentQuickBookmarkTargetPlaylist._id)
showToast(
this.$t('User Playlists.SinglePlaylistView.Toast["Reverted to use {oldPlaylistName} for quick bookmark"]', {
oldPlaylistName: currentQuickBookmarkTargetPlaylist.playlistName,
}),
5000,
)
},
)
} else {
showToast(this.$t('User Playlists.SinglePlaylistView.Toast.This playlist is now used for quick bookmark'))
}
},
disableQuickBookmark() {
this.updateQuickBookmarkTargetPlaylistId(null)
showToast(this.$t('User Playlists.SinglePlaylistView.Toast.Quick bookmark disabled'))
},

...mapActions([
'showAddToPlaylistPromptForManyVideos',
'updatePlaylist',
'removePlaylist',
'updateQuickBookmarkTargetPlaylistId',
]),
},
})
14 changes: 14 additions & 0 deletions src/renderer/components/playlist-info/playlist-info.vue
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,20 @@
theme="secondary"
@click="toggleCopyVideosPrompt"
/>
<ft-icon-button
v-if="!editMode && !markedAsQuickBookmarkTarget"
:title="$t('User Playlists.Enable Quick Bookmark With This Playlist')"
:icon="['fas', 'link']"
theme="secondary"
@click="enableQuickBookmarkForThisPlaylist"
/>
<ft-icon-button
v-if="!editMode && markedAsQuickBookmarkTarget"
:title="$t('User Playlists.Disable Quick Bookmark')"
:icon="['fas', 'link-slash']"
theme="secondary"
@click="disableQuickBookmark"
/>
<ft-icon-button
v-if="!editMode && infoSource === 'user' && videoCount > 0"
:title="$t('User Playlists.Remove Watched Videos')"
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/components/privacy-settings/privacy-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export default defineComponent({
if (option !== 'yes') { return }

this.removeAllPlaylists()
this.updateQuickBookmarkTargetPlaylistId('favorites')
showToast(this.$t('Settings.Privacy Settings.All playlists have been removed'))
},

Expand All @@ -137,6 +138,7 @@ export default defineComponent({
'updateAllSubscriptionsList',
'updateProfileSubscriptions',
'removeAllPlaylists',
'updateQuickBookmarkTargetPlaylistId',
])
}
})
Loading

0 comments on commit 32a2ad9

Please sign in to comment.