Skip to content

Commit

Permalink
Add video search in user playlist feature (FreeTubeApp#4622)
Browse files Browse the repository at this point in the history
* * Update single playlist view for user playlists to add search video function

* ! Fix load more button appears when searching & visible items under pagination limit

* * Show message when search returns no result

* * Make search button focused after existing search mode

* * Make search result show search result show original playlist item indexes

* * Make search button only appear with video count > 0
  • Loading branch information
PikachuEXE authored Mar 6, 2024
1 parent f54c45a commit 672803d
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 44 deletions.
7 changes: 6 additions & 1 deletion src/renderer/components/ft-icon-button/ft-icon-button.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ export default defineComponent({

handleResize: function () {
this.useModal = window.innerWidth <= 900
}
},

focus() {
// To be called by parent components
this.$refs.iconButton.focus()
},
}
})
1 change: 1 addition & 0 deletions src/renderer/components/ft-icon-button/ft-icon-button.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<div class="ftIconButton">
<font-awesome-icon
ref="iconButton"
class="iconButton"
:title="title"
:icon="icon"
Expand Down
32 changes: 32 additions & 0 deletions src/renderer/components/playlist-info/playlist-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ 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 {
formatNumber,
showToast,
} from '../../helpers/utils'
import debounce from 'lodash.debounce'

