Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature to set a playlist as quick bookmark target #4518

Merged
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
15 changes: 14 additions & 1 deletion src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -995,14 +995,27 @@ 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,
{ event: SyncEvents.PLAYLISTS.DELETE_VIDEO, data }
)
return null

case DBActions.PLAYLISTS.SET_QUICK_SAVE_TARGET_PLAYLIST:
absidue marked this conversation as resolved.
Show resolved Hide resolved
await baseHandlers.playlists.setPlaylistAsQuickBookmarkTarget(data)
syncOtherWindows(
IpcChannels.SYNC_PLAYLISTS,
event,
{ event: SyncEvents.PLAYLISTS.SET_QUICK_SAVE_TARGET_PLAYLIST, data }
)
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)
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 @@ -66,6 +66,10 @@ export default defineComponent({
type: Boolean,
default: false,
},
quickBookmarkButtonEnabled: {
type: Boolean,
default: true,
},
canMoveVideoUp: {
type: Boolean,
default: false,
Expand Down Expand Up @@ -397,6 +401,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 @@ -729,12 +763,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