diff --git a/src/renderer/components/playlist-info/playlist-info.js b/src/renderer/components/playlist-info/playlist-info.js index 7deaf2d387100..22cf724666799 100644 --- a/src/renderer/components/playlist-info/playlist-info.js +++ b/src/renderer/components/playlist-info/playlist-info.js @@ -5,9 +5,11 @@ import FtFlexBox from '../ft-flex-box/ft-flex-box.vue' import FtIconButton from '../ft-icon-button/ft-icon-button.vue' import FtInput from '../ft-input/ft-input.vue' import FtPrompt from '../ft-prompt/ft-prompt.vue' +import FtButton from '../ft-button/ft-button.vue' import { showToast, } from '../../helpers/utils' +import debounce from 'lodash.debounce' export default defineComponent({ name: 'PlaylistInfo', @@ -17,6 +19,7 @@ export default defineComponent({ 'ft-icon-button': FtIconButton, 'ft-input': FtInput, 'ft-prompt': FtPrompt, + 'ft-button': FtButton, }, props: { id: { @@ -82,6 +85,9 @@ export default defineComponent({ }, data: function () { return { + searchVideoMode: false, + query: '', + updateQueryDebounce: function() {}, editMode: false, showDeletePlaylistPrompt: false, showRemoveVideosOnWatchPrompt: false, @@ -223,6 +229,8 @@ export default defineComponent({ created: function () { this.newTitle = this.title this.newDescription = this.description + + this.updateQueryDebounce = debounce(this.updateQuery, 500) }, methods: { toggleCopyVideosPrompt: function (force = false) { @@ -364,6 +372,25 @@ export default defineComponent({ showToast(this.$t('User Playlists.SinglePlaylistView.Toast.Quick bookmark disabled')) }, + updateQuery(query) { + this.query = query + this.$emit('search-video-query-change', query) + }, + enableVideoSearchMode() { + this.searchVideoMode = true + this.$emit('search-video-mode-on') + + nextTick(() => { + // Some elements only present after rendering update + this.$refs.searchInput.focus() + }) + }, + disableVideoSearchMode() { + this.searchVideoMode = false + this.updateQuery('') + this.$emit('search-video-mode-off') + }, + ...mapActions([ 'showAddToPlaylistPromptForManyVideos', 'updatePlaylist', diff --git a/src/renderer/components/playlist-info/playlist-info.scss b/src/renderer/components/playlist-info/playlist-info.scss index 3fb13c0c0fe8a..be8766bfa89c2 100644 --- a/src/renderer/components/playlist-info/playlist-info.scss +++ b/src/renderer/components/playlist-info/playlist-info.scss @@ -72,3 +72,13 @@ column-gap: 8px; justify-content: flex-end; } + +.searchInputsRow { + margin-block-start: 8px; + + display: grid; + + /* 2 columns */ + grid-template-columns: 1fr auto; + column-gap: 8px; +} diff --git a/src/renderer/components/playlist-info/playlist-info.vue b/src/renderer/components/playlist-info/playlist-info.vue index 4dd1f1e95bb01..249a017aa347f 100644 --- a/src/renderer/components/playlist-info/playlist-info.vue +++ b/src/renderer/components/playlist-info/playlist-info.vue @@ -76,6 +76,7 @@
+
+ +
+ + +
diff --git a/src/renderer/views/Playlist/Playlist.js b/src/renderer/views/Playlist/Playlist.js index 601284fd9a891..42f374faa5d97 100644 --- a/src/renderer/views/Playlist/Playlist.js +++ b/src/renderer/views/Playlist/Playlist.js @@ -60,6 +60,9 @@ export default defineComponent({ getPlaylistInfoDebounce: function() {}, playlistInEditMode: false, + playlistInVideoSearchMode: false, + videoSearchQuery: '', + promptOpen: false, } }, @@ -123,17 +126,29 @@ export default defineComponent({ return this.selectedUserPlaylist?._id !== this.quickBookmarkPlaylistId }, + sometimesFilteredUserPlaylistItems() { + if (!this.isUserPlaylistRequested) { return this.playlistItems } + if (this.processedVideoSearchQuery === '') { return this.playlistItems } + + return this.playlistItems.filter((v) => { + return v.title.toLowerCase().includes(this.processedVideoSearchQuery) + }) + }, visiblePlaylistItems: function () { if (!this.isUserPlaylistRequested) { + // No filtering for non user playlists yet return this.playlistItems } if (this.userPlaylistVisibleLimit < this.videoCount) { - return this.playlistItems.slice(0, this.userPlaylistVisibleLimit) + return this.sometimesFilteredUserPlaylistItems.slice(0, this.userPlaylistVisibleLimit) } else { - return this.playlistItems + return this.sometimesFilteredUserPlaylistItems } - } + }, + processedVideoSearchQuery() { + return this.videoSearchQuery.trim().toLowerCase() + }, }, watch: { $route () { diff --git a/src/renderer/views/Playlist/Playlist.vue b/src/renderer/views/Playlist/Playlist.vue index 81d022609632b..40c6ae5682673 100644 --- a/src/renderer/views/Playlist/Playlist.vue +++ b/src/renderer/views/Playlist/Playlist.vue @@ -28,6 +28,9 @@ }" @enter-edit-mode="playlistInEditMode = true" @exit-edit-mode="playlistInEditMode = false" + @search-video-mode-on="playlistInVideoSearchMode = true" + @search-video-mode-off="playlistInVideoSearchMode = false" + @search-video-query-change="(v) => videoSearchQuery = v" @prompt-open="promptOpen = true" @prompt-close="promptOpen = false" /> @@ -55,8 +58,8 @@ appearance="result" :always-show-add-to-playlist-button="true" :quick-bookmark-button-enabled="quickBookmarkButtonEnabled" - :can-move-video-up="index > 0" - :can-move-video-down="index < visiblePlaylistItems.length - 1" + :can-move-video-up="index > 0 && !playlistInVideoSearchMode" + :can-move-video-down="index < playlistItems.length - 1 && !playlistInVideoSearchMode" :can-remove-from-playlist="true" :video-index="index" :initial-visible-state="index < 10" diff --git a/static/locales/en-US.yaml b/static/locales/en-US.yaml index b8fc52b81a3b5..78a7ad475be6a 100644 --- a/static/locales/en-US.yaml +++ b/static/locales/en-US.yaml @@ -186,6 +186,8 @@ User Playlists: EarliestPlayedFirst: 'Earliest Played' SinglePlaylistView: + Search for Videos: Search for Videos + Toast: This video cannot be moved up.: This video cannot be moved up. This video cannot be moved down.: This video cannot be moved down.