export default defineComponent({
name: 'PlaylistInfo',
Expand All @@ -18,6 +20,7 @@ export default defineComponent({
'ft-icon-button': FtIconButton,
'ft-input': FtInput,
'ft-prompt': FtPrompt,
'ft-button': FtButton,
},
props: {
id: {
Expand Down Expand Up @@ -83,6 +86,9 @@ export default defineComponent({
},
data: function () {
return {
searchVideoMode: false,
query: '',
updateQueryDebounce: function() {},
editMode: false,
showDeletePlaylistPrompt: false,
showRemoveVideosOnWatchPrompt: false,
Expand Down Expand Up @@ -232,6 +238,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) {
Expand Down Expand Up @@ -373,6 +381,30 @@ 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')

nextTick(() => {
// Some elements only present after rendering update
this.$refs.enableSearchModeButton?.focus()
})
},

...mapActions([
'showAddToPlaylistPromptForManyVideos',
'updatePlaylist',
Expand Down
10 changes: 10 additions & 0 deletions src/renderer/components/playlist-info/playlist-info.scss
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@
justify-content: flex-end;
}

.searchInputsRow {
margin-block-start: 8px;

display: grid;

/* 2 columns */
grid-template-columns: 1fr auto;
column-gap: 8px;
}

@media only screen and (max-width: 1250px) {
:deep(.sharePlaylistIcon .iconDropdown) {
inset-inline-start: auto;
Expand Down
31 changes: 31 additions & 0 deletions src/renderer/components/playlist-info/playlist-info.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
<hr>

<div
v-if="!searchVideoMode"
class="channelShareWrapper"
>
<router-link
Expand Down Expand Up @@ -106,6 +107,14 @@
</div>

<div class="playlistOptions">
<ft-icon-button
v-if="isUserPlaylist && videoCount > 0 && !editMode"
ref="enableSearchModeButton"
:title="$t('User Playlists.SinglePlaylistView.Search for Videos')"
:icon="['fas', 'search']"
theme="secondary"
@click="enableVideoSearchMode"
/>
<ft-icon-button
v-if="editMode"
:title="$t('User Playlists.Save Changes')"
Expand Down Expand Up @@ -187,6 +196,28 @@
@click="handleRemoveVideosOnWatchPromptAnswer"
/>
</div>

<div
v-if="isUserPlaylist && searchVideoMode"
class="searchInputsRow"
>
<ft-input
ref="searchInput"
class="searchInput"
:placeholder="$t('User Playlists.SinglePlaylistView.Search for Videos')"
:show-clear-text-button="true"
:show-action-button="false"
@input="(input) => updateQueryDebounce(input)"
@clear="updateQueryDebounce('')"
/>
<ft-icon-button
v-if="isUserPlaylist && searchVideoMode"
:title="$t('User Playlists.Cancel')"
:icon="['fas', 'times']"
theme="secondary"
@click="disableVideoSearchMode"
/>
</div>
</div>
</template>

Expand Down
25 changes: 20 additions & 5 deletions src/renderer/views/Playlist/Playlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ export default defineComponent({
getPlaylistInfoDebounce: function() {},
playlistInEditMode: false,

playlistInVideoSearchMode: false,
videoSearchQuery: '',

promptOpen: false,
}
},
Expand Down Expand Up @@ -104,7 +107,7 @@ export default defineComponent({

moreVideoDataAvailable() {
if (this.isUserPlaylistRequested) {
return this.userPlaylistVisibleLimit < this.videoCount
return this.userPlaylistVisibleLimit < this.sometimesFilteredUserPlaylistItems.length
} else {
return this.continuationData !== null
}
Expand All @@ -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)
if (this.userPlaylistVisibleLimit < this.sometimesFilteredUserPlaylistItems.length) {
return this.sometimesFilteredUserPlaylistItems.slice(0, this.userPlaylistVisibleLimit)
} else {
return this.playlistItems
return this.sometimesFilteredUserPlaylistItems
}
}
},
processedVideoSearchQuery() {
return this.videoSearchQuery.trim().toLowerCase()
},
},
watch: {
$route () {
Expand Down
90 changes: 52 additions & 38 deletions src/renderer/views/Playlist/Playlist.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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"
/>
Expand All @@ -39,48 +42,59 @@
<template
v-if="playlistItems.length > 0"
>
<transition-group
name="playlistItem"
tag="span"
<template
v-if="visiblePlaylistItems.length > 0"
>
<ft-list-video-numbered
v-for="(item, index) in visiblePlaylistItems"
:key="`${item.videoId}-${item.playlistItemId || index}`"
class="playlistItem"
:data="item"
:playlist-id="playlistId"
:playlist-type="infoSource"
:playlist-index="index"
:playlist-item-id="item.playlistItemId"
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-remove-from-playlist="true"
:video-index="index"
:initial-visible-state="index < 10"
@move-video-up="moveVideoUp(item.videoId, item.playlistItemId)"
@move-video-down="moveVideoDown(item.videoId, item.playlistItemId)"
@remove-from-playlist="removeVideoFromPlaylist(item.videoId, item.playlistItemId)"
/>
</transition-group>
<transition-group
name="playlistItem"
tag="span"
>
<ft-list-video-numbered
v-for="(item, index) in visiblePlaylistItems"
:key="`${item.videoId}-${item.playlistItemId || index}`"
class="playlistItem"
:data="item"
:playlist-id="playlistId"
:playlist-type="infoSource"
:playlist-index="playlistInVideoSearchMode ? playlistItems.findIndex(i => i === item) : index"
:playlist-item-id="item.playlistItemId"
appearance="result"
:always-show-add-to-playlist-button="true"
:quick-bookmark-button-enabled="quickBookmarkButtonEnabled"
:can-move-video-up="index > 0 && !playlistInVideoSearchMode"
:can-move-video-down="index < playlistItems.length - 1 && !playlistInVideoSearchMode"
:can-remove-from-playlist="true"
:video-index="playlistInVideoSearchMode ? playlistItems.findIndex(i => i === item) : index"
:initial-visible-state="index < 10"
@move-video-up="moveVideoUp(item.videoId, item.playlistItemId)"
@move-video-down="moveVideoDown(item.videoId, item.playlistItemId)"
@remove-from-playlist="removeVideoFromPlaylist(item.videoId, item.playlistItemId)"
/>
</transition-group>
<ft-flex-box
v-if="moreVideoDataAvailable && !isLoadingMore"
>
<ft-button
:label="$t('Subscriptions.Load More Videos')"
background-color="var(--primary-color)"
text-color="var(--text-with-main-color)"
@click="getNextPage"
/>
</ft-flex-box>
<div
v-if="isLoadingMore"
class="loadNextPageWrapper"
>
<ft-loader />
</div>
</template>
<ft-flex-box
v-if="moreVideoDataAvailable && !isLoadingMore"
v-else
>
<ft-button
:label="$t('Subscriptions.Load More Videos')"
background-color="var(--primary-color)"
text-color="var(--text-with-main-color)"
@click="getNextPage"
/>
<p class="message">
{{ $t("User Playlists['Empty Search Message']") }}
</p>
</ft-flex-box>
<div
v-if="isLoadingMore"
class="loadNextPageWrapper"
>
<ft-loader />
</div>
</template>
<ft-flex-box
v-else
Expand Down
2 changes: 2 additions & 0 deletions static/locales/en-US.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,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.
Expand Down

0 comments on commit 672803d

Please sign in to comment.