mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
[feat-216] support for sorting playlists
This commit is contained in:
@@ -115,6 +115,16 @@
|
||||
"date-uploaded": "Hochladedatum"
|
||||
}
|
||||
},
|
||||
"playlists": {
|
||||
"sort": {
|
||||
"title": "Title",
|
||||
"author": "Author",
|
||||
"number-of-maps": "Number of Maps",
|
||||
"duration": "Duration",
|
||||
"min-notes-per-second": "Min Notes per Second",
|
||||
"max-notes-per-second": "Max Notes per Second"
|
||||
}
|
||||
},
|
||||
"mods": {
|
||||
"loading-mods": "Lade Mods...",
|
||||
"no-internet": "Kein Internet",
|
||||
|
||||
@@ -115,6 +115,16 @@
|
||||
"date-uploaded": "Date Uploaded"
|
||||
}
|
||||
},
|
||||
"playlists": {
|
||||
"sort": {
|
||||
"title": "Title",
|
||||
"author": "Author",
|
||||
"number-of-maps": "Number of Maps",
|
||||
"duration": "Duration",
|
||||
"min-notes-per-second": "Min Notes per Second",
|
||||
"max-notes-per-second": "Max Notes per Second"
|
||||
}
|
||||
},
|
||||
"mods": {
|
||||
"loading-mods": "Loading mods...",
|
||||
"no-internet": "No internet",
|
||||
|
||||
@@ -115,6 +115,16 @@
|
||||
"date-uploaded": "Fecha de subida"
|
||||
}
|
||||
},
|
||||
"playlists": {
|
||||
"sort": {
|
||||
"title": "Title",
|
||||
"author": "Author",
|
||||
"number-of-maps": "Number of Maps",
|
||||
"duration": "Duration",
|
||||
"min-notes-per-second": "Min Notes per Second",
|
||||
"max-notes-per-second": "Max Notes per Second"
|
||||
}
|
||||
},
|
||||
"mods": {
|
||||
"loading-mods": "Cargando mods...",
|
||||
"no-internet": "Sin internet",
|
||||
|
||||
@@ -115,6 +115,16 @@
|
||||
"date-uploaded": "Date de téléchargement"
|
||||
}
|
||||
},
|
||||
"playlists": {
|
||||
"sort": {
|
||||
"title": "Title",
|
||||
"author": "Author",
|
||||
"number-of-maps": "Number of Maps",
|
||||
"duration": "Duration",
|
||||
"min-notes-per-second": "Min Notes per Second",
|
||||
"max-notes-per-second": "Max Notes per Second"
|
||||
}
|
||||
},
|
||||
"mods": {
|
||||
"loading-mods": "Chargement des mods...",
|
||||
"no-internet": "Pas d'accès Internet",
|
||||
|
||||
@@ -115,6 +115,16 @@
|
||||
"date-uploaded": "アップロード日"
|
||||
}
|
||||
},
|
||||
"playlists": {
|
||||
"sort": {
|
||||
"title": "Title",
|
||||
"author": "Author",
|
||||
"number-of-maps": "Number of Maps",
|
||||
"duration": "Duration",
|
||||
"min-notes-per-second": "Min Notes per Second",
|
||||
"max-notes-per-second": "Max Notes per Second"
|
||||
}
|
||||
},
|
||||
"mods": {
|
||||
"loading-mods": "Modを読み込み中...",
|
||||
"no-internet": "インターネット接続がありません!",
|
||||
|
||||
@@ -115,6 +115,16 @@
|
||||
"date-uploaded": "Дата загрузки"
|
||||
}
|
||||
},
|
||||
"playlists": {
|
||||
"sort": {
|
||||
"title": "Title",
|
||||
"author": "Author",
|
||||
"number-of-maps": "Number of Maps",
|
||||
"duration": "Duration",
|
||||
"min-notes-per-second": "Min Notes per Second",
|
||||
"max-notes-per-second": "Max Notes per Second"
|
||||
}
|
||||
},
|
||||
"mods": {
|
||||
"loading-mods": "Загрузка модов...",
|
||||
"no-internet": "Нет интернета",
|
||||
|
||||
@@ -115,6 +115,16 @@
|
||||
"date-uploaded": "上傳日期"
|
||||
}
|
||||
},
|
||||
"playlists": {
|
||||
"sort": {
|
||||
"title": "Title",
|
||||
"author": "Author",
|
||||
"number-of-maps": "Number of Maps",
|
||||
"duration": "Duration",
|
||||
"min-notes-per-second": "Min Notes per Second",
|
||||
"max-notes-per-second": "Max Notes per Second"
|
||||
}
|
||||
},
|
||||
"mods": {
|
||||
"loading-mods": "Mod 載入中...",
|
||||
"no-internet": "無網路",
|
||||
|
||||
@@ -115,6 +115,16 @@
|
||||
"date-uploaded": "上传日期"
|
||||
}
|
||||
},
|
||||
"playlists": {
|
||||
"sort": {
|
||||
"title": "Title",
|
||||
"author": "Author",
|
||||
"number-of-maps": "Number of Maps",
|
||||
"duration": "Duration",
|
||||
"min-notes-per-second": "Min Notes per Second",
|
||||
"max-notes-per-second": "Max Notes per Second"
|
||||
}
|
||||
},
|
||||
"mods": {
|
||||
"loading-mods": "Mod 加载中...",
|
||||
"no-internet": "无网络",
|
||||
|
||||
@@ -20,13 +20,14 @@ import { BsmLocalMap } from "shared/models/maps/bsm-local-map.interface";
|
||||
import { useConstant } from "renderer/hooks/use-constant.hook";
|
||||
import { LocalBPListsDetails } from "shared/models/playlists/local-playlist.models";
|
||||
import { PlaylistDownloaderService } from "renderer/services/playlist-downloader.service";
|
||||
import { LocalPlaylistFilter, LocalPlaylistFilterPanel } from "./playlists/local-playlist-filter-panel.component";
|
||||
import { LocalPlaylistFilter, LocalPlaylistFilterPanel, LocalPlaylistSort } from "./playlists/local-playlist-filter-panel.component";
|
||||
import { noop } from "shared/helpers/function.helpers";
|
||||
import { Dropzone } from "../shared/dropzone.component";
|
||||
import { logRenderError } from "renderer";
|
||||
import { BsmSelect, BsmSelectOption } from "../shared/bsm-select.component";
|
||||
import { MapsSorterService } from "renderer/services/maps/sorter.service";
|
||||
import { BsmButton } from "../shared/bsm-button.component";
|
||||
import { PlaylistsSorterService } from "renderer/services/playlists/sorter.service";
|
||||
|
||||
type Props = {
|
||||
readonly version?: BSVersion;
|
||||
@@ -42,11 +43,15 @@ export const InstalledMapsContext = createContext<{
|
||||
|
||||
export function MapsPlaylistsPanel({ version, isActive }: Props) {
|
||||
|
||||
const MAP_TAB = 0;
|
||||
const PLAYLIST_TAB = 1;
|
||||
|
||||
const mapsManager = useService(MapsManagerService);
|
||||
const mapsDownloader = useService(MapsDownloaderService);
|
||||
const mapsSorter = useService(MapsSorterService);
|
||||
const playlistsManager = useService(PlaylistsManagerService);
|
||||
const playlistsDownloader = useService(PlaylistDownloaderService);
|
||||
const playlistsSorter = useService(PlaylistsSorterService);
|
||||
|
||||
const t = useTranslation();
|
||||
const [tabIndex, setTabIndex] = useState(0);
|
||||
@@ -73,6 +78,18 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) {
|
||||
ascending: true,
|
||||
});
|
||||
const [playlistFilter, setPlaylistFilter] = useState<LocalPlaylistFilter>({});
|
||||
const [playlistSort, setPlaylistSort] = useState<LocalPlaylistSort>({
|
||||
compare: playlistsSorter.getDefaultComparator(),
|
||||
ascending: true,
|
||||
});
|
||||
const [selectedSort, setSelectedSort] = useState<string>("name");
|
||||
|
||||
const [sortOptions, setSortOptions] = useState<BsmSelectOption<string>[]>(
|
||||
() => mapsSorter.getComparatorKeys().map(key => ({
|
||||
text: `pages.version-viewer.maps.sort.${key}`,
|
||||
value: key,
|
||||
}))
|
||||
);
|
||||
|
||||
const [search, setSearch] = useState("");
|
||||
const mapsLinkedState = useObservable(() => {
|
||||
@@ -108,6 +125,39 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) {
|
||||
return mapsManager.unlinkVersion(version);
|
||||
};
|
||||
|
||||
const handleTabChange = (index: number) => {
|
||||
setTabIndex(index);
|
||||
|
||||
switch (index) {
|
||||
case MAP_TAB:
|
||||
setSortOptions(() => mapsSorter.getComparatorKeys().map(key => ({
|
||||
text: `pages.version-viewer.maps.sort.${key}`,
|
||||
value: key,
|
||||
})));
|
||||
setSelectedSort(mapsSorter.getDefaultComparatorKey());
|
||||
setMapSort({
|
||||
compare: mapsSorter.getDefaultComparator(),
|
||||
ascending: playlistSort.ascending,
|
||||
});
|
||||
break;
|
||||
|
||||
case PLAYLIST_TAB:
|
||||
setSortOptions(() => playlistsSorter.getComparatorKeys().map(key => ({
|
||||
text: `pages.version-viewer.playlists.sort.${key}`,
|
||||
value: key,
|
||||
})));
|
||||
setSelectedSort(playlistsSorter.getDefaultComparatorKey());
|
||||
setPlaylistSort({
|
||||
compare: playlistsSorter.getDefaultComparator(),
|
||||
ascending: mapSort.ascending,
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const handlePlaylistLinkClick = () => {
|
||||
if(playlistLinkedState === FolderLinkState.Pending || playlistLinkedState === FolderLinkState.Processing){ return Promise.resolve(false); }
|
||||
|
||||
@@ -143,22 +193,50 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) {
|
||||
];
|
||||
})();
|
||||
|
||||
const sortOptions: BsmSelectOption<string>[] = useConstant(() => {
|
||||
return mapsSorter.getComparatorKeys().map(key => ({
|
||||
text: `pages.version-viewer.maps.sort.${key}`,
|
||||
value: key,
|
||||
}));
|
||||
});
|
||||
|
||||
const handleSortChange = (key: string) => {
|
||||
setMapSort({
|
||||
compare: mapsSorter.getComparator(key),
|
||||
ascending: mapSort.ascending,
|
||||
});
|
||||
switch (tabIndex) {
|
||||
case MAP_TAB:
|
||||
setMapSort({
|
||||
compare: mapsSorter.getComparator(key),
|
||||
ascending: mapSort.ascending,
|
||||
});
|
||||
break;
|
||||
|
||||
case PLAYLIST_TAB:
|
||||
setPlaylistSort({
|
||||
compare: playlistsSorter.getComparator(key),
|
||||
ascending: playlistSort.ascending,
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const handleAscendingClick = () => {
|
||||
switch (tabIndex) {
|
||||
case MAP_TAB:
|
||||
setMapSort({
|
||||
compare: mapSort.compare,
|
||||
ascending: !mapSort.ascending,
|
||||
});
|
||||
break;
|
||||
|
||||
case PLAYLIST_TAB:
|
||||
setPlaylistSort({
|
||||
compare: playlistSort.compare,
|
||||
ascending: !playlistSort.ascending,
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const dropDownItems = ((): DropDownItem[] => {
|
||||
if (tabIndex === 0) {
|
||||
if (tabIndex === MAP_TAB) {
|
||||
return [
|
||||
{ icon: "export", text: "pages.version-viewer.maps.search-bar.dropdown.export-maps", onClick: () => mapsRef.current.exportMaps?.() },
|
||||
{ icon: "trash", text: "pages.version-viewer.maps.search-bar.dropdown.delete-maps", onClick: () => mapsRef.current.deleteMaps?.() },
|
||||
@@ -195,7 +273,7 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) {
|
||||
<input
|
||||
type="text"
|
||||
className="h-full w-full bg-light-main-color-1 dark:bg-main-color-1 rounded-full px-2"
|
||||
placeholder={tabIndex === 0 ? t("pages.version-viewer.maps.search-bar.search-placeholder") : t("playlist.search-playlist")}
|
||||
placeholder={tabIndex === MAP_TAB ? t("pages.version-viewer.maps.search-bar.search-placeholder") : t("playlist.search-playlist")}
|
||||
value={search}
|
||||
onChange={e => setSearch(() => e.target.value)}
|
||||
tabIndex={-1}
|
||||
@@ -213,31 +291,30 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) {
|
||||
<BsmSelect
|
||||
className="bg-theme-1 rounded-full px-1 pb-0.5 text-center cursor-pointer"
|
||||
options={sortOptions}
|
||||
selected="name"
|
||||
selected={selectedSort}
|
||||
onChange={handleSortChange}
|
||||
/>
|
||||
<BsmButton
|
||||
className="h-full flex aspect-square relative rounded-full z-[1] bg-light-main-color-1 dark:bg-main-color-3"
|
||||
iconClassName="transition-transform size-full ease-in-out duration-200 bg-light-main-color-1 dark:bg-main-color-3"
|
||||
iconStyle={{ transform: mapSort.ascending ? "rotate(360deg)" : "rotate(180deg)" }}
|
||||
iconStyle={{ transform: (tabIndex === MAP_TAB && mapSort.ascending)
|
||||
|| (tabIndex === PLAYLIST_TAB && playlistSort.ascending)
|
||||
? "rotate(360deg)" : "rotate(180deg)"}}
|
||||
icon="chevron-top"
|
||||
typeColor="primary"
|
||||
withBar={false}
|
||||
onClick={() => setMapSort({
|
||||
compare: mapSort.compare,
|
||||
ascending: !mapSort.ascending,
|
||||
})}
|
||||
onClick={handleAscendingClick}
|
||||
/>
|
||||
<BsmDropdownButton className="h-full flex aspect-square relative rounded-full z-[1] bg-light-main-color-1 dark:bg-main-color-3" buttonClassName="rounded-full h-full w-full p-[6px]" icon="three-dots" withBar={false} items={dropDownItems} menuTranslationY="6px" align="center" />
|
||||
</nav>
|
||||
<BsContentTabPanel
|
||||
tabIndex={tabIndex}
|
||||
onTabChange={index => setTabIndex(index)}
|
||||
onTabChange={handleTabChange}
|
||||
tabs={[
|
||||
{
|
||||
text: "misc.maps",
|
||||
icon: MapIcon,
|
||||
onClick: () => setTabIndex(0),
|
||||
onClick: () => handleTabChange(MAP_TAB),
|
||||
linkProps: version ? {
|
||||
state: mapsLinkedState,
|
||||
onClick: handleMapsLinkClick,
|
||||
@@ -246,7 +323,7 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) {
|
||||
{
|
||||
text: "misc.playlists",
|
||||
icon: PlaylistIcon,
|
||||
onClick: () => setTabIndex(1),
|
||||
onClick: () => handleTabChange(PLAYLIST_TAB),
|
||||
linkProps: version ? {
|
||||
state: playlistLinkedState,
|
||||
onClick: handlePlaylistLinkClick,
|
||||
@@ -292,7 +369,16 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) {
|
||||
properties: ["openFile", "multiSelections"],
|
||||
}}}
|
||||
>
|
||||
<LocalPlaylistsListPanel className="w-full h-full shrink-0" isActive={isActive && tabIndex === 1} ref={playlistsRef} version={version} linkedState={playlistLinkedState} filter={playlistFilter} search={search}/>
|
||||
<LocalPlaylistsListPanel
|
||||
className="w-full h-full shrink-0"
|
||||
isActive={isActive && tabIndex === 1}
|
||||
ref={playlistsRef}
|
||||
version={version}
|
||||
linkedState={playlistLinkedState}
|
||||
filter={playlistFilter}
|
||||
search={search}
|
||||
sort={playlistSort}
|
||||
/>
|
||||
</Dropzone>
|
||||
|
||||
</InstalledMapsContext.Provider>
|
||||
|
||||
+7
@@ -6,6 +6,8 @@ import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { hourToS, sToMs } from "shared/helpers/time.helpers";
|
||||
import { useOnUpdate } from "renderer/hooks/use-on-update.hook";
|
||||
import formatDuration from "format-duration";
|
||||
import { LocalBPListsDetails } from "shared/models/playlists/local-playlist.models";
|
||||
import { Comparator } from "shared/models/generics.type";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
@@ -120,3 +122,8 @@ export type LocalPlaylistFilter = Partial<{
|
||||
minDuration: number;
|
||||
maxDuration: number;
|
||||
}>
|
||||
|
||||
export interface LocalPlaylistSort {
|
||||
compare: Comparator<LocalBPListsDetails>;
|
||||
ascending: boolean;
|
||||
}
|
||||
|
||||
+28
-15
@@ -24,7 +24,7 @@ import { DeletePlaylistModal } from "renderer/components/modal/modal-types/playl
|
||||
import { OsDiagnosticService } from "renderer/services/os-diagnostic.service";
|
||||
import { PlaylistItemComponentPropsMapper } from "shared/mappers/playlist/playlist-item-component-props.mapper";
|
||||
import { VirtualScroll } from "renderer/components/shared/virtual-scroll/virtual-scroll.component";
|
||||
import { LocalPlaylistFilter } from "./local-playlist-filter-panel.component";
|
||||
import { LocalPlaylistFilter, LocalPlaylistSort } from "./local-playlist-filter-panel.component";
|
||||
import { BsmImage } from "renderer/components/shared/bsm-image.component";
|
||||
import { BsmButton } from "renderer/components/shared/bsm-button.component";
|
||||
import BeatConflict from "../../../../../assets/images/apngs/beat-conflict.png";
|
||||
@@ -48,6 +48,7 @@ type Props = {
|
||||
search?: string;
|
||||
linkedState?: FolderLinkState;
|
||||
isActive?: boolean;
|
||||
sort: LocalPlaylistSort;
|
||||
};
|
||||
|
||||
export type LocalPlaylistsListRef = {
|
||||
@@ -57,7 +58,15 @@ export type LocalPlaylistsListRef = {
|
||||
exportPlaylists: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(({ version, className, filter: playlistFiler, search, isActive, linkedState }, forwardedRef) => {
|
||||
export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(({
|
||||
version,
|
||||
className,
|
||||
filter: playlistFilter,
|
||||
search,
|
||||
isActive,
|
||||
linkedState,
|
||||
sort: playlistSort,
|
||||
}, forwardedRef) => {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
@@ -397,27 +406,31 @@ export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(
|
||||
if(!p.playlistTitle?.toLowerCase().includes(search.toLowerCase())){ return false; }
|
||||
if(!p.playlistAuthor?.toLowerCase().includes(search.toLowerCase())){ return false; }
|
||||
|
||||
if(typeof p.nbMaps === "number" && (typeof playlistFiler?.minNbMaps === "number" || typeof playlistFiler?.maxNbMaps === "number")){
|
||||
if(playlistFiler?.minNbMaps && p.nbMaps < playlistFiler.minNbMaps){ return false; }
|
||||
if(playlistFiler?.maxNbMaps && p.nbMaps > playlistFiler.maxNbMaps){ return false; }
|
||||
if(typeof p.nbMaps === "number" && (typeof playlistFilter?.minNbMaps === "number" || typeof playlistFilter?.maxNbMaps === "number")){
|
||||
if(playlistFilter?.minNbMaps && p.nbMaps < playlistFilter.minNbMaps){ return false; }
|
||||
if(playlistFilter?.maxNbMaps && p.nbMaps > playlistFilter.maxNbMaps){ return false; }
|
||||
}
|
||||
|
||||
if(typeof p.nbMappers === "number" && (typeof playlistFiler?.minNbMappers === "number" || typeof playlistFiler?.maxNbMappers === "number")){
|
||||
if(playlistFiler?.minNbMappers && p.nbMappers < playlistFiler.minNbMappers){ return false; }
|
||||
if(playlistFiler?.maxNbMappers && p.nbMappers > playlistFiler.maxNbMappers){ return false; }
|
||||
if(typeof p.nbMappers === "number" && (typeof playlistFilter?.minNbMappers === "number" || typeof playlistFilter?.maxNbMappers === "number")){
|
||||
if(playlistFilter?.minNbMappers && p.nbMappers < playlistFilter.minNbMappers){ return false; }
|
||||
if(playlistFilter?.maxNbMappers && p.nbMappers > playlistFilter.maxNbMappers){ return false; }
|
||||
}
|
||||
|
||||
if(typeof p.duration === "number" && (typeof playlistFiler?.minDuration === "number" || typeof playlistFiler?.maxDuration === "number")){
|
||||
if(playlistFiler?.minDuration && p.duration < playlistFiler.minDuration){ return false; }
|
||||
if(playlistFiler?.maxDuration && p.duration > playlistFiler.maxDuration){ return false; }
|
||||
if(typeof p.duration === "number" && (typeof playlistFilter?.minDuration === "number" || typeof playlistFilter?.maxDuration === "number")){
|
||||
if(playlistFilter?.minDuration && p.duration < playlistFilter.minDuration){ return false; }
|
||||
if(playlistFilter?.maxDuration && p.duration > playlistFilter.maxDuration){ return false; }
|
||||
}
|
||||
|
||||
if(typeof p.minNps === "number" && typeof playlistFiler.minNps === "number" && p.minNps < playlistFiler.minNps){ return false; }
|
||||
if(typeof p.maxNps === "number" && typeof playlistFiler.maxNps === "number" && p.maxNps > playlistFiler.maxNps){ return false; }
|
||||
if(typeof p.minNps === "number" && typeof playlistFilter.minNps === "number" && p.minNps < playlistFilter.minNps){ return false; }
|
||||
if(typeof p.maxNps === "number" && typeof playlistFilter.maxNps === "number" && p.maxNps > playlistFilter.maxNps){ return false; }
|
||||
|
||||
return true;
|
||||
});
|
||||
}, [playlists, search, playlistFiler]);
|
||||
})
|
||||
.sort((playlist1, playlist2) =>
|
||||
playlistSort.compare(playlist1, playlist2)
|
||||
* (playlistSort.ascending ? 1 : -1)
|
||||
);
|
||||
}, [playlists, search, playlistFilter, playlistSort]);
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
|
||||
@@ -49,17 +49,29 @@ export class MapsSorterService {
|
||||
}
|
||||
};
|
||||
|
||||
private addTiebreak(comparator: Comparator<BsmLocalMap>): Comparator<BsmLocalMap> {
|
||||
return (map1, map2) =>
|
||||
comparator(map1, map2)
|
||||
|| this.getDefaultComparator()(map1, map2);
|
||||
}
|
||||
|
||||
public getComparatorKeys(): string[] {
|
||||
return Object.keys(this.comparators);
|
||||
}
|
||||
|
||||
public getDefaultComparatorKey(): string {
|
||||
return "name";
|
||||
}
|
||||
|
||||
public getDefaultComparator(): Comparator<BsmLocalMap> {
|
||||
return this.comparators.name;
|
||||
}
|
||||
|
||||
public getComparator(key: string): Comparator<BsmLocalMap> {
|
||||
const comparator = this.comparators[key];
|
||||
return comparator || this.comparators.name;
|
||||
return comparator
|
||||
? this.addTiebreak(comparator)
|
||||
: this.getDefaultComparator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { Comparator } from "shared/models/generics.type";
|
||||
import { LocalBPListsDetails } from "shared/models/playlists/local-playlist.models";
|
||||
|
||||
export class PlaylistsSorterService {
|
||||
private static instance: PlaylistsSorterService;
|
||||
|
||||
public static getInstance() {
|
||||
if (!PlaylistsSorterService.instance) {
|
||||
PlaylistsSorterService.instance = new PlaylistsSorterService();
|
||||
}
|
||||
return PlaylistsSorterService.instance;
|
||||
}
|
||||
|
||||
private readonly comparators: {
|
||||
[key: string]: Comparator<LocalBPListsDetails>;
|
||||
} = {
|
||||
title: (playlist1, playlist2) =>
|
||||
playlist1.playlistTitle.localeCompare(playlist2.playlistTitle),
|
||||
author: (playlist1, playlist2) =>
|
||||
playlist1.playlistAuthor.localeCompare(playlist2.playlistAuthor),
|
||||
"number-of-maps": (playlist1, playlist2) =>
|
||||
playlist1.nbMaps - playlist2.nbMaps,
|
||||
duration: (playlist1, playlist2) => {
|
||||
if (!playlist1.duration) {
|
||||
return playlist2.duration ? -1 : 0;
|
||||
}
|
||||
|
||||
return !playlist2.duration ? -1
|
||||
: playlist1.duration - playlist2.duration;
|
||||
},
|
||||
"min-notes-per-second": (playlist1, playlist2) => {
|
||||
if (!playlist1.minNps) {
|
||||
return playlist2.minNps ? -1 : 0;
|
||||
}
|
||||
|
||||
return !playlist2.minNps ? -1
|
||||
: playlist1.minNps - playlist2.minNps;
|
||||
},
|
||||
"max-notes-per-second": (playlist1, playlist2) => {
|
||||
if (!playlist1.maxNps) {
|
||||
return playlist2.maxNps ? -1 : 0;
|
||||
}
|
||||
|
||||
return !playlist2.maxNps ? -1
|
||||
: playlist1.maxNps - playlist2.maxNps;
|
||||
},
|
||||
};
|
||||
|
||||
private addTiebreak(comparator: Comparator<LocalBPListsDetails>): Comparator<LocalBPListsDetails> {
|
||||
return (playlist1, playlist2) =>
|
||||
comparator(playlist1, playlist2)
|
||||
|| this.getDefaultComparator()(playlist1, playlist2);
|
||||
}
|
||||
|
||||
public getComparatorKeys(): string[] {
|
||||
return Object.keys(this.comparators);
|
||||
}
|
||||
|
||||
public getDefaultComparatorKey(): string {
|
||||
return "title";
|
||||
}
|
||||
|
||||
public getDefaultComparator(): Comparator<LocalBPListsDetails> {
|
||||
return this.comparators.title;
|
||||
}
|
||||
|
||||
public getComparator(key: string): Comparator<LocalBPListsDetails> {
|
||||
const comparator = this.comparators[key];
|
||||
return comparator
|
||||
? this.addTiebreak(comparator)
|
||||
: this.getDefaultComparator();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user