Compare commits

...

20 Commits

Author SHA1 Message Date
PikachuEXE
8874b14515
Merge 03f887ecf1 into dc65eece29 2024-11-20 16:01:00 +00:00
Rex_sa
dc65eece29
Translated using Weblate (Arabic)
Currently translated at 100.0% (852 of 852 strings)

Translation: FreeTube/Translations
Translate-URL: https://hosted.weblate.org/projects/free-tube/translations/ar/
2024-11-20 17:00:26 +01:00
Gideon Wentink
540a5a9385
Translated using Weblate (Afrikaans)
Currently translated at 30.7% (262 of 852 strings)

Translation: FreeTube/Translations
Translate-URL: https://hosted.weblate.org/projects/free-tube/translations/af/
2024-11-20 13:00:39 +01:00
PikachuEXE
03f887ecf1
! Fix search tab showing "0 results" before search done 2024-10-16 09:44:51 +08:00
PikachuEXE
cdc64d078d
Merge branch 'development' into feature/history/remember-search-query
* development: (30 commits)
  Translated using Weblate (Azerbaijani)
  Translated using Weblate (French)
  Translated using Weblate (Estonian)
  Bump the babel group with 3 updates (#5858)
  Bump electron-builder from 25.1.7 to 25.1.8 (#5862)
  Bump stylelint from 16.9.0 to 16.10.0 in the stylelint group (#5860)
  Bump the eslint group with 2 updates (#5859)
  Bump globals from 15.10.0 to 15.11.0 (#5861)
  Bump sass from 1.79.4 to 1.79.5 (#5863)
  Bump electron from 32.1.2 to 32.2.0 (#5864)
  Translated using Weblate (Icelandic)
  Translated using Weblate (Russian)
  Translated using Weblate (Hebrew)
  Translated using Weblate (Chinese (Traditional Han script))
  Translated using Weblate (Italian)
  Translated using Weblate (Chinese (Simplified Han script))
  Translated using Weblate (Serbian)
  Translated using Weblate (Czech)
  Translated using Weblate (Japanese)
  Translated using Weblate (Portuguese (Brazil))
  ...
2024-10-16 09:26:52 +08:00
PikachuEXE
49cd4490a0
* Put currentTab value into proper place params instead of query 2024-10-13 08:24:00 +08:00
PikachuEXE
1be051e086
Merge branch 'development' into feature/history/remember-search-query
* development: (35 commits)
  Translated using Weblate (Hungarian)
  Translated using Weblate (French)
  Translated using Weblate (Chinese (Simplified Han script))
  Translated using Weblate (Serbian)
  Translated using Weblate (Italian)
  Translated using Weblate (English (United Kingdom))
  Translated using Weblate (French)
  Translated using Weblate (Czech)
  Translated using Weblate (Bulgarian)
  Translated using Weblate (Italian)
  Translated using Weblate (Turkish)
  Translated using Weblate (German)
  Translated using Weblate (Spanish)
  Add Playlist Sort By Video Duration (#5627)
  Translated using Weblate (Estonian)
  Translated using Weblate (Italian)
  Translated using Weblate (French)
  Translated using Weblate (Chinese (Simplified Han script))
  Translated using Weblate (Serbian)
  Translated using Weblate (Serbian)
  ...
2024-10-12 16:19:09 +08:00
PikachuEXE
707f950e7f
Merge branch 'development' into feature/history/remember-search-query
* development: (55 commits)
  Translated using Weblate (Russian)
  Translated using Weblate (Japanese)
  fix cmd+m shortcut to minimize the window on macos and not mute the video (FreeTubeApp#5828) (#5829)
  Update dependabot.yml to add new eslint packages into the eslint group (#5837)
  Bump the babel group with 4 updates (#5831)
  Bump globals from 15.9.0 to 15.10.0 (#5835)
  Bump lefthook from 1.7.17 to 1.7.18 (#5836)
  Bump the eslint group with 2 updates (#5832)
  Bump @eslint/js from 9.11.1 to 9.12.0 (#5833)
  Bump @eslint/compat from 1.1.1 to 1.2.0 (#5834)
  Translated using Weblate (Belarusian)
  Translated using Weblate (Hebrew)
  Translated using Weblate (Chinese (Traditional Han script))
  Turn `vue/no-useless-template-attributes` eslint rule back on (#5830)
  Translated using Weblate (Serbian)
  Translated using Weblate (Belarusian)
  Translated using Weblate (Belarusian)
  Translated using Weblate (Belarusian)
  Upgrade eslint 8 > 9 (#5777)
  Translated using Weblate (Belarusian)
  ...
2024-10-08 06:57:05 +08:00
PikachuEXE
52ad0951e0
! Fix strange outline on nav buttons 2024-10-03 09:45:26 +08:00
PikachuEXE
5d7cac4276
Merge branch 'development' into feature/history/remember-search-query
* development: (49 commits)
  Translated using Weblate (Japanese)
  Translated using Weblate (French)
  Fix long comments overflowing and breaking the layout (#5774)
  Cleanup unused code in the store (#5776)
  Revert "Larger search/URL bar (#5348)" (#5773)
  Fix loading from search cache failing when no features are selected (#5775)
  Bump lefthook from 1.7.15 to 1.7.17 (#5768)
  Bump webpack from 5.94.0 to 5.95.0 (#5770)
  Bump sass from 1.79.3 to 1.79.4 (#5771)
  Translated using Weblate (Chinese (Traditional Han script))
  Change the Proxy Videos Through Invidious setting to only apply when using the Invidious API (#5758)
  Support copying post links from the context menu (#5760)
  Local API: Fix error when a community post has no likes (#5759)
  Translated using Weblate (English (United Kingdom))
  Translated using Weblate (Dutch)
  Translated using Weblate (Swedish)
  Do not overwrite cache entries when hitting RSS ratelimits (#5756)
  Translated using Weblate (Czech)
  Translated using Weblate (Hungarian)
  Translated using Weblate (Hungarian)
  ...
2024-10-03 09:38:07 +08:00
PikachuEXE
54f4331ed7
* Save more options 2024-10-01 11:33:36 +08:00
PikachuEXE
ce7c712bb2
Merge branch 'development' into feature/history/remember-search-query
* development:
  Fix video start time handling (#5719)
  Local API: Handle new shorts node (#5679)
  Update subscription cache when subscribing from the channel page (#5717)
  Translated using Weblate (English (United Kingdom))
  Implement persistent subscription cache (#5185)
  Translated using Weblate (Belarusian)
  Bump npm-run-all2 from 6.2.2 to 6.2.3 (#5708)
  Translated using Weblate (Swedish)
  Bump shaka-player from 4.11.1 to 4.11.3 (#5707)
  Translated using Weblate (Persian)
  v Downgrade electron-builder 25.x > 24.x (#5712)
  Translated using Weblate (Persian)
  Translated using Weblate (Persian)

# Conflicts:
#	src/renderer/views/Channel/Channel.js
#	src/renderer/views/Channel/Channel.vue
2024-09-21 11:40:55 +08:00
PikachuEXE
47222b9e54
Merge branch 'development' into feature/history/remember-search-query
* development: (74 commits)
  Translated using Weblate (Flemish (West))
  Added translation using Weblate (Flemish (West))
  Translated using Weblate (Slovak)
  Improve history import performance and fix some bugs (#5666)
  Bump electron from 32.0.2 to 32.1.0 (#5710)
  Local API: Use IOS HLS manifest for livestreams (#5705)
  Translated using Weblate (Slovak)
  Translated using Weblate (Japanese)
  Bump the stylelint group with 2 updates (#5706)
  Bump swiper from 11.1.12 to 11.1.14 (#5709)
  Fix a few memory leaks while tearing down the player (#5698)
  Fix audio track selection (#5697)
  Bump shaka-player from 4.10.12 to 4.11.1 (#5677)
  Translated using Weblate (Czech)
  Translated using Weblate (Serbian)
  Translated using Weblate (Polish)
  Translated using Weblate (German)
  Translated using Weblate (Chinese (Simplified Han script))
  Bump express from 4.19.2 to 4.20.0 (#5687)
  Translated using Weblate (Turkish)
  ...
2024-09-17 05:30:17 +08:00
PikachuEXE
2f8fa91b32
* Make channel page remember last query string only when going back 2024-09-03 08:43:54 +08:00
PikachuEXE
d7ce3286d7
* Make playlist page remember last query string only when going back 2024-09-03 08:43:54 +08:00
PikachuEXE
39b407926f
* Make user playlists page remember last query string only when going back 2024-09-03 08:43:53 +08:00
PikachuEXE
66103aee06
* Make subscribed channels page remember last query string only when going back 2024-09-03 08:43:52 +08:00
PikachuEXE
65c41b897f
! Fix restoring filtered history having unnecessary delay 2024-09-03 08:43:51 +08:00
PikachuEXE
7c0c69dc42
* Make history page remember last query string & search limit only when going back 2024-09-03 08:43:51 +08:00
PikachuEXE
70983739f6
$ Simplify boolean assignment, rename session storage key 2024-09-03 08:43:50 +08:00
13 changed files with 319 additions and 61 deletions

View File

@ -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'])

View File

@ -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([

View File

@ -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") }}

View File

@ -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)
},

View File

@ -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"

View File

@ -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([

View File

@ -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"
/>

View File

@ -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)
},

View File

@ -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"

View File

@ -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)
},

View File

@ -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

View File

@ -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'

View File

@ -1099,6 +1099,7 @@ Chapters:
الحالي: {chapterName}'
'Chapters list visible, current chapter: {chapterName}': 'قائمة الفصول المرئية ،
الفصل الحالي: {chapterName}'
Key Moments: ‌العناصر الرئيسية
Preferences: التفضيلات
Ok: موافق
Hashtag: