mirror of
https://github.com/FreeTubeApp/FreeTube
synced 2024-11-22 09:56:23 +01:00
Compare commits
20 Commits
6bfdd3faeb
...
8874b14515
Author | SHA1 | Date | |
---|---|---|---|
|
8874b14515 | ||
|
dc65eece29 | ||
|
540a5a9385 | ||
|
03f887ecf1 | ||
|
cdc64d078d | ||
|
49cd4490a0 | ||
|
1be051e086 | ||
|
707f950e7f | ||
|
52ad0951e0 | ||
|
5d7cac4276 | ||
|
54f4331ed7 | ||
|
ce7c712bb2 | ||
|
47222b9e54 | ||
|
2f8fa91b32 | ||
|
d7ce3286d7 | ||
|
39b407926f | ||
|
66103aee06 | ||
|
65c41b897f | ||
|
7c0c69dc42 | ||
|
70983739f6 |
@ -207,6 +207,7 @@
|
||||
v-if="showSearchBar"
|
||||
ref="searchBar"
|
||||
:placeholder="$t('Channel.Search Channel')"
|
||||
:value="props.query"
|
||||
:show-clear-text-button="true"
|
||||
class="channelSearch"
|
||||
:maxlength="255"
|
||||
@ -275,7 +276,11 @@ const props = defineProps({
|
||||
currentTab: {
|
||||
type: String,
|
||||
default: 'videos'
|
||||
}
|
||||
},
|
||||
query: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['change-tab', 'search', 'subscribed'])
|
||||
|
@ -44,6 +44,7 @@ import {
|
||||
getLocalPlaylist,
|
||||
parseLocalPlaylistVideo
|
||||
} from '../../helpers/api/local'
|
||||
import { isNavigationFailure, NavigationFailureType } from 'vue-router'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Channel',
|
||||
@ -60,8 +61,10 @@ export default defineComponent({
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
skipRouteChangeWatcherOnce: false,
|
||||
isLoading: true,
|
||||
isElementListLoading: false,
|
||||
isSearchTabLoading: false,
|
||||
currentTab: 'videos',
|
||||
id: '',
|
||||
/** @type {import('youtubei.js').YT.Channel|null} */
|
||||
@ -294,10 +297,22 @@ export default defineComponent({
|
||||
|
||||
return values
|
||||
},
|
||||
|
||||
isCurrentTabLoading() {
|
||||
if (this.currentTab === 'search') {
|
||||
return this.isSearchTabLoading
|
||||
}
|
||||
|
||||
return this.isElementListLoading
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
// react to route changes...
|
||||
if (this.skipRouteChangeWatcherOnce) {
|
||||
this.skipRouteChangeWatcherOnce = false
|
||||
return
|
||||
}
|
||||
this.isLoading = true
|
||||
|
||||
if (this.$route.query.url) {
|
||||
@ -354,8 +369,9 @@ export default defineComponent({
|
||||
|
||||
// Re-enable auto refresh on sort value change AFTER update done
|
||||
if (!process.env.SUPPORTS_LOCAL_API || this.backendPreference === 'invidious') {
|
||||
this.getChannelInfoInvidious()
|
||||
this.autoRefreshOnSortByChangeEnabled = true
|
||||
this.getChannelInfoInvidious().finally(() => {
|
||||
this.autoRefreshOnSortByChangeEnabled = true
|
||||
})
|
||||
} else {
|
||||
this.getChannelLocal().finally(() => {
|
||||
this.autoRefreshOnSortByChangeEnabled = true
|
||||
@ -432,9 +448,9 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
mounted: async function () {
|
||||
if (this.$route.query.url) {
|
||||
this.resolveChannelUrl(this.$route.query.url, this.$route.params.currentTab)
|
||||
await this.resolveChannelUrl(this.$route.query.url, this.$route.params.currentTab)
|
||||
return
|
||||
}
|
||||
|
||||
@ -450,12 +466,18 @@ export default defineComponent({
|
||||
|
||||
// Enable auto refresh on sort value change AFTER initial update done
|
||||
if (!process.env.SUPPORTS_LOCAL_API || this.backendPreference === 'invidious') {
|
||||
this.getChannelInfoInvidious()
|
||||
this.autoRefreshOnSortByChangeEnabled = true
|
||||
} else {
|
||||
this.getChannelLocal().finally(() => {
|
||||
await this.getChannelInfoInvidious().finally(() => {
|
||||
this.autoRefreshOnSortByChangeEnabled = true
|
||||
})
|
||||
} else {
|
||||
await this.getChannelLocal().finally(() => {
|
||||
this.autoRefreshOnSortByChangeEnabled = true
|
||||
})
|
||||
}
|
||||
|
||||
const oldQuery = this.$route.query.searchQueryText ?? ''
|
||||
if (oldQuery !== null && oldQuery !== '') {
|
||||
this.newSearch(oldQuery)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -1010,7 +1032,7 @@ export default defineComponent({
|
||||
this.channelInstance = null
|
||||
|
||||
const expectedId = this.id
|
||||
invidiousGetChannelInfo(this.id).then((response) => {
|
||||
return invidiousGetChannelInfo(this.id).then((response) => {
|
||||
if (expectedId !== this.id) {
|
||||
return
|
||||
}
|
||||
@ -1872,13 +1894,14 @@ export default defineComponent({
|
||||
const newTabNode = document.getElementById(`${tab}Tab`)
|
||||
this.currentTab = tab
|
||||
newTabNode?.focus()
|
||||
this.showOutlines()
|
||||
// Prevents outline shown in strange places
|
||||
if (newTabNode != null) { this.showOutlines() }
|
||||
},
|
||||
|
||||
newSearch: function (query) {
|
||||
this.lastSearchQuery = query
|
||||
this.searchContinuationData = null
|
||||
this.isElementListLoading = true
|
||||
this.isSearchTabLoading = true
|
||||
this.searchPage = 1
|
||||
this.searchResults = []
|
||||
this.changeTab('search')
|
||||
@ -1891,6 +1914,10 @@ export default defineComponent({
|
||||
break
|
||||
}
|
||||
},
|
||||
newSearchWithStatePersist(query) {
|
||||
this.saveStateInRouter(query)
|
||||
this.newSearch(query)
|
||||
},
|
||||
|
||||
searchChannelLocal: async function () {
|
||||
const isNewSearch = this.searchContinuationData === null
|
||||
@ -1929,7 +1956,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
this.searchContinuationData = result.has_continuation ? result : null
|
||||
this.isElementListLoading = false
|
||||
this.isSearchTabLoading = false
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
const errorMessage = this.$t('Local API Error (Click to copy)')
|
||||
@ -1965,7 +1992,7 @@ export default defineComponent({
|
||||
} else {
|
||||
this.searchResults = this.searchResults.concat(response)
|
||||
}
|
||||
this.isElementListLoading = false
|
||||
this.isSearchTabLoading = false
|
||||
this.searchPage++
|
||||
}).catch((err) => {
|
||||
console.error(err)
|
||||
@ -2009,6 +2036,37 @@ export default defineComponent({
|
||||
})
|
||||
},
|
||||
|
||||
async saveStateInRouter(query) {
|
||||
this.skipRouteChangeWatcherOnce = true
|
||||
if (query === '') {
|
||||
await this.$router.replace({ path: `/channel/${this.id}` }).catch(failure => {
|
||||
if (isNavigationFailure(failure, NavigationFailureType.duplicated)) {
|
||||
return
|
||||
}
|
||||
|
||||
throw failure
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
await this.$router.replace({
|
||||
path: `/channel/${this.id}`,
|
||||
params: {
|
||||
currentTab: 'search',
|
||||
},
|
||||
query: {
|
||||
searchQueryText: query,
|
||||
},
|
||||
}).catch(failure => {
|
||||
if (isNavigationFailure(failure, NavigationFailureType.duplicated)) {
|
||||
return
|
||||
}
|
||||
|
||||
throw failure
|
||||
})
|
||||
this.skipRouteChangeWatcherOnce = false
|
||||
},
|
||||
|
||||
getIconForSortPreference: (s) => getIconForSortPreference(s),
|
||||
|
||||
...mapActions([
|
||||
|
@ -17,9 +17,10 @@
|
||||
:is-subscribed="isSubscribed"
|
||||
:visible-tabs="tabInfoValues"
|
||||
:current-tab="currentTab"
|
||||
:query="lastSearchQuery"
|
||||
class="card channelDetails"
|
||||
@change-tab="changeTab"
|
||||
@search="newSearch"
|
||||
@search="(v) => newSearchWithStatePersist(v)"
|
||||
@subscribed="handleSubscription"
|
||||
/>
|
||||
<ft-card
|
||||
@ -80,7 +81,7 @@
|
||||
/>
|
||||
</div>
|
||||
<ft-loader
|
||||
v-if="isElementListLoading"
|
||||
v-if="isCurrentTabLoading"
|
||||
/>
|
||||
<div
|
||||
v-if="currentTab !== 'about' && !isElementListLoading"
|
||||
@ -200,7 +201,7 @@
|
||||
:use-channels-hidden-preference="false"
|
||||
/>
|
||||
<ft-flex-box
|
||||
v-if="currentTab === 'search' && searchResults.length === 0"
|
||||
v-if="currentTab === 'search' && !isSearchTabLoading && searchResults.length === 0"
|
||||
>
|
||||
<p class="message">
|
||||
{{ $t("Channel.Your search results have returned 0 results") }}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { defineComponent } from 'vue'
|
||||
import { isNavigationFailure, NavigationFailureType } from 'vue-router'
|
||||
import debounce from 'lodash.debounce'
|
||||
import FtLoader from '../../components/ft-loader/ft-loader.vue'
|
||||
import FtCard from '../../components/ft-card/ft-card.vue'
|
||||
@ -63,42 +64,68 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
query() {
|
||||
this.searchDataLimit = 100
|
||||
this.filterHistoryAsync()
|
||||
},
|
||||
fullData() {
|
||||
this.filterHistory()
|
||||
},
|
||||
doCaseSensitiveSearch() {
|
||||
this.filterHistory()
|
||||
this.saveStateInRouter()
|
||||
},
|
||||
},
|
||||
mounted: function () {
|
||||
created: function () {
|
||||
document.addEventListener('keydown', this.keyboardShortcutHandler)
|
||||
const limit = sessionStorage.getItem('historyLimit')
|
||||
|
||||
if (limit !== null) {
|
||||
this.dataLimit = limit
|
||||
const oldDataLimit = sessionStorage.getItem('History/dataLimit')
|
||||
if (oldDataLimit !== null) {
|
||||
this.dataLimit = oldDataLimit
|
||||
}
|
||||
|
||||
this.activeData = this.fullData
|
||||
|
||||
this.showLoadMoreButton = this.activeData.length < this.historyCacheSorted.length
|
||||
|
||||
this.filterHistoryDebounce = debounce(this.filterHistory, 500)
|
||||
|
||||
const oldQuery = this.$route.query.searchQueryText ?? ''
|
||||
if (oldQuery !== null && oldQuery !== '') {
|
||||
// `handleQueryChange` must be called after `filterHistoryDebounce` assigned
|
||||
this.handleQueryChange(
|
||||
oldQuery,
|
||||
{
|
||||
limit: this.$route.query.searchDataLimit,
|
||||
doCaseSensitiveSearch: this.$route.query.doCaseSensitiveSearch === 'true',
|
||||
filterNow: true,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
// Only display unfiltered data when no query used last time
|
||||
this.filterHistory()
|
||||
}
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
document.removeEventListener('keydown', this.keyboardShortcutHandler)
|
||||
},
|
||||
methods: {
|
||||
handleQueryChange(query, { limit = null, doCaseSensitiveSearch = null, filterNow = false } = {}) {
|
||||
this.query = query
|
||||
|
||||
const newLimit = limit ?? 100
|
||||
this.searchDataLimit = newLimit
|
||||
const newDoCaseSensitiveSearch = doCaseSensitiveSearch ?? this.doCaseSensitiveSearch
|
||||
this.doCaseSensitiveSearch = newDoCaseSensitiveSearch
|
||||
|
||||
this.saveStateInRouter({
|
||||
query: query,
|
||||
searchDataLimit: newLimit,
|
||||
doCaseSensitiveSearch: newDoCaseSensitiveSearch,
|
||||
})
|
||||
|
||||
filterNow ? this.filterHistory() : this.filterHistoryAsync()
|
||||
},
|
||||
|
||||
increaseLimit: function () {
|
||||
if (this.query !== '') {
|
||||
this.searchDataLimit += 100
|
||||
this.filterHistory()
|
||||
} else {
|
||||
this.dataLimit += 100
|
||||
sessionStorage.setItem('historyLimit', this.dataLimit)
|
||||
sessionStorage.setItem('History/dataLimit', this.dataLimit)
|
||||
}
|
||||
},
|
||||
filterHistoryAsync: function() {
|
||||
@ -122,6 +149,36 @@ export default defineComponent({
|
||||
this.activeData = filteredQuery.length < this.searchDataLimit ? filteredQuery : filteredQuery.slice(0, this.searchDataLimit)
|
||||
this.showLoadMoreButton = this.activeData.length > this.searchDataLimit
|
||||
},
|
||||
|
||||
async saveStateInRouter({ query = this.query, searchDataLimit = this.searchDataLimit, doCaseSensitiveSearch = this.doCaseSensitiveSearch } = {}) {
|
||||
if (query === '') {
|
||||
await this.$router.replace({ name: 'history' }).catch(failure => {
|
||||
if (isNavigationFailure(failure, NavigationFailureType.duplicated)) {
|
||||
return
|
||||
}
|
||||
|
||||
throw failure
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const routerQuery = {
|
||||
searchQueryText: query,
|
||||
searchDataLimit: searchDataLimit,
|
||||
}
|
||||
if (doCaseSensitiveSearch) { routerQuery.doCaseSensitiveSearch = 'true' }
|
||||
await this.$router.replace({
|
||||
name: 'history',
|
||||
query: routerQuery,
|
||||
}).catch(failure => {
|
||||
if (isNavigationFailure(failure, NavigationFailureType.duplicated)) {
|
||||
return
|
||||
}
|
||||
|
||||
throw failure
|
||||
})
|
||||
},
|
||||
|
||||
keyboardShortcutHandler: function (event) {
|
||||
ctrlFHandler(event, this.$refs.searchBar)
|
||||
},
|
||||
|
@ -15,8 +15,9 @@
|
||||
:placeholder="$t('History.Search bar placeholder')"
|
||||
:show-clear-text-button="true"
|
||||
:show-action-button="false"
|
||||
@input="(input) => query = input"
|
||||
@clear="query = ''"
|
||||
:value="query"
|
||||
@input="(input) => handleQueryChange(input)"
|
||||
@clear="() => handleQueryChange('')"
|
||||
/>
|
||||
<div
|
||||
class="optionsRow"
|
||||
|
@ -26,6 +26,7 @@ import { invidiousGetPlaylistInfo, youtubeImageUrlToInvidious } from '../../help
|
||||
import { getSortedPlaylistItems, videoDurationPresent, videoDurationWithFallback, SORT_BY_VALUES } from '../../helpers/playlists'
|
||||
import packageDetails from '../../../../package.json'
|
||||
import { MOBILE_WIDTH_THRESHOLD, PLAYLIST_HEIGHT_FORCE_LIST_THRESHOLD } from '../../../constants'
|
||||
import { isNavigationFailure, NavigationFailureType } from 'vue-router'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Playlist',
|
||||
@ -270,7 +271,7 @@ export default defineComponent({
|
||||
this.getPlaylistInfoDebounce = debounce(this.getPlaylistInfo, 100)
|
||||
|
||||
if (this.isUserPlaylistRequested && this.searchQueryTextPresent) {
|
||||
this.videoSearchQuery = this.searchQueryTextRequested
|
||||
this.handleVideoSearchQueryChange(this.searchQueryTextRequested)
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
@ -609,6 +610,32 @@ export default defineComponent({
|
||||
this.forceListView = window.innerWidth <= MOBILE_WIDTH_THRESHOLD || window.innerHeight <= PLAYLIST_HEIGHT_FORCE_LIST_THRESHOLD
|
||||
},
|
||||
|
||||
handleVideoSearchQueryChange(val) {
|
||||
this.videoSearchQuery = val
|
||||
|
||||
this.saveStateInRouter(val)
|
||||
},
|
||||
|
||||
async saveStateInRouter(query) {
|
||||
const routeQuery = {
|
||||
playlistType: this.$route.query.playlistType,
|
||||
}
|
||||
if (query !== '') {
|
||||
routeQuery.searchQueryText = query
|
||||
}
|
||||
|
||||
await this.$router.replace({
|
||||
path: `/playlist/${this.playlistId}`,
|
||||
query: routeQuery,
|
||||
}).catch(failure => {
|
||||
if (isNavigationFailure(failure, NavigationFailureType.duplicated)) {
|
||||
return
|
||||
}
|
||||
|
||||
throw failure
|
||||
})
|
||||
},
|
||||
|
||||
getIconForSortPreference: (s) => getIconForSortPreference(s),
|
||||
|
||||
...mapActions([
|
||||
|
@ -37,7 +37,7 @@
|
||||
class="playlistInfo"
|
||||
@enter-edit-mode="playlistInEditMode = true"
|
||||
@exit-edit-mode="playlistInEditMode = false"
|
||||
@search-video-query-change="(v) => videoSearchQuery = v"
|
||||
@search-video-query-change="(v) => handleVideoSearchQueryChange(v)"
|
||||
@prompt-open="promptOpen = true"
|
||||
@prompt-close="promptOpen = false"
|
||||
/>
|
||||
|
@ -7,6 +7,8 @@ import FtSubscribeButton from '../../components/ft-subscribe-button/ft-subscribe
|
||||
import { invidiousGetChannelInfo, youtubeImageUrlToInvidious, invidiousImageUrlToInvidious } from '../../helpers/api/invidious'
|
||||
import { getLocalChannel, parseLocalChannelHeader } from '../../helpers/api/local'
|
||||
import { ctrlFHandler } from '../../helpers/utils'
|
||||
import { isNavigationFailure, NavigationFailureType } from 'vue-router'
|
||||
import debounce from 'lodash.debounce'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SubscribedChannels',
|
||||
@ -78,9 +80,17 @@ export default defineComponent({
|
||||
this.filterChannels()
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
created: function () {
|
||||
document.addEventListener('keydown', this.keyboardShortcutHandler)
|
||||
|
||||
this.filterChannelsDebounce = debounce(this.filterChannels, 500)
|
||||
this.getSubscription()
|
||||
|
||||
const oldQuery = this.$route.query.searchQueryText ?? ''
|
||||
if (oldQuery !== null && oldQuery !== '') {
|
||||
// `handleQueryChange` must be called after `filterHistoryDebounce` assigned
|
||||
this.handleQueryChange(oldQuery, true)
|
||||
}
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
document.removeEventListener('keydown', this.keyboardShortcutHandler)
|
||||
@ -92,11 +102,6 @@ export default defineComponent({
|
||||
})
|
||||
},
|
||||
|
||||
handleInput: function(input) {
|
||||
this.query = input
|
||||
this.filterChannels()
|
||||
},
|
||||
|
||||
filterChannels: function () {
|
||||
if (this.query === '') {
|
||||
this.filteredChannels = []
|
||||
@ -109,6 +114,11 @@ export default defineComponent({
|
||||
return re.test(channel.name)
|
||||
})
|
||||
},
|
||||
filterChannelsAsync: function() {
|
||||
// Updating list on every char input could be wasting resources on rendering
|
||||
// So run it with delay (to be cancelled when more input received within time)
|
||||
this.filterChannelsDebounce()
|
||||
},
|
||||
|
||||
thumbnailURL: function(originalURL) {
|
||||
if (originalURL == null) { return null }
|
||||
@ -161,6 +171,38 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
|
||||
handleQueryChange(val, filterNow = false) {
|
||||
this.query = val
|
||||
|
||||
this.saveStateInRouter(val)
|
||||
|
||||
filterNow ? this.filterChannels() : this.filterChannelsAsync()
|
||||
},
|
||||
|
||||
async saveStateInRouter(query) {
|
||||
if (this.query === '') {
|
||||
await this.$router.replace({ name: 'subscribedChannels' }).catch(failure => {
|
||||
if (isNavigationFailure(failure, NavigationFailureType.duplicated)) {
|
||||
return
|
||||
}
|
||||
|
||||
throw failure
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
await this.$router.replace({
|
||||
name: 'subscribedChannels',
|
||||
query: { searchQueryText: query },
|
||||
}).catch(failure => {
|
||||
if (isNavigationFailure(failure, NavigationFailureType.duplicated)) {
|
||||
return
|
||||
}
|
||||
|
||||
throw failure
|
||||
})
|
||||
},
|
||||
|
||||
keyboardShortcutHandler: function (event) {
|
||||
ctrlFHandler(event, this.$refs.searchBarChannels)
|
||||
},
|
||||
|
@ -6,12 +6,13 @@
|
||||
v-show="subscribedChannels.length > 0"
|
||||
ref="searchBarChannels"
|
||||
:placeholder="$t('Channels.Search bar placeholder')"
|
||||
:value="query"
|
||||
:show-clear-text-button="true"
|
||||
:show-action-button="false"
|
||||
:spellcheck="false"
|
||||
:maxlength="255"
|
||||
@input="handleInput"
|
||||
@clear="query = ''"
|
||||
@input="(input) => handleQueryChange(input)"
|
||||
@clear="() => handleQueryChange('')"
|
||||
/>
|
||||
<ft-flex-box
|
||||
v-if="activeSubscriptionList.length === 0"
|
||||
|
@ -13,6 +13,7 @@ import FtIconButton from '../../components/ft-icon-button/ft-icon-button.vue'
|
||||
import FtToggleSwitch from '../../components/ft-toggle-switch/ft-toggle-switch.vue'
|
||||
import FtAutoLoadNextPageWrapper from '../../components/ft-auto-load-next-page-wrapper/ft-auto-load-next-page-wrapper.vue'
|
||||
import { ctrlFHandler, getIconForSortPreference } from '../../helpers/utils'
|
||||
import { isNavigationFailure, NavigationFailureType } from 'vue-router'
|
||||
|
||||
const SORT_BY_VALUES = {
|
||||
NameAscending: 'name_ascending',
|
||||
@ -174,6 +175,7 @@ export default defineComponent({
|
||||
doSearchPlaylistsWithMatchingVideos() {
|
||||
this.searchDataLimit = 100
|
||||
this.filterPlaylistAsync()
|
||||
this.saveStateInRouter()
|
||||
},
|
||||
fullData() {
|
||||
this.activeData = this.fullData
|
||||
@ -183,9 +185,9 @@ export default defineComponent({
|
||||
sessionStorage.setItem('UserPlaylists/sortBy', this.sortBy)
|
||||
},
|
||||
},
|
||||
mounted: function () {
|
||||
created: function () {
|
||||
document.addEventListener('keydown', this.keyboardShortcutHandler)
|
||||
const limit = sessionStorage.getItem('favoritesLimit')
|
||||
const limit = sessionStorage.getItem('UserPlaylists/dataLimit')
|
||||
if (limit !== null) {
|
||||
this.dataLimit = limit
|
||||
}
|
||||
@ -195,23 +197,53 @@ export default defineComponent({
|
||||
this.sortBy = sortBy
|
||||
}
|
||||
|
||||
this.activeData = this.fullData
|
||||
|
||||
this.showLoadMoreButton = this.activeData.length < this.allPlaylists.length
|
||||
|
||||
this.filterPlaylistDebounce = debounce(this.filterPlaylist, 500)
|
||||
|
||||
const oldQuery = this.$route.query.searchQueryText ?? ''
|
||||
if (oldQuery !== null && oldQuery !== '') {
|
||||
// `handleQueryChange` must be called after `filterHistoryDebounce` assigned
|
||||
this.handleQueryChange(
|
||||
oldQuery,
|
||||
{
|
||||
limit: this.$route.query.searchDataLimit,
|
||||
doSearchPlaylistsWithMatchingVideos: this.$route.query.doSearchPlaylistsWithMatchingVideos === 'true',
|
||||
filterNow: true,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
// Only display unfiltered data when no query used last time
|
||||
this.filterPlaylist()
|
||||
}
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
document.removeEventListener('keydown', this.keyboardShortcutHandler)
|
||||
},
|
||||
methods: {
|
||||
handleQueryChange(query, { limit = null, doSearchPlaylistsWithMatchingVideos = null, filterNow = false } = {}) {
|
||||
this.query = query
|
||||
|
||||
const newLimit = limit ?? 100
|
||||
this.searchDataLimit = newLimit
|
||||
const newDoSearchPlaylistsWithMatchingVideos = doSearchPlaylistsWithMatchingVideos ?? this.doSearchPlaylistsWithMatchingVideos
|
||||
this.doSearchPlaylistsWithMatchingVideos = newDoSearchPlaylistsWithMatchingVideos
|
||||
|
||||
this.saveStateInRouter({
|
||||
query: query,
|
||||
searchDataLimit: newLimit,
|
||||
doSearchPlaylistsWithMatchingVideos: newDoSearchPlaylistsWithMatchingVideos,
|
||||
})
|
||||
|
||||
filterNow ? this.filterPlaylist() : this.filterPlaylistAsync()
|
||||
},
|
||||
|
||||
increaseLimit: function () {
|
||||
if (this.query !== '') {
|
||||
this.searchDataLimit += 100
|
||||
this.saveStateInRouter()
|
||||
this.filterPlaylist()
|
||||
} else {
|
||||
this.dataLimit += 100
|
||||
sessionStorage.setItem('favoritesLimit', this.dataLimit)
|
||||
sessionStorage.setItem('UserPlaylists/dataLimit', this.dataLimit)
|
||||
}
|
||||
},
|
||||
filterPlaylistAsync: function() {
|
||||
@ -247,6 +279,35 @@ export default defineComponent({
|
||||
})
|
||||
},
|
||||
|
||||
async saveStateInRouter({ query = this.query, searchDataLimit = this.searchDataLimit, doSearchPlaylistsWithMatchingVideos = this.doSearchPlaylistsWithMatchingVideos } = {}) {
|
||||
if (this.query === '') {
|
||||
await this.$router.replace({ name: 'userPlaylists' }).catch(failure => {
|
||||
if (isNavigationFailure(failure, NavigationFailureType.duplicated)) {
|
||||
return
|
||||
}
|
||||
|
||||
throw failure
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const routerQuery = {
|
||||
searchQueryText: query,
|
||||
searchDataLimit: searchDataLimit,
|
||||
}
|
||||
if (doSearchPlaylistsWithMatchingVideos) { routerQuery.doSearchPlaylistsWithMatchingVideos = 'true' }
|
||||
await this.$router.replace({
|
||||
name: 'userPlaylists',
|
||||
query: routerQuery,
|
||||
}).catch(failure => {
|
||||
if (isNavigationFailure(failure, NavigationFailureType.duplicated)) {
|
||||
return
|
||||
}
|
||||
|
||||
throw failure
|
||||
})
|
||||
},
|
||||
|
||||
keyboardShortcutHandler: function (event) {
|
||||
ctrlFHandler(event, this.$refs.searchBar)
|
||||
},
|
||||
|
@ -26,11 +26,12 @@
|
||||
<ft-input
|
||||
ref="searchBar"
|
||||
:placeholder="$t('User Playlists.Search bar placeholder')"
|
||||
:value="query"
|
||||
:show-clear-text-button="true"
|
||||
:show-action-button="false"
|
||||
:maxlength="255"
|
||||
@input="(input) => query = input"
|
||||
@clear="query = ''"
|
||||
@input="(input) => handleQueryChange(input)"
|
||||
@clear="() => handleQueryChange('')"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
@ -700,19 +700,21 @@ Channel:
|
||||
This channel does not currently have any podcasts: ''
|
||||
Releases:
|
||||
Releases: ''
|
||||
This channel does not currently have any releases: ''
|
||||
This channel does not currently have any releases: 'Hierdie kanaal het nie tans
|
||||
enige vrystellings nie'
|
||||
About:
|
||||
About: ''
|
||||
Channel Description: ''
|
||||
About: 'Oor'
|
||||
Channel Description: 'Kanaalbeskrywing'
|
||||
Tags:
|
||||
Tags: ''
|
||||
Search for: ''
|
||||
Details: ''
|
||||
Joined: ''
|
||||
Location: ''
|
||||
Featured Channels: ''
|
||||
Tags: 'Etikette'
|
||||
Search for: 'Soek na ‘{tag}’'
|
||||
Details: 'Details'
|
||||
Joined: 'Lid sedert'
|
||||
Location: 'Ligging'
|
||||
Featured Channels: 'Uitgeligte kanale'
|
||||
Community:
|
||||
This channel currently does not have any posts: ''
|
||||
This channel currently does not have any posts: 'Hierdie kanaal het nie tans enige
|
||||
plasings nie'
|
||||
votes: '{votes} stemme'
|
||||
Reveal Answers: 'Onthul antwoorde'
|
||||
Hide Answers: 'Versteek antwoorde'
|
||||
@ -910,6 +912,7 @@ Chapters:
|
||||
'Chapters list hidden, current chapter: {chapterName}': 'Hoofstukke lys versteekte,
|
||||
huidige hoofstuk: {chapterName}'
|
||||
|
||||
Key Moments: Sleuteloomblikke
|
||||
Mini Player: 'Mini-speler'
|
||||
Comments:
|
||||
Comments: 'Kommentare'
|
||||
|
@ -1099,6 +1099,7 @@ Chapters:
|
||||
الحالي: {chapterName}'
|
||||
'Chapters list visible, current chapter: {chapterName}': 'قائمة الفصول المرئية ،
|
||||
الفصل الحالي: {chapterName}'
|
||||
Key Moments: العناصر الرئيسية
|
||||
Preferences: التفضيلات
|
||||
Ok: موافق
|
||||
Hashtag:
|
||||
|
Loading…
Reference in New Issue
Block a user