From e9251db64767626c6550dec83595ae2a7c7af53c Mon Sep 17 00:00:00 2001 From: advplyr Date: Fri, 3 Nov 2023 15:53:38 -0500 Subject: [PATCH] Add more menu on playlist item row to show library item more menu #914 --- components/modals/ItemMoreMenuModal.vue | 473 ++++++++++++++++++ components/tables/playlist/ItemTableRow.vue | 38 +- .../tables/playlist/PlaylistItemsTable.vue | 8 +- pages/item/_id/index.vue | 276 +--------- pages/playlist/_id.vue | 19 +- 5 files changed, 535 insertions(+), 279 deletions(-) create mode 100644 components/modals/ItemMoreMenuModal.vue diff --git a/components/modals/ItemMoreMenuModal.vue b/components/modals/ItemMoreMenuModal.vue new file mode 100644 index 00000000..23a179b0 --- /dev/null +++ b/components/modals/ItemMoreMenuModal.vue @@ -0,0 +1,473 @@ + + + \ No newline at end of file diff --git a/components/tables/playlist/ItemTableRow.vue b/components/tables/playlist/ItemTableRow.vue index c2a46e7b..5889849b 100644 --- a/components/tables/playlist/ItemTableRow.vue +++ b/components/tables/playlist/ItemTableRow.vue @@ -1,7 +1,7 @@ @@ -36,6 +41,10 @@ export default { return {} }, computed: { + itemUrl() { + if (this.episodeId) return `/item/${this.libraryItem.id}/${this.episodeId}` + return `/item/${this.libraryItem.id}` + }, libraryItem() { return this.item.libraryItem || {} }, @@ -57,6 +66,12 @@ export default { mediaMetadata() { return this.media.metadata || {} }, + mediaType() { + return this.libraryItem.mediaType + }, + isPodcast() { + return this.mediaType === 'podcast' + }, tracks() { if (this.episode) return [] return this.media.tracks || [] @@ -109,10 +124,23 @@ export default { return !!this.userItemProgress?.isFinished }, progressPercent() { - return this.userItemProgress ? Math.max(Math.min(1, this.userItemProgress.progress), 0) : 0 + return Math.max(Math.min(1, this.userItemProgress?.progress || 0), 0) } }, methods: { + showMore() { + const playlistItem = { + libraryItem: this.libraryItem, + episode: this.episode + } + if (this.localLibraryItem) { + playlistItem.libraryItem.localLibraryItem = this.localLibraryItem + } + if (this.localEpisode && playlistItem.episode) { + playlistItem.episode.localEpisode = this.localEpisode + } + this.$emit('showMore', playlistItem) + }, async playClick() { await this.$hapticsImpact() if (this.streamIsPlaying) { @@ -138,7 +166,7 @@ export default { \ No newline at end of file diff --git a/components/tables/playlist/PlaylistItemsTable.vue b/components/tables/playlist/PlaylistItemsTable.vue index 292fe270..28a10892 100644 --- a/components/tables/playlist/PlaylistItemsTable.vue +++ b/components/tables/playlist/PlaylistItemsTable.vue @@ -11,7 +11,7 @@

{{ totalDurationPretty }}

@@ -41,7 +41,11 @@ export default { return this.$elapsedPrettyExtended(this.totalDuration) } }, - methods: {}, + methods: { + showMore(playlistItem) { + this.$emit('showMore', playlistItem) + } + }, mounted() {} } diff --git a/pages/item/_id/index.vue b/pages/item/_id/index.vue index 5cd9078d..6f6c8ba4 100644 --- a/pages/item/_id/index.vue +++ b/pages/item/_id/index.vue @@ -50,7 +50,7 @@ -
+

Your Progress: {{ Math.round(progressPercent * 100) }}%

{{ $elapsedPretty(userTimeRemaining) }} remaining

Finished {{ $formatDate(userProgressFinishedAt) }}

