diff --git a/assets/jsons/translations/de.json b/assets/jsons/translations/de.json index db477c9b..d51065ab 100644 --- a/assets/jsons/translations/de.json +++ b/assets/jsons/translations/de.json @@ -91,6 +91,15 @@ "drop-zone": { "text": "Deine Karten importieren", "subtext": "Lasse deine ZIP-Dateien hier fallen, um deine Karten zu importieren" + }, + "sort": { + "name": "Name", + "song-author": "Liedautor", + "map-author": "Kartenautor", + "bpm": "BPM", + "duration": "Dauer", + "likes": "Likes", + "date-uploaded": "Hochladedatum" } }, "playlists": { @@ -102,6 +111,13 @@ "drop-zone": { "text": "Importiere deine Wiedergabelisten", "subtext": "Ziehe deine \".bplist\" oder \".json\" Dateien hierhin, um sie zu importieren" + }, + "sort": { + "title": "Titel", + "author": "Autor", + "number-of-maps": "Anzahl der Karten", + "duration": "Dauer", + "notes-per-second": "NPS" } } } diff --git a/assets/jsons/translations/en.json b/assets/jsons/translations/en.json index 6e7c296d..90d3fe7f 100644 --- a/assets/jsons/translations/en.json +++ b/assets/jsons/translations/en.json @@ -91,6 +91,15 @@ "drop-zone": { "text": "Import your maps", "subtext": "Drop your zip files here to import your maps" + }, + "sort": { + "name": "Name", + "song-author": "Song Author", + "map-author": "Map Author", + "bpm": "BPM", + "duration": "Duration", + "likes": "Likes", + "date-uploaded": "Date Uploaded" } }, "playlists": { @@ -102,6 +111,13 @@ "drop-zone": { "text": "Import your playlists", "subtext": "Drop your \".bplist\" or \".json\" files here to import them" + }, + "sort": { + "title": "Title", + "author": "Author", + "number-of-maps": "Number of Maps", + "duration": "Duration", + "notes-per-second": "NPS" } } } diff --git a/assets/jsons/translations/es.json b/assets/jsons/translations/es.json index 5b489edc..639a59ee 100644 --- a/assets/jsons/translations/es.json +++ b/assets/jsons/translations/es.json @@ -91,6 +91,15 @@ "drop-zone": { "text": "Importar tus mapas", "subtext": "Suelta tus archivos zip aquí para importar tus mapas" + }, + "sort": { + "name": "Nombre", + "song-author": "Autor de la canción", + "map-author": "Autor del mapa", + "bpm": "BPM", + "duration": "Duración", + "likes": "Me gusta", + "date-uploaded": "Fecha de subida" } }, "playlists": { @@ -102,6 +111,13 @@ "drop-zone": { "text": "Importa tus listas de reproducción", "subtext": "Suelta tus archivos \".bplist\" o \".json\" aquí para importarlos" + }, + "sort": { + "title": "Título", + "author": "Autor", + "number-of-maps": "Número de mapas", + "duration": "Duración", + "notes-per-second": "NPS" } } } diff --git a/assets/jsons/translations/fr.json b/assets/jsons/translations/fr.json index 06929d43..9f626b80 100644 --- a/assets/jsons/translations/fr.json +++ b/assets/jsons/translations/fr.json @@ -91,6 +91,15 @@ "drop-zone": { "text": "Importer vos maps", "subtext": "Déposez vos fichiers zip ici pour importer vos maps" + }, + "sort": { + "name": "Nom", + "song-author": "Auteur de la chanson", + "map-author": "Auteur de la carte", + "bpm": "BPM", + "duration": "Durée", + "likes": "J'aime", + "date-uploaded": "Date de téléchargement" } }, "playlists": { @@ -102,6 +111,13 @@ "drop-zone": { "text": "Importer vos playlists", "subtext": "Déposez vos fichiers \".bplist\" ou \".json\" ici pour les importer" + }, + "sort": { + "title": "Titre", + "author": "Auteur", + "number-of-maps": "Nombre de cartes", + "duration": "Durée", + "notes-per-second": "NPS" } } } diff --git a/assets/jsons/translations/it.json b/assets/jsons/translations/it.json index 2ad7ca1f..d91f50ff 100644 --- a/assets/jsons/translations/it.json +++ b/assets/jsons/translations/it.json @@ -91,6 +91,15 @@ "drop-zone": { "text": "Importa le tue mappe", "subtext": "Rilascia qui i tuoi file zip per importare le tue mappe" + }, + "sort": { + "name": "Nome", + "song-author": "Autore della canzone", + "map-author": "Autore della mappa", + "bpm": "BPM", + "duration": "Durata", + "likes": "Mi piace", + "date-uploaded": "Data di caricamento" } }, "playlists": { @@ -102,6 +111,13 @@ "drop-zone": { "text": "Importa le tue playlist", "subtext": "Rilascia qui i file \".bplist\" o \".json\" per importarli" + }, + "sort": { + "title": "Titolo", + "author": "Autore", + "number-of-maps": "Numero di mappe", + "duration": "Durata", + "notes-per-second": "NPS" } } } diff --git a/assets/jsons/translations/ja.json b/assets/jsons/translations/ja.json index c97bac6d..2622dcd4 100644 --- a/assets/jsons/translations/ja.json +++ b/assets/jsons/translations/ja.json @@ -91,6 +91,15 @@ "drop-zone": { "text": "マップをインポート", "subtext": "ZIPファイルをここにドロップしてマップをインポート" + }, + "sort": { + "name": "名前", + "song-author": "曲の作者", + "map-author": "地図の作者", + "bpm": "BPM", + "duration": "持続時間", + "likes": "いいね", + "date-uploaded": "アップロード日" } }, "playlists": { @@ -102,6 +111,13 @@ "drop-zone": { "text": "プレイリストをインポート", "subtext": "\".bplist\" または \".json\" ファイルをここにドロップしてインポート" + }, + "sort": { + "title": "タイトル", + "author": "著者", + "number-of-maps": "地図の数", + "duration": "持続時間", + "notes-per-second": "NPS" } } } diff --git a/assets/jsons/translations/ko.json b/assets/jsons/translations/ko.json index c48a5857..a63919d3 100644 --- a/assets/jsons/translations/ko.json +++ b/assets/jsons/translations/ko.json @@ -91,6 +91,15 @@ "drop-zone": { "text": "맵 가져오기", "subtext": "ZIP 파일을 여기에 드롭하여 맵 가져오기" + }, + "sort": { + "name": "이름", + "song-author": "작곡가", + "map-author": "지도 제작자", + "bpm": "BPM", + "duration": "지속 시간", + "likes": "좋아요", + "date-uploaded": "업로드 날짜" } }, "playlists": { @@ -102,6 +111,13 @@ "drop-zone": { "text": "재생 목록 가져오기", "subtext": "\".bplist\" 또는 \".json\" 파일을 여기에 드롭하여 가져오기" + }, + "sort": { + "title": "제목", + "author": "저자", + "number-of-maps": "지도 수", + "duration": "지속 시간", + "notes-per-second": "NPS" } } } diff --git a/assets/jsons/translations/ru.json b/assets/jsons/translations/ru.json index 5fb1a70d..902367b3 100644 --- a/assets/jsons/translations/ru.json +++ b/assets/jsons/translations/ru.json @@ -91,6 +91,15 @@ "drop-zone": { "text": "Импортируйте свои карты", "subtext": "Перетащите сюда файлы ZIP, чтобы импортировать свои карты" + }, + "sort": { + "name": "Имя", + "song-author": "Автор песни", + "map-author": "Автор карты", + "bpm": "BPM", + "duration": "Длительность", + "likes": "Лайки", + "date-uploaded": "Дата загрузки" } }, "playlists": { @@ -102,6 +111,13 @@ "drop-zone": { "text": "Импортируйте ваши плейлисты", "subtext": "Перетащите сюда файлы \".bplist\" или \".json\" для их импорта" + }, + "sort": { + "title": "Заголовок", + "author": "Автор", + "number-of-maps": "Количество карт", + "duration": "Длительность", + "notes-per-second": "NPS" } } } diff --git a/assets/jsons/translations/zh-tw.json b/assets/jsons/translations/zh-tw.json index 614af414..ce2b6964 100644 --- a/assets/jsons/translations/zh-tw.json +++ b/assets/jsons/translations/zh-tw.json @@ -91,6 +91,15 @@ "drop-zone": { "text": "導入您的地圖", "subtext": "將ZIP文件拖放到此處以導入您的地圖" + }, + "sort": { + "name": "名字", + "song-author": "歌曲作者", + "map-author": "地圖作者", + "bpm": "BPM", + "duration": "持續時間", + "likes": "喜歡", + "date-uploaded": "上傳日期" } }, "playlists": { @@ -102,6 +111,13 @@ "drop-zone": { "text": "匯入您的播放列表", "subtext": "將您的 \".bplist\" 或 \".json\" 檔案拖曳到這裡進行匯入" + }, + "sort": { + "title": "標題", + "author": "作者", + "number-of-maps": "地圖數量", + "duration": "持續時間", + "notes-per-second": "NPS" } } } diff --git a/assets/jsons/translations/zh.json b/assets/jsons/translations/zh.json index 0d229eae..f5a6cd15 100644 --- a/assets/jsons/translations/zh.json +++ b/assets/jsons/translations/zh.json @@ -91,6 +91,15 @@ "drop-zone": { "text": "导入您的地图", "subtext": "将ZIP文件拖放到此处以导入您的地图" + }, + "sort": { + "name": "名字", + "song-author": "歌曲作者", + "map-author": "地图作者", + "bpm": "BPM", + "duration": "持续时间", + "likes": "喜欢", + "date-uploaded": "上传日期" } }, "playlists": { @@ -102,6 +111,13 @@ "drop-zone": { "text": "导入您的播放列表", "subtext": "将您的 \".bplist\" 或 \".json\" 文件拖放到这里进行导入" + }, + "sort": { + "title": "标题", + "author": "作者", + "number-of-maps": "地图数量", + "duration": "持续时间", + "notes-per-second": "NPS" } } } diff --git a/src/__tests__/unit/os.test.ts b/src/__tests__/unit/os.test.ts index 616b14b6..28c3e4eb 100644 --- a/src/__tests__/unit/os.test.ts +++ b/src/__tests__/unit/os.test.ts @@ -23,7 +23,7 @@ jest.mock("electron-log", () => ({ info: jest.fn(), error: jest.fn(), })); -jest.mock("ps-list", () => () => []); +jest.mock("ps-list", () => (): unknown[] => []); const IS_WINDOWS = process.platform === "win32"; const IS_LINUX = process.platform === "linux"; 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 31308119..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 @@ -3,10 +3,10 @@ import { BSVersion } from "shared/bs-version.interface"; import { LocalMapsListPanel, LocalMapsListPanelRef } from "./maps/local-maps-list-panel.component"; import { BsmDropdownButton, DropDownItem } from "../shared/bsm-dropdown-button.component"; import { FilterPanel } from "./maps/filter-panel.component"; -import { MapFilter } from "shared/models/maps/beat-saver.model"; +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"; @@ -20,10 +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 { BsmButton } from "../shared/bsm-button.component"; +import { mapSorter } from "shared/models/maps/sorter"; +import { playlistSorter } from "shared/models/playlists/sorter"; type Props = { readonly version?: BSVersion; @@ -39,12 +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 playlistsManager = useService(PlaylistsManagerService); const playlistsDownloader = useService(PlaylistDownloaderService); - const t = useTranslation(); + const { text: t } = useTranslationV2(); const [tabIndex, setTabIndex] = useState(0); const [mapsDropZoneOpen, setMapsDropZoneOpen] = useState(false); @@ -64,7 +71,23 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) { const playlistsRef = useRef(); const [mapFilter, setMapFilter] = useState({}); + const [mapSort, setMapSort] = useState({ + compare: mapSorter.getDefaultComparator(), + ascending: true, + }); const [playlistFilter, setPlaylistFilter] = useState({}); + const [playlistSort, setPlaylistSort] = useState({ + compare: playlistSorter.getDefaultComparator(), + ascending: true, + }); + const [selectedSort, setSelectedSort] = useState("name"); + + const [sortOptions, setSortOptions] = useState[]>( + () => mapSorter.getComparatorKeys().map(key => ({ + text: `pages.version-viewer.maps.tabs.maps.sort.${key}`, + value: key, + })) + ); const [search, setSearch] = useState(""); const mapsLinkedState = useObservable(() => { @@ -100,6 +123,42 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) { return mapsManager.unlinkVersion(version); }; + const handleTabChange = (index: number) => { + if (tabIndex === index) { + return; + } + setTabIndex(index); + + switch (index) { + case MAP_TAB: + setSortOptions(() => mapSorter.getComparatorKeys().map(key => ({ + text: `pages.version-viewer.maps.tabs.maps.sort.${key}`, + value: key, + }))); + setSelectedSort(mapSorter.defaultKey); + setMapSort({ + compare: mapSorter.getDefaultComparator(), + ascending: playlistSort.ascending, + }); + break; + + case PLAYLIST_TAB: + setSortOptions(() => playlistSorter.getComparatorKeys().map(key => ({ + text: `pages.version-viewer.maps.tabs.playlists.sort.${key}`, + value: key, + }))); + setSelectedSort(playlistSorter.defaultKey); + setPlaylistSort({ + compare: playlistSorter.getDefaultComparator(), + ascending: mapSort.ascending, + }); + break; + + default: + break; + } + }; + const handlePlaylistLinkClick = () => { if(playlistLinkedState === FolderLinkState.Pending || playlistLinkedState === FolderLinkState.Processing){ return Promise.resolve(false); } @@ -135,8 +194,50 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) { ]; })(); + const handleSortChange = (key: string) => { + switch (tabIndex) { + case MAP_TAB: + setMapSort({ + compare: mapSorter.getComparator(key), + ascending: mapSort.ascending, + }); + break; + + case PLAYLIST_TAB: + setPlaylistSort({ + compare: playlistSorter.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?.() }, @@ -153,7 +254,7 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) { return ( <> -