[feature-107] use size optimized song details cache

This commit is contained in:
MathieuG-P
2024-04-20 16:22:07 +02:00
parent 6836f18e89
commit 0cea1cd89f
24 changed files with 565 additions and 200 deletions
@@ -1,8 +1,6 @@
import { MapFilter, MapSpecificity, MapStyle, MapTag, MapType } from "shared/models/maps/beat-saver.model";
import { motion } from "framer-motion";
import { MutableRefObject, useEffect, useRef, useState } from "react";
import { MAP_TYPES } from "renderer/partials/maps/map-tags/map-types";
import { MAP_STYLES } from "renderer/partials/maps/map-tags/map-styles";
import { BsmCheckbox } from "../../shared/bsm-checkbox.component";
import { minToS } from "../../../../shared/helpers/time.helpers";
import dateFormat from "dateformat";
@@ -210,14 +208,14 @@ export function FilterPanel({ className, ref, playlist = false, filter, localDat
<section className="grow capitalize">
<h2 className="uppercase text-sm mb-1">{t("maps.map-filter-panel.tags")}</h2>
<div className="w-full flex flex-row flex-wrap items-start justify-start content-start gap-1 mb-2">
{MAP_TYPES.map(tag => (
{Object.values(MapType).map(tag => (
<span key={tag} onClick={() => handleTagClick(tag)} className={`text-[12.5px] text-black rounded-md px-1 font-bold cursor-pointer ${!isTagActivated(tag) && "opacity-40 hover:opacity-90"}`} style={{ backgroundColor: isTagExcluded(tag) ? MAP_DIFFICULTIES_COLORS.Expert : MAP_DIFFICULTIES_COLORS.Normal }}>
{translateMapType(tag)}
</span>
))}
</div>
<div className="w-full flex flex-row flex-wrap items-start justify-start content-start gap-1">
{MAP_STYLES.map(tag => (
{Object.values(MapStyle).map(tag => (
<span key={tag} onClick={() => handleTagClick(tag)} className={`text-[12.5px] text-black rounded-md px-1 font-bold cursor-pointer ${!isTagActivated(tag) && "opacity-40 hover:opacity-90"}`} style={{ backgroundColor: isTagExcluded(tag) ? MAP_DIFFICULTIES_COLORS.Expert : MAP_DIFFICULTIES_COLORS.Easy }}>
{translateMapStyle(tag)}
</span>
@@ -249,10 +249,10 @@ export const LocalMapsListPanel = forwardRef<unknown, Props>(({ version, classNa
return true;
}
if (!map?.songDetails?.metadata?.duration) {
if (!map?.songDetails?.duration) {
return false;
}
return map.songDetails?.metadata.duration >= filter.minDuration;
return map.songDetails?.duration >= filter.minDuration;
})();
if (!fitMinDuration) {
@@ -263,10 +263,10 @@ export const LocalMapsListPanel = forwardRef<unknown, Props>(({ version, classNa
if (!filter?.maxDuration) {
return true;
}
if (!map?.songDetails?.metadata?.duration) {
if (!map?.songDetails?.duration) {
return false;
}
return map.songDetails?.metadata.duration <= filter.maxDuration;
return map.songDetails?.duration <= filter.maxDuration;
})();
if (!fitMaxDuration) {
@@ -346,7 +346,7 @@ export const LocalMapsListPanel = forwardRef<unknown, Props>(({ version, classNa
}
const searchCheck = (() => {
return ((map.rawInfo?._songName ?? map.songDetails?.name) || "")?.toLowerCase().includes(search.toLowerCase()) || ((map.rawInfo?._songAuthorName ?? map.songDetails?.metadata?.songAuthorName) || "")?.toLowerCase().includes(search.toLowerCase()) || ((map.rawInfo?._levelAuthorName ?? map.songDetails?.metadata?.levelAuthorName) || "")?.toLowerCase().includes(search.toLowerCase());
return (map.rawInfo?._songName || "")?.toLowerCase().includes(search.toLowerCase()) || (map.rawInfo?._songAuthorName || "")?.toLowerCase().includes(search.toLowerCase()) || (map.rawInfo?._levelAuthorName || "")?.toLowerCase().includes(search.toLowerCase());
})();
if (!searchCheck) {
@@ -1,5 +1,4 @@
import { BsmImage } from "../../shared/bsm-image.component";
import { BsvMapDifficultyType } from "shared/models/maps/beat-saver.model";
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
import { BsmLink } from "../../shared/bsm-link.component";
import { BsmIcon } from "../../svgs/bsm-icon.component";
@@ -89,7 +88,7 @@ export const MapItem = memo(({ hash, title, autor, songAutor, coverUrl, songUrl,
})();
const parseDiffLabel = (diffLabel: string) => {
if (MAP_DIFFICULTIES.includes(diffLabel as BsvMapDifficultyType)) {
if (MAP_DIFFICULTIES.includes(diffLabel as SongDiffName)) {
return t(`maps.difficulties.${diffLabel}`);
}
return diffLabel;
@@ -135,16 +134,16 @@ export const MapItem = memo(({ hash, title, autor, songAutor, coverUrl, songUrl,
<>
<BsmIcon className="h-4 w-4 mr-px" icon="bsMapDifficulty" />
<div className="flex py-[2px] gap-[1px] h-full">
{diffSet.map(diff => (
<span key={crypto.randomUUID()} className="h-full w-[6px] rounded-full" style={{ backgroundColor: MAP_DIFFICULTIES_COLORS[diff.type] }} />
{diffSet.map((diff, index) => (
<span key={index} className="h-full w-[6px] rounded-full" style={{ backgroundColor: MAP_DIFFICULTIES_COLORS[diff.type] }} />
))}
</div>
</>
);
}
if (diffSets.length > 1) {
return diffSets.map(([diffType, diffSet]) => (
<Fragment key={crypto.randomUUID()}>
return diffSets.map(([diffType, diffSet], index) => (
<Fragment key={index}>
<BsmIcon className="h-full w-fit mr-px" icon={diffType} />
<span className="mr-2 font-bold text-[15px] h-full flex items-center pb-px">{diffSet.length}</span>
</Fragment>
@@ -160,10 +159,10 @@ export const MapItem = memo(({ hash, title, autor, songAutor, coverUrl, songUrl,
<AnimatePresence>
{(diffsPanelHovered || bottomBarHovered) && (
<motion.ul key={hash} className="absolute top-[calc(100%-10px)] w-full h-fit max-h-[200%] pt-4 pb-2 px-2 overflow-y-scroll bg-light-main-color-3 dark:bg-main-color-3 text-main-color-1 dark:text-current brightness-125 rounded-md flex flex-col gap-3 scrollbar-default shadow-sm shadow-black" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.15 }} onHoverStart={diffsPanelHoverStart} onHoverEnd={diffsPanelHoverEnd}>
{Array.from(diffs.entries()).map(([charac, diffSet]) => (
<ol key={crypto.randomUUID()} className="flex flex-col w-full gap-1">
{diffSet.map(({ type, name, stars }) => (
<li key={`${type}${name}${stars}`} className="w-full h-4 flex items-center gap-1">
{Array.from(diffs.entries()).map(([charac, diffSet], index) => (
<ol key={index} className="flex flex-col w-full gap-1">
{diffSet.map(({ type, name, stars }, index) => (
<li key={`${type}${name}${stars}${index}`} className="w-full h-4 flex items-center gap-1">
<BsmIcon className="h-full w-fit p-px" icon={charac} />
<span className="h-full px-2 flex items-center text-xs font-bold bg-current rounded-full" style={{ color: MAP_DIFFICULTIES_COLORS[type] }}>
{stars ? <span className="h-full block brightness-[.25]"> {stars.toFixed(2)}</span> : <span className="h-full brightness-[.25] leading-4 pb-[2px] capitalize">{parseDiffLabel(type)}</span>}
@@ -5,7 +5,7 @@ import { BehaviorSubject } from "rxjs";
import { BsmLocalMap } from "shared/models/maps/bsm-local-map.interface";
import { ParsedMapDiff, MapItem } from "./map-item.component";
import equal from "fast-deep-equal/es6";
import { SongDetailDiffCharactertistic, SongDetails } from "shared/models/maps/song-details-cache.model";
import { SongDetailDiffCharactertistic, SongDetails } from "shared/models/maps/song-details-cache/song-details-cache.model";
import { BsvMapDetail, RawMapInfoData } from "shared/models/maps";
type Props = {
@@ -33,7 +33,7 @@ export const MapsRow = memo(({ maps, style, selectedMaps$, onMapSelect, onMapDel
autor={map.rawInfo._levelAuthorName}
songAutor={map.rawInfo._songAuthorName}
bpm={map.rawInfo._beatsPerMinute}
duration={map.songDetails?.metadata.duration}
duration={map.songDetails?.duration}
selected={selectedMaps.some(selected => selected.hash === map.hash)}
diffs={extractMapDiffs({ rawMapInfo: map.rawInfo, songDetails: map.songDetails })}
mapId={map.songDetails?.id}
@@ -22,6 +22,7 @@ import { PlaylistDownloaderService } from "renderer/services/playlist-downloader
import { ProgressBarService } from "renderer/services/progress-bar.service";
import { NotificationService } from "renderer/services/notification.service";
import { DeletePlaylistModal } from "renderer/components/modal/modal-types/playlist/delete-playlist-modal.component";
import { OsDiagnosticService } from "renderer/services/os-diagnostic.service";
type Props = {
version: BSVersion;
@@ -39,8 +40,10 @@ export const LocalPlaylistsListPanel = forwardRef<unknown, Props>(({ version, cl
const modals = useService(ModalService);
const ipc = useService(IpcService);
const progress = useService(ProgressBarService);
const osDiagnostic = useService(OsDiagnosticService);
const notification = useService(NotificationService);
const isOnline = useObservable(() => osDiagnostic.isOnline$, false);
const isActiveOnce = useChangeUntilEqual(isActive, { untilEqual: true });
const { maps$, playlists$, setPlaylists } = useContext(InstalledMapsContext);
@@ -164,7 +167,7 @@ export const LocalPlaylistsListPanel = forwardRef<unknown, Props>(({ version, cl
isInQueue$={playlistDownloader.$isPlaylistInQueue(p, version)}
onClickOpen={() => openPlaylistDetails(p.path)}
onClickDelete={() => deletePlaylist(p)}
onClickSync={() => installPlaylist(p)}
onClickSync={isOnline && (() => installPlaylist(p))}
onClickOpenFile={() => viewPlaylistFile(p.path)}
onClickCancelDownload={() => playlistDownloader.cancelDownload(p, version)}
/>
@@ -1,7 +1,7 @@
import { motion } from "framer-motion";
import { useCallback, useEffect, useRef, useState } from "react";
import { FilterPanel } from "renderer/components/maps-playlists-panel/maps/filter-panel.component";
import { MapItem, ParsedMapDiff } from "renderer/components/maps-playlists-panel/maps/map-item.component";
import { MapItem } from "renderer/components/maps-playlists-panel/maps/map-item.component";
import { BsmButton } from "renderer/components/shared/bsm-button.component";
import { BsmDropdownButton } from "renderer/components/shared/bsm-dropdown-button.component";
import { BsmSelect, BsmSelectOption } from "renderer/components/shared/bsm-select.component";
@@ -11,7 +11,7 @@ import { BeatSaverService } from "renderer/services/thrird-partys/beat-saver.ser
import { MapsDownloaderService } from "renderer/services/maps-downloader.service";
import { ModalComponent } from "renderer/services/modale.service";
import { BSVersion } from "shared/bs-version.interface";
import { BsvMapCharacteristic, BsvMapDetail, MapFilter, SearchOrder, SearchParams } from "shared/models/maps/beat-saver.model";
import { BsvMapDetail, MapFilter, SearchOrder, SearchParams } from "shared/models/maps/beat-saver.model";
import BeatWaitingImg from "../../../../../assets/images/apngs/beat-waiting.png";
import BeatConflictImg from "../../../../../assets/images/apngs/beat-conflict.png";
import equal from "fast-deep-equal/es6";
@@ -106,7 +106,7 @@ export const LocalPlaylistDetailsModal: ModalComponent<void, Props> = ({resolver
autor={map.rawInfo._levelAuthorName}
songAutor={map.rawInfo._songAuthorName}
bpm={map.rawInfo._beatsPerMinute}
duration={map.songDetails?.metadata.duration}
duration={map.songDetails?.duration}
diffs={extractMapDiffs({ rawMapInfo: map.rawInfo, songDetails: map.songDetails })}
mapId={map.songDetails?.id}
ranked={map.songDetails?.ranked}
@@ -32,7 +32,6 @@ import { ThumbUpFillIcon } from "./icons/thumb-up-fill-icon.component";
import { TimerFillIcon } from "./icons/timer-fill.component";
import { CheckCircleIcon } from "./icons/check-circle-icon.component";
import { PauseIcon } from "./icons/pause-icon.component";
import { BsvMapCharacteristic } from "shared/models/maps/beat-saver.model";
import { LightshowIcon } from "./icons/lightshow-icon.component";
import { LawlessIcon } from "./icons/lawless-icon.component";
import { NoArrowIcon } from "./icons/no-arrow-icon.component";
@@ -60,8 +59,9 @@ import { ChevronTopIcon } from "./icons/chevron-top-icon.component";
import { EyeCrossIcon } from "./icons/eye-cross-icon.component";
import { ShortcutIcon } from "./icons/shortcut-icon.component";
import { BackupRestoreIcon } from "./icons/backup-restore-icon.component";
import { SongDetailDiffCharactertistic } from "shared/models/maps/song-details-cache/song-details-cache.model";
export type BsmIconType = BsvMapCharacteristic | ("settings" | "trash" | "favorite" | "folder" | "bsNote" | "check" | "three-dots" | "twitch" | "eye" | "play" | "checkCircleIcon" | "discord" | "info" | "eye-cross" | "terminal" | "desktop" | "oculus" | "add" | "cross" | "task" | "github" | "close" | "thumbUpFill" | "timerFill" | "pause" | "twitter" | "sync" | "chevron-top" | "copy" | "steam" | "edit" | "export" | "patreon" | "search" | "bsMapDifficulty" | "link" | "unlink" | "download" | "filter" | "mee6" | "volume-up" | "volume-off" | "volume-down" | "shortcut" | "backup-restore" | "fr-FR-flag" | "es-ES-flag" | "en-US-flag" | "en-EN-flag" | "de-DE-flag" | "ru-RU-flag" | "zh-CN-flag" | "zh-TW-flag" | "ja-JP-flag");
export type BsmIconType = SongDetailDiffCharactertistic | ("settings" | "trash" | "favorite" | "folder" | "bsNote" | "check" | "three-dots" | "twitch" | "eye" | "play" | "checkCircleIcon" | "discord" | "info" | "eye-cross" | "terminal" | "desktop" | "oculus" | "add" | "cross" | "task" | "github" | "close" | "thumbUpFill" | "timerFill" | "pause" | "twitter" | "sync" | "chevron-top" | "copy" | "steam" | "edit" | "export" | "patreon" | "search" | "bsMapDifficulty" | "link" | "unlink" | "download" | "filter" | "mee6" | "volume-up" | "volume-off" | "volume-down" | "shortcut" | "backup-restore" | "fr-FR-flag" | "es-ES-flag" | "en-US-flag" | "en-EN-flag" | "de-DE-flag" | "ru-RU-flag" | "zh-CN-flag" | "zh-TW-flag" | "ja-JP-flag");
export const BsmIcon = memo(({ className, icon, style }: { className?: string; icon: BsmIconType; style?: CSSProperties }) => {
// TODO : Very ugly very messy, need to find a better way to do this
@@ -1,6 +1,6 @@
import { BsvMapDifficultyType } from "shared/models/maps/beat-saver.model";
import { SongDiffName } from "shared/models/maps/song-details-cache/song-details-cache.model";
export const MAP_DIFFICULTIES_COLORS: Record<BsvMapDifficultyType, string> = {
export const MAP_DIFFICULTIES_COLORS: Record<SongDiffName, string> = {
Easy: "#00FF9E",
Normal: "#00FFFF",
Hard: "#FFA700",
@@ -1,3 +1,2 @@
import { BsvMapDifficultyType } from "shared/models/maps/beat-saver.model";
export const MAP_DIFFICULTIES: BsvMapDifficultyType[] = ["Easy", "Normal", "Hard", "Expert", "ExpertPlus"];
import { SongDiffName } from "shared/models/maps/song-details-cache/song-details-cache.model";
export const MAP_DIFFICULTIES = Object.values(SongDiffName);
@@ -1,3 +0,0 @@
import { MapStyle } from "shared/models/maps/beat-saver.model";
export const MAP_STYLES: MapStyle[] = ["dance", "swing", "nightcore", "folk", "family", "ambient", "funk", "jazz", "soul", "speedcore", "punk", "rb", "holiday", "vocaloid", "j-rock", "trance", "drumbass", "comedy", "instrumental", "hardcore", "k-pop", "indie", "techno", "house", "game", "film", "alt", "dubstep", "metal", "anime", "hiphop", "j-pop", "rock", "pop", "electronic", "classical-orchestral"];