mirror of https://github.com/FreeTubeApp/FreeTube
Sort videos within playlist (#4921)
* Implement playlist sorting * Hide sort menu for remote playlists * Remove 'Custom (descending)' sort order I don't see the need for this particular sort order. * Adjust sort order & align dropdown with 'More Options' button * Make 'Latest added first' default option instead of custom * Remove unlikely-to-be-implemented 'Date published' sorting options Context from absidue: 'I don't think we should even attempt to support it, due to all of the situations where it wouldn't be possible.' * Update to use sortOrder as main variable throughout * Hide sort menu for playlists of length <2
This commit is contained in:
parent
ea35a13614
commit
9815ed3da2
|
@ -107,7 +107,9 @@ export default defineComponent({
|
|||
newPlaylistDefaultProperties: function () {
|
||||
return this.$store.getters.getNewPlaylistDefaultProperties
|
||||
},
|
||||
|
||||
locale: function () {
|
||||
return this.$i18n.locale.replace('_', '-')
|
||||
},
|
||||
processedQuery: function() {
|
||||
return this.query.trim().toLowerCase()
|
||||
},
|
||||
|
|
|
@ -280,6 +280,7 @@ const state = {
|
|||
thumbnailPreference: '',
|
||||
blurThumbnails: false,
|
||||
useProxy: false,
|
||||
userPlaylistSortOrder: 'date_added_descending',
|
||||
useRssFeeds: false,
|
||||
useSponsorBlock: false,
|
||||
videoVolumeMouseScroll: false,
|
||||
|
|
|
@ -7,6 +7,7 @@ import PlaylistInfo from '../../components/playlist-info/playlist-info.vue'
|
|||
import FtListVideoNumbered from '../../components/ft-list-video-numbered/ft-list-video-numbered.vue'
|
||||
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
|
||||
import FtButton from '../../components/ft-button/ft-button.vue'
|
||||
import FtSelect from '../../components/ft-select/ft-select.vue'
|
||||
import FtAutoLoadNextPageWrapper from '../../components/ft-auto-load-next-page-wrapper/ft-auto-load-next-page-wrapper.vue'
|
||||
import {
|
||||
getLocalPlaylist,
|
||||
|
@ -16,6 +17,16 @@ import {
|
|||
import { extractNumberFromString, setPublishedTimestampsInvidious, showToast } from '../../helpers/utils'
|
||||
import { invidiousGetPlaylistInfo, youtubeImageUrlToInvidious } from '../../helpers/api/invidious'
|
||||
|
||||
const SORT_BY_VALUES = {
|
||||
DateAddedNewest: 'date_added_descending',
|
||||
DateAddedOldest: 'date_added_ascending',
|
||||
AuthorAscending: 'author_ascending',
|
||||
AuthorDescending: 'author_descending',
|
||||
VideoTitleAscending: 'video_title_ascending',
|
||||
VideoTitleDescending: 'video_title_descending',
|
||||
Custom: 'custom',
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Playlist',
|
||||
components: {
|
||||
|
@ -25,6 +36,7 @@ export default defineComponent({
|
|||
'ft-list-video-numbered': FtListVideoNumbered,
|
||||
'ft-flex-box': FtFlexBox,
|
||||
'ft-button': FtButton,
|
||||
'ft-select': FtSelect,
|
||||
'ft-auto-load-next-page-wrapper': FtAutoLoadNextPageWrapper,
|
||||
},
|
||||
beforeRouteLeave(to, from, next) {
|
||||
|
@ -77,6 +89,12 @@ export default defineComponent({
|
|||
currentInvidiousInstance: function () {
|
||||
return this.$store.getters.getCurrentInvidiousInstance
|
||||
},
|
||||
userPlaylistSortOrder: function () {
|
||||
return this.$store.getters.getUserPlaylistSortOrder
|
||||
},
|
||||
sortOrder: function () {
|
||||
return this.isUserPlaylistRequested ? this.userPlaylistSortOrder : SORT_BY_VALUES.Custom
|
||||
},
|
||||
currentLocale: function () {
|
||||
return this.$i18n.locale.replace('_', '-')
|
||||
},
|
||||
|
@ -138,10 +156,10 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
sometimesFilteredUserPlaylistItems() {
|
||||
if (!this.isUserPlaylistRequested) { return this.playlistItems }
|
||||
if (this.processedVideoSearchQuery === '') { return this.playlistItems }
|
||||
if (!this.isUserPlaylistRequested) { return this.sortedPlaylistItems }
|
||||
if (this.processedVideoSearchQuery === '') { return this.sortedPlaylistItems }
|
||||
|
||||
return this.playlistItems.filter((v) => {
|
||||
return this.sortedPlaylistItems.filter((v) => {
|
||||
if (typeof (v.title) === 'string' && v.title.toLowerCase().includes(this.processedVideoSearchQuery)) {
|
||||
return true
|
||||
} else if (typeof (v.author) === 'string' && v.author.toLowerCase().includes(this.processedVideoSearchQuery)) {
|
||||
|
@ -151,10 +169,41 @@ export default defineComponent({
|
|||
return false
|
||||
})
|
||||
},
|
||||
sortByValues() {
|
||||
return Object.values(SORT_BY_VALUES)
|
||||
},
|
||||
isSortOrderCustom() {
|
||||
return this.sortOrder === SORT_BY_VALUES.Custom
|
||||
},
|
||||
sortedPlaylistItems: function () {
|
||||
if (this.sortOrder === SORT_BY_VALUES.Custom) {
|
||||
return this.playlistItems
|
||||
}
|
||||
|
||||
return this.playlistItems.toSorted((a, b) => {
|
||||
switch (this.sortOrder) {
|
||||
case SORT_BY_VALUES.DateAddedNewest:
|
||||
return b.timeAdded - a.timeAdded
|
||||
case SORT_BY_VALUES.DateAddedOldest:
|
||||
return a.timeAdded - b.timeAdded
|
||||
case SORT_BY_VALUES.VideoTitleAscending:
|
||||
return a.title.localeCompare(b.title, this.currentLocale)
|
||||
case SORT_BY_VALUES.VideoTitleDescending:
|
||||
return b.title.localeCompare(a.title, this.currentLocale)
|
||||
case SORT_BY_VALUES.AuthorAscending:
|
||||
return a.author.localeCompare(b.author, this.currentLocale)
|
||||
case SORT_BY_VALUES.AuthorDescending:
|
||||
return b.author.localeCompare(a.author, this.currentLocale)
|
||||
default:
|
||||
console.error(`Unknown sortOrder: ${this.sortOrder}`)
|
||||
return 0
|
||||
}
|
||||
})
|
||||
},
|
||||
visiblePlaylistItems: function () {
|
||||
if (!this.isUserPlaylistRequested) {
|
||||
// No filtering for non user playlists yet
|
||||
return this.playlistItems
|
||||
return this.sortedPlaylistItems
|
||||
}
|
||||
|
||||
if (this.userPlaylistVisibleLimit < this.sometimesFilteredUserPlaylistItems.length) {
|
||||
|
@ -166,6 +215,32 @@ export default defineComponent({
|
|||
processedVideoSearchQuery() {
|
||||
return this.videoSearchQuery.trim().toLowerCase()
|
||||
},
|
||||
sortBySelectNames() {
|
||||
return this.sortByValues.map((k) => {
|
||||
switch (k) {
|
||||
case SORT_BY_VALUES.Custom:
|
||||
return this.$t('Playlist.Sort By.Custom')
|
||||
case SORT_BY_VALUES.DateAddedNewest:
|
||||
return this.$t('Playlist.Sort By.DateAddedNewest')
|
||||
case SORT_BY_VALUES.DateAddedOldest:
|
||||
return this.$t('Playlist.Sort By.DateAddedOldest')
|
||||
case SORT_BY_VALUES.VideoTitleAscending:
|
||||
return this.$t('Playlist.Sort By.VideoTitleAscending')
|
||||
case SORT_BY_VALUES.VideoTitleDescending:
|
||||
return this.$t('Playlist.Sort By.VideoTitleDescending')
|
||||
case SORT_BY_VALUES.AuthorAscending:
|
||||
return this.$t('Playlist.Sort By.AuthorAscending')
|
||||
case SORT_BY_VALUES.AuthorDescending:
|
||||
return this.$t('Playlist.Sort By.AuthorDescending')
|
||||
default:
|
||||
console.error(`Unknown sort: ${k}`)
|
||||
return k
|
||||
}
|
||||
})
|
||||
},
|
||||
sortBySelectValues() {
|
||||
return this.sortByValues
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route () {
|
||||
|
@ -485,6 +560,7 @@ export default defineComponent({
|
|||
...mapActions([
|
||||
'updateSubscriptionDetails',
|
||||
'updatePlaylist',
|
||||
'updateUserPlaylistSortOrder',
|
||||
'removeVideo',
|
||||
]),
|
||||
|
||||
|
|
|
@ -66,6 +66,13 @@
|
|||
max-block-size: 7vh;
|
||||
}
|
||||
|
||||
.sortSelect {
|
||||
/* Put it on the right */
|
||||
margin-inline-start: auto;
|
||||
/* Align with 'More Options' dropdown button */
|
||||
margin-inline-end: 20px;
|
||||
}
|
||||
|
||||
:deep(.videoThumbnail) {
|
||||
margin-block-start: auto;
|
||||
margin-block-end: auto;
|
||||
|
|
|
@ -43,6 +43,15 @@
|
|||
<template
|
||||
v-if="playlistItems.length > 0"
|
||||
>
|
||||
<ft-select
|
||||
v-if="isUserPlaylistRequested && playlistItems.length > 1"
|
||||
class="sortSelect"
|
||||
:value="sortOrder"
|
||||
:select-names="sortBySelectNames"
|
||||
:select-values="sortBySelectValues"
|
||||
:placeholder="$t('Playlist.Sort By.Sort By')"
|
||||
@change="updateUserPlaylistSortOrder"
|
||||
/>
|
||||
<template
|
||||
v-if="visiblePlaylistItems.length > 0"
|
||||
>
|
||||
|
@ -62,8 +71,8 @@
|
|||
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-move-video-up="index > 0 && !playlistInVideoSearchMode && isSortOrderCustom"
|
||||
:can-move-video-down="index < playlistItems.length - 1 && !playlistInVideoSearchMode && isSortOrderCustom"
|
||||
:can-remove-from-playlist="true"
|
||||
:video-index="playlistInVideoSearchMode ? playlistItems.findIndex(i => i === item) : index"
|
||||
:initial-visible-state="index < 10"
|
||||
|
|
|
@ -185,7 +185,6 @@ User Playlists:
|
|||
|
||||
LatestPlayedFirst: 'Recently Played'
|
||||
EarliestPlayedFirst: 'Earliest Played'
|
||||
|
||||
SinglePlaylistView:
|
||||
Search for Videos: Search for Videos
|
||||
|
||||
|
@ -872,6 +871,15 @@ Playlist:
|
|||
View: View
|
||||
Views: Views
|
||||
Last Updated On: Last Updated On
|
||||
Sort By:
|
||||
Sort By: Sort By
|
||||
DateAddedNewest: Latest added first
|
||||
DateAddedOldest: Earliest added first
|
||||
AuthorAscending: Author (A-Z)
|
||||
AuthorDescending: Author (Z-A)
|
||||
VideoTitleAscending: Title (A-Z)
|
||||
VideoTitleDescending: Title (Z-A)
|
||||
Custom: Custom
|
||||
|
||||
# On Video Watch Page
|
||||
#* Published
|
||||
|
|
Loading…
Reference in New Issue