From e89333ea0958ffed02786257838c3e6fce49f526 Mon Sep 17 00:00:00 2001 From: silentrald Date: Fri, 10 Jan 2025 01:02:39 +0800 Subject: [PATCH] [feat-216] refactored sorting services to classes and const --- .../maps-playlists-panel.component.tsx | 35 ++++++------ .../services/playlists/sorter.service.ts | 56 ------------------- .../models/maps/sorter.ts} | 48 ++++------------ src/shared/models/playlists/sorter.ts | 30 ++++++++++ src/shared/models/sorter.model.ts | 36 ++++++++++++ 5 files changed, 95 insertions(+), 110 deletions(-) delete mode 100644 src/renderer/services/playlists/sorter.service.ts rename src/{renderer/services/maps/sorter.service.ts => shared/models/maps/sorter.ts} (52%) create mode 100644 src/shared/models/playlists/sorter.ts create mode 100644 src/shared/models/sorter.model.ts diff --git a/src/renderer/components/maps-playlists-panel/maps-playlists-panel.component.tsx b/src/renderer/components/maps-playlists-panel/maps-playlists-panel.component.tsx index 8c018766..8c36deb0 100644 --- a/src/renderer/components/maps-playlists-panel/maps-playlists-panel.component.tsx +++ b/src/renderer/components/maps-playlists-panel/maps-playlists-panel.component.tsx @@ -6,7 +6,7 @@ import { FilterPanel } from "./maps/filter-panel.component"; import { MapFilter, MapSort } from "shared/models/maps/beat-saver.model"; import { MapsManagerService } from "renderer/services/maps-manager.service"; import { MapsDownloaderService } from "renderer/services/maps-downloader.service"; -import { useTranslation } from "renderer/hooks/use-translation.hook"; +import { useTranslationV2 } from "renderer/hooks/use-translation.hook"; import { FolderLinkState } from "renderer/services/version-folder-linker.service"; import { useService } from "renderer/hooks/use-service.hook"; import { BsContentTabPanel } from "../shared/bs-content-tab-panel/bs-content-tab-panel.component"; @@ -25,9 +25,9 @@ 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"; +import { mapSorter } from "shared/models/maps/sorter"; +import { playlistSorter } from "shared/models/playlists/sorter"; type Props = { readonly version?: BSVersion; @@ -48,12 +48,10 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) { 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 { text: t } = useTranslationV2(); const [tabIndex, setTabIndex] = useState(0); const [mapsDropZoneOpen, setMapsDropZoneOpen] = useState(false); @@ -74,18 +72,18 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) { const [mapFilter, setMapFilter] = useState({}); const [mapSort, setMapSort] = useState({ - compare: mapsSorter.getDefaultComparator(), + compare: mapSorter.getDefaultComparator(), ascending: true, }); const [playlistFilter, setPlaylistFilter] = useState({}); const [playlistSort, setPlaylistSort] = useState({ - compare: playlistsSorter.getDefaultComparator(), + compare: playlistSorter.getDefaultComparator(), ascending: true, }); const [selectedSort, setSelectedSort] = useState("name"); const [sortOptions, setSortOptions] = useState[]>( - () => mapsSorter.getComparatorKeys().map(key => ({ + () => mapSorter.getComparatorKeys().map(key => ({ text: `pages.version-viewer.maps.tabs.maps.sort.${key}`, value: key, })) @@ -126,29 +124,32 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) { }; const handleTabChange = (index: number) => { + if (tabIndex === index) { + return; + } setTabIndex(index); switch (index) { case MAP_TAB: - setSortOptions(() => mapsSorter.getComparatorKeys().map(key => ({ + setSortOptions(() => mapSorter.getComparatorKeys().map(key => ({ text: `pages.version-viewer.maps.tabs.maps.sort.${key}`, value: key, }))); - setSelectedSort(mapsSorter.getDefaultComparatorKey()); + setSelectedSort(mapSorter.defaultKey); setMapSort({ - compare: mapsSorter.getDefaultComparator(), + compare: mapSorter.getDefaultComparator(), ascending: playlistSort.ascending, }); break; case PLAYLIST_TAB: - setSortOptions(() => playlistsSorter.getComparatorKeys().map(key => ({ + setSortOptions(() => playlistSorter.getComparatorKeys().map(key => ({ text: `pages.version-viewer.maps.tabs.playlists.sort.${key}`, value: key, }))); - setSelectedSort(playlistsSorter.getDefaultComparatorKey()); + setSelectedSort(playlistSorter.defaultKey); setPlaylistSort({ - compare: playlistsSorter.getDefaultComparator(), + compare: playlistSorter.getDefaultComparator(), ascending: mapSort.ascending, }); break; @@ -197,14 +198,14 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) { switch (tabIndex) { case MAP_TAB: setMapSort({ - compare: mapsSorter.getComparator(key), + compare: mapSorter.getComparator(key), ascending: mapSort.ascending, }); break; case PLAYLIST_TAB: setPlaylistSort({ - compare: playlistsSorter.getComparator(key), + compare: playlistSorter.getComparator(key), ascending: playlistSort.ascending, }); break; diff --git a/src/renderer/services/playlists/sorter.service.ts b/src/renderer/services/playlists/sorter.service.ts deleted file mode 100644 index e61c2177..00000000 --- a/src/renderer/services/playlists/sorter.service.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Comparator, Comparison } from "shared/models/comparator.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; - } = { - 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 ? Comparison.LESSER : Comparison.EQUAL; - } - - return !playlist2.duration ? Comparison.GREATER : playlist1.duration - playlist2.duration; - }, - "notes-per-second": (playlist1, playlist2) => { - if (!playlist1.maxNps) { - return playlist2.maxNps ? Comparison.LESSER : Comparison.EQUAL; - } - - return !playlist2.maxNps ? Comparison.GREATER : playlist1.maxNps - playlist2.maxNps; - }, - }; - - private addTiebreak(comparator: Comparator): Comparator { - 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 { - return this.comparators.title; - } - - public getComparator(key: string): Comparator { - const comparator = this.comparators[key]; - return comparator ? this.addTiebreak(comparator) : this.getDefaultComparator(); - } -} diff --git a/src/renderer/services/maps/sorter.service.ts b/src/shared/models/maps/sorter.ts similarity index 52% rename from src/renderer/services/maps/sorter.service.ts rename to src/shared/models/maps/sorter.ts index a73529b3..a5cbe6de 100644 --- a/src/renderer/services/maps/sorter.service.ts +++ b/src/shared/models/maps/sorter.ts @@ -1,20 +1,12 @@ -import { Comparator, Comparison } from "shared/models/comparator.type"; -import { BsmLocalMap } from "shared/models/maps/bsm-local-map.interface"; +import { Comparison } from "../comparator.type"; +import { Sorter } from "../sorter.model"; +import { BsmLocalMap } from "./bsm-local-map.interface"; -export class MapsSorterService { - private static instance: MapsSorterService; +const sortName = (map1: BsmLocalMap, map2: BsmLocalMap) => map1.mapInfo.songName.localeCompare(map2.mapInfo.songName); - public static getInstance() { - if (!MapsSorterService.instance) { - MapsSorterService.instance = new MapsSorterService(); - } - return MapsSorterService.instance; - } - - private readonly comparators: { - [key: string]: Comparator; - } = { - name: (map1, map2) => map1.mapInfo.songName.localeCompare(map2.mapInfo.songName), +export const mapSorter = new Sorter({ + comparators: { + name: sortName, "song-author": (map1, map2) => map1.mapInfo.songAuthorName.localeCompare(map2.mapInfo.songAuthorName), "map-author": (map1, map2) => { // Compare the number of mappers, else compare the first mapper @@ -43,26 +35,8 @@ export class MapsSorterService { return !map2.songDetails ? Comparison.GREATER : map1.songDetails.uploadedAt - map2.songDetails.uploadedAt; }, - }; + }, + tiebreak: sortName, + defaultKey: "name" +}); - private addTiebreak(comparator: Comparator): Comparator { - 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 { - return this.comparators.name; - } - - public getComparator(key: string): Comparator { - const comparator = this.comparators[key]; - return comparator ? this.addTiebreak(comparator) : this.getDefaultComparator(); - } -} diff --git a/src/shared/models/playlists/sorter.ts b/src/shared/models/playlists/sorter.ts new file mode 100644 index 00000000..b7fdcc4b --- /dev/null +++ b/src/shared/models/playlists/sorter.ts @@ -0,0 +1,30 @@ +import { Comparison } from "../comparator.type"; +import { Sorter } from "../sorter.model"; +import { LocalBPListsDetails } from "./local-playlist.models"; + +const sortTitle = (playlist1: LocalBPListsDetails, playlist2: LocalBPListsDetails) => + playlist1.playlistTitle.localeCompare(playlist2.playlistTitle); + +export const playlistSorter = new Sorter({ + comparators: { + title: sortTitle, + 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 ? Comparison.LESSER : Comparison.EQUAL; + } + + return !playlist2.duration ? Comparison.GREATER : playlist1.duration - playlist2.duration; + }, + "notes-per-second": (playlist1, playlist2) => { + if (!playlist1.maxNps) { + return playlist2.maxNps ? Comparison.LESSER : Comparison.EQUAL; + } + + return !playlist2.maxNps ? Comparison.GREATER : playlist1.maxNps - playlist2.maxNps; + }, + }, + tiebreak: sortTitle, + defaultKey: "title", +}); diff --git a/src/shared/models/sorter.model.ts b/src/shared/models/sorter.model.ts new file mode 100644 index 00000000..993981b8 --- /dev/null +++ b/src/shared/models/sorter.model.ts @@ -0,0 +1,36 @@ +import { Comparator } from "./comparator.type"; + +export type SorterOptions = { + defaultKey: string; + comparators: Record>; + tiebreak: Comparator; +} + +export class Sorter { + public readonly defaultKey: string; + + private readonly comparators: Record>; + private readonly tiebreak: Comparator; + + constructor(options: SorterOptions) { + this.defaultKey = options.defaultKey; + this.comparators = options.comparators; + this.tiebreak = options.tiebreak; + } + + public getComparatorKeys(): string[] { + return Object.keys(this.comparators); + } + + public getDefaultComparator(): Comparator { + return this.comparators[this.defaultKey]; + } + + public getComparator(key: string): Comparator { + const comparator = this.comparators[key] || this.comparators[this.defaultKey]; + return this.tiebreak === comparator + ? comparator + : (object1, object2) => comparator(object1, object2) || this.tiebreak(object1, object2); + } + +}