@@ -126,13 +126,10 @@ + + - - - - -
@@ -181,12 +178,8 @@ export default { data() { return { processing: false, - resettingProgress: false, - isProcessingReadUpdate: false, showSelectLocalFolder: false, showMoreMenu: false, - showDetailsModal: false, - showSendEbookDevicesModal: false, showFullscreenCover: false, coverRgb: 'rgb(55, 56, 56)', coverBgIsLight: false, @@ -318,7 +311,7 @@ export default { return this.$store.getters['user/getUserMediaProgress'](this.serverLibraryItemId) }, userIsFinished() { - return this.userItemProgress ? !!this.userItemProgress.isFinished : false + return !!this.userItemProgress?.isFinished }, userTimeRemaining() { if (!this.userItemProgress) return 0 @@ -331,10 +324,10 @@ export default { }, progressPercent() { if (this.useEBookProgress) return Math.max(Math.min(1, this.userItemProgress.ebookProgress), 0) - return this.userItemProgress ? Math.max(Math.min(1, this.userItemProgress.progress), 0) : 0 + return Math.max(Math.min(1, this.userItemProgress?.progress || 0), 0) }, userProgressFinishedAt() { - return this.userItemProgress ? this.userItemProgress.finishedAt : 0 + return this.userItemProgress?.finishedAt || 0 }, isStreaming() { return this.isPlaying && !this.$store.getters['getIsCurrentSessionLocal'] @@ -394,100 +387,6 @@ export default { isCasting() { return this.$store.state.isCasting }, - showRSSFeedOption() { - if (!this.serverLibraryItemId) return false - if (!this.rssFeed && !this.episodes.length && !this.tracks.length) return false // Cannot open RSS feed with no episodes/tracks - - // If rss feed is open then show feed url to users otherwise just show to admins - return this.userIsAdminOrUp || this.rssFeed - }, - moreMenuItems() { - const items = [] - - if (!this.isPodcast) { - // TODO: Implement on iOS - if (!this.isIos) { - items.push({ - text: 'History', - value: 'history', - icon: 'history' - }) - } - - if (!this.userIsFinished) { - items.push({ - text: 'Mark as Finished', - value: 'markFinished', - icon: 'beenhere' - }) - } - - if (this.progressPercent > 0) { - items.push({ - text: 'Discard Progress', - value: 'discardProgress', - icon: 'backspace' - }) - } - } - - if (!this.isPodcast && this.serverLibraryItemId) { - items.push({ - text: 'Add to Playlist', - value: 'playlist', - icon: 'playlist_add' - }) - - if (this.ebookFile && this.$store.state.libraries.ereaderDevices?.length) { - items.push({ - text: 'Send ebook to device', - value: 'sendEbook', - icon: 'send' - }) - } - } - - if (this.showRSSFeedOption) { - items.push({ - text: this.rssFeed ? 'RSS Feed' : 'Open RSS Feed', - value: 'rssFeed', - icon: 'rss_feed' - }) - } - - if (this.localLibraryItemId) { - items.push({ - text: 'Manage Local Files', - value: 'manageLocal', - icon: 'folder' - }) - - if (!this.isPodcast) { - items.push({ - text: 'Delete Local Item', - value: 'deleteLocal', - icon: 'delete' - }) - } - } - - items.push({ - text: 'More Info', - value: 'details', - icon: 'info' - }) - - return items - }, - ereaderDeviceItems() { - if (!this.ebookFile || !this.$store.state.libraries.ereaderDevices?.length) return [] - return this.$store.state.libraries.ereaderDevices.map((d) => { - return { - text: d.name, - value: d.name - } - }) - }, coverWidth() { let width = this.windowWidth - 94 if (width > 325) return 325 @@ -495,42 +394,9 @@ export default { if (width * this.bookCoverAspectRatio > 325) width = 325 / this.bookCoverAspectRatio return width - }, - mediaId() { - if (this.isPodcast) return null - return this.serverLibraryItemId || this.localLibraryItemId } }, methods: { - async deleteLocalItem() { - await this.$hapticsImpact() - - let confirmMessage = 'Remove local files of this item from your device?' - if (this.serverLibraryItemId) { - confirmMessage += ' The files on the server and your progress will be unaffected.' - } - const { value } = await Dialog.confirm({ - title: 'Confirm', - message: confirmMessage - }) - if (value) { - const res = await AbsFileSystem.deleteItem(this.localLibraryItem) - if (res?.success) { - this.$toast.success('Deleted successfully') - if (this.isLocal) { - // If local then redirect to server version when available - if (this.serverLibraryItemId) { - this.$router.replace(`/item/${this.serverLibraryItemId}`) - } else { - this.$router.replace('/bookshelf') - } - } else { - // Remove localLibraryItem - this.$delete(this.libraryItem, 'localLibraryItem') - } - } else this.$toast.error('Failed to delete') - } - }, async coverImageLoaded(fullCoverUrl) { if (!fullCoverUrl) return @@ -545,64 +411,7 @@ export default { console.log(e) }) }, - moreMenuAction(action) { - this.showMoreMenu = false - if (action === 'manageLocal') { - this.$nextTick(() => { - this.$router.push(`/localMedia/item/${this.localLibraryItemId}`) - }) - } else if (action === 'details') { - this.showDetailsModal = true - } else if (action === 'playlist') { - this.$store.commit('globals/setSelectedPlaylistItems', [{ libraryItem: this.libraryItem, episode: null }]) - this.$store.commit('globals/setShowPlaylistsAddCreateModal', true) - } else if (action === 'markFinished') { - if (this.isProcessingReadUpdate) return - this.toggleFinished() - } else if (action === 'history') { - this.$router.push(`/media/${this.mediaId}/history?title=${this.title}`) - } else if (action === 'discardProgress') { - this.clearProgressClick() - } else if (action === 'deleteLocal') { - this.deleteLocalItem() - } else if (action === 'rssFeed') { - this.clickRSSFeed() - } else if (action === 'sendEbook') { - this.showSendEbookDevicesModal = true - } - }, - sendEbookToDeviceAction(deviceName) { - this.showSendEbookDevicesModal = false - - const payload = { - libraryItemId: this.serverLibraryItemId, - deviceName - } - this.processing = true - this.$nativeHttp - .post(`/api/emails/send-ebook-to-device`, payload) - .then(() => { - this.$toast.success('Ebook sent successfully') - }) - .catch((error) => { - console.error('Failed to send ebook to device', error) - this.$toast.error('Failed to send ebook to device') - }) - .finally(() => { - this.processing = false - }) - }, - clickRSSFeed() { - this.$store.commit('globals/setRSSFeedOpenCloseModal', { - id: this.serverLibraryItemId, - name: this.title, - type: 'item', - feed: this.rssFeed, - hasEpisodesWithoutPubDate: this.episodes.some((ep) => !ep.pubDate) - }) - }, moreButtonPress() { - this.showSendEbookDevicesModal = false this.showMoreMenu = true }, readBook() { @@ -683,37 +492,6 @@ export default { this.$eventBus.$emit('play-item', { libraryItemId, serverLibraryItemId: this.serverLibraryItemId, startTime }) } }, - async clearProgressClick() { - await this.$hapticsImpact() - - const { value } = await Dialog.confirm({ - title: 'Confirm', - message: 'Are you sure you want to reset your progress?' - }) - if (value) { - this.resettingProgress = true - const serverMediaProgressId = this.serverItemProgress?.id - if (this.localLibraryItemId) { - await this.$db.removeLocalMediaProgress(this.localLibraryItemId) - this.$store.commit('globals/removeLocalMediaProgressForItem', this.localLibraryItemId) - } - - if (this.serverLibraryItemId && serverMediaProgressId) { - await this.$nativeHttp - .delete(`/api/me/progress/${serverMediaProgressId}`) - .then(() => { - console.log('Progress reset complete') - this.$toast.success(`Your progress was reset`) - this.$store.commit('user/removeMediaProgress', serverMediaProgressId) - }) - .catch((error) => { - console.error('Progress reset failed', error) - }) - } - - this.resettingProgress = false - } - }, itemUpdated(libraryItem) { if (libraryItem.id === this.serverLibraryItemId) { console.log('Item Updated') @@ -815,48 +593,6 @@ export default { this.$set(this.libraryItem, 'localLibraryItem', item) } }, - async toggleFinished() { - await this.$hapticsImpact() - - // Show confirm if item has progress since it will reset - if (this.userItemProgress && this.userItemProgress.progress > 0 && !this.userIsFinished) { - const { value } = await Dialog.confirm({ - title: 'Confirm', - message: 'Are you sure you want to mark this item as Finished?' - }) - if (!value) return - } - - this.isProcessingReadUpdate = true - if (this.isLocal) { - const isFinished = !this.userIsFinished - const payload = await this.$db.updateLocalMediaProgressFinished({ localLibraryItemId: this.localLibraryItemId, isFinished }) - console.log('toggleFinished payload', JSON.stringify(payload)) - if (payload?.error) { - this.$toast.error(payload?.error || 'Unknown error') - } else { - const localMediaProgress = payload.localMediaProgress - console.log('toggleFinished localMediaProgress', JSON.stringify(localMediaProgress)) - if (localMediaProgress) { - this.$store.commit('globals/updateLocalMediaProgress', localMediaProgress) - } - } - this.isProcessingReadUpdate = false - } else { - const updatePayload = { - isFinished: !this.userIsFinished - } - this.$nativeHttp - .patch(`/api/me/progress/${this.libraryItemId}`, updatePayload) - .catch((error) => { - console.error('Failed', error) - this.$toast.error(`Failed to mark as ${updatePayload.isFinished ? 'Finished' : 'Not Finished'}`) - }) - .finally(() => { - this.isProcessingReadUpdate = false - }) - } - }, libraryChanged(libraryId) { if (this.libraryItem.libraryId !== libraryId) { this.$router.replace('/bookshelf') diff --git a/pages/playlist/_id.vue b/pages/playlist/_id.vue index b6623fca..a2278dd6 100644 --- a/pages/playlist/_id.vue +++ b/pages/playlist/_id.vue @@ -20,9 +20,14 @@

{{ description }}

- + + + +
+ +
@@ -67,7 +72,12 @@ export default { } }, data() { - return {} + return { + showMoreMenu: false, + processing: false, + selectedLibraryItem: null, + selectedEpisode: null + } }, computed: { bookCoverAspectRatio() { @@ -101,6 +111,11 @@ export default { } }, methods: { + showMore(playlistItem) { + this.selectedLibraryItem = playlistItem.libraryItem + this.selectedEpisode = playlistItem.episode + this.showMoreMenu = true + }, clickPlay() { const nextItem = this.playableItems.find((i) => { const prog = this.$store.getters['user/getUserMediaProgress'](i.libraryItemId, i.episodeId)