mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
[feature-107] translations
This commit is contained in:
@@ -108,10 +108,10 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) {
|
||||
}
|
||||
|
||||
return [
|
||||
{ icon: "add", text: "Créer une playlist", onClick: () => playlistsRef?.current?.createPlaylist?.() },
|
||||
{ icon: "sync", text: "Synchroniser les playlists", onClick: () => playlistsRef?.current?.syncPlaylists?.() },
|
||||
{ icon: "export", text: "Exporter les playlists", onClick: () => playlistsRef?.current?.exportPlaylists?.() },
|
||||
{ icon: "trash", text: "Supprimer les playlists", onClick: () => playlistsRef?.current?.deletePlaylists?.() },
|
||||
{ icon: "add", text: t("playlist.create-a-playlist"), onClick: () => playlistsRef?.current?.createPlaylist?.() },
|
||||
{ icon: "sync", text: t("playlist.synchronize-playlists"), onClick: () => playlistsRef?.current?.syncPlaylists?.() },
|
||||
{ icon: "export", text: t("playlist.export-playlists"), onClick: () => playlistsRef?.current?.exportPlaylists?.() },
|
||||
{ icon: "trash", text: t("playlist.delete-playlists"), onClick: () => playlistsRef?.current?.deletePlaylists?.() },
|
||||
];
|
||||
})();
|
||||
|
||||
@@ -119,7 +119,7 @@ export function MapsPlaylistsPanel({ version, isActive }: Props) {
|
||||
<div className="w-full h-full flex flex-col items-center justify-center gap-4">
|
||||
<nav className="w-full shrink-0 flex h-9 justify-center px-40 gap-2 text-main-color-1 dark:text-white">
|
||||
<BsmButton
|
||||
className="flex items-center justify-center w-fit rounded-full px-2 py-1 font-bold"
|
||||
className="flex items-center justify-center w-fit rounded-full px-2 py-1 font-bold whitespace-nowrap"
|
||||
icon="add"
|
||||
text="misc.add"
|
||||
typeColor="primary"
|
||||
@@ -130,13 +130,13 @@ 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") : "Rechercher une playlist"}
|
||||
placeholder={tabIndex === 0 ? t("pages.version-viewer.maps.search-bar.search-placeholder") : t("playlist.search-playlist")}
|
||||
value={search}
|
||||
onChange={e => setSearch(() => e.target.value)}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
</div>
|
||||
<BsmDropdownButton className="h-full relative z-[1] flex justify-center" buttonClassName="flex items-center justify-center h-full rounded-full px-2 py-1" icon="filter" text="pages.version-viewer.maps.search-bar.filters-btn" textClassName="whitespace-nowrap" withBar={false}>
|
||||
<BsmDropdownButton className="h-full relative z-[1] flex justify-center" buttonClassName="flex items-center justify-center h-full rounded-full px-2 py-1 whitespace-nowrap" icon="filter" text="pages.version-viewer.maps.search-bar.filters-btn" textClassName="whitespace-nowrap" withBar={false}>
|
||||
{(
|
||||
tabIndex === 0 ? (
|
||||
<FilterPanel className="absolute top-[calc(100%+3px)] origin-top w-[500px] h-fit p-2 rounded-md shadow-md shadow-black" filter={mapFilter} onChange={setMapFilter} />
|
||||
|
||||
@@ -210,7 +210,7 @@ export function MapItemComponent <T = unknown>({ hash, title, autor, songAutor,
|
||||
{diffSet.map(({ name, libelle, stars }, index) => (
|
||||
<li key={`${name}${libelle}${stars}${index}`} className="w-full h-4 flex items-center gap-1">
|
||||
{onHighlightedDiffsChange && (
|
||||
<Tippy content="Surligner la difficulté" placement="top" theme="default">
|
||||
<Tippy content={t("maps.map-item.hightlight-difficulty")} placement="top" theme="default">
|
||||
<div className="h-full aspect-square">
|
||||
<BsmCheckbox className="size-full relative" checked={isDiffHightlighted({name, characteristic: charac})} onChange={() => handleDiffCheckChange({name, characteristic: charac})} />
|
||||
</div>
|
||||
@@ -251,7 +251,7 @@ export function MapItemComponent <T = unknown>({ hash, title, autor, songAutor,
|
||||
<BsmLink className="hover:underline" href={mapUrl}>
|
||||
{title}
|
||||
</BsmLink> ) : (
|
||||
<h1>{title}</h1>
|
||||
title
|
||||
)}
|
||||
</h1>
|
||||
<h2 className="font-bold whitespace-nowrap text-ellipsis overflow-hidden w-full text-sm mb-[3px]">{songAutor && t("maps.map-item.by", { songAutor })}</h2>
|
||||
|
||||
+4
-4
@@ -91,19 +91,19 @@ export function LocalPlaylistFilterPanel({ className, filter, onChange }: Props)
|
||||
<motion.div className={cn("bg-theme-3 flex flex-col gap-1.5 p-2 absolute origin-top shadow-md shadow-black rounded-md", className)} initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{duration: .1}}>
|
||||
<div className="w-full flex flex-col justify-center p-2">
|
||||
<BsmRange values={[minNbMaps ?? MIN_NB_MAPS, maxNbMaps ?? MAX_NB_MAPS]} min={MIN_NB_MAPS} max={MAX_NB_MAPS} step={1} onChange={handleOnNbMapsChange} renderLabel={v => renderSimpleMinMaxLabel(v, MAX_NB_MAPS)}/>
|
||||
<span className=" text-sm font-bold text-center mt-2.5">Nombre de maps</span>
|
||||
<span className=" text-sm font-bold text-center mt-2.5">{t("playlist.nb-maps")}</span>
|
||||
</div>
|
||||
<div className="w-full flex flex-col justify-center p-2">
|
||||
<BsmRange values={[minNbMappers ?? MIN_NB_MAPPER, maxNbMappers ?? MAX_NB_MAPPER]} min={MIN_NB_MAPPER} max={MAX_NB_MAPPER} step={1} onChange={handleOnNbMapperChange} renderLabel={v => renderSimpleMinMaxLabel(v, MAX_NB_MAPPER)}/>
|
||||
<span className=" text-sm font-bold text-center mt-2.5">Nombre de mappeurs</span>
|
||||
<span className=" text-sm font-bold text-center mt-2.5">{t("playlist.nb-mappers")}</span>
|
||||
</div>
|
||||
<div className="w-full flex flex-col justify-center p-2">
|
||||
<BsmRange values={[minDuration ?? MIN_DURATION, maxDuration ?? MAX_DURATION]} min={MIN_DURATION} max={MAX_DURATION} step={1} onChange={handleOnDurationChange} renderLabel={renderDurationLabel}/>
|
||||
<span className=" text-sm font-bold text-center mt-2.5">Durée</span>
|
||||
<span className=" text-sm font-bold text-center mt-2.5">{t("playlist.duration")}</span>
|
||||
</div>
|
||||
<div className="w-full flex flex-col justify-center p-2">
|
||||
<BsmRange values={[minNps ?? MIN_NPS, maxNps ?? MAX_NPS]} min={MIN_NPS} max={MAX_NPS} step={0.1} onChange={handleOnNpsChange} renderLabel={v => renderSimpleMinMaxLabel(v, MAX_NPS)}/>
|
||||
<span className=" text-sm font-bold text-center mt-2.5">Notes par secondes</span>
|
||||
<span className=" text-sm font-bold text-center mt-2.5">{t("playlist.nps")}</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
)
|
||||
|
||||
+19
-19
@@ -57,8 +57,6 @@ export type LocalPlaylistsListRef = {
|
||||
exportPlaylists: () => Promise<void>;
|
||||
}
|
||||
|
||||
// TODO : Translate
|
||||
|
||||
export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(({ version, className, filter: playlistFiler, search, isActive, linkedState }, forwardedRef) => {
|
||||
|
||||
const t = useTranslation();
|
||||
@@ -90,7 +88,7 @@ export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(
|
||||
|
||||
useImperativeHandle(forwardedRef, () => ({
|
||||
createPlaylist: async () => {
|
||||
const modalRes = await modals.openModal(EditPlaylistModal, { noStyle: true, data: { version, maps$ } });
|
||||
const modalRes = await modals.openModal(EditPlaylistModal, { noStyle: true, data: { maps$ } });
|
||||
|
||||
if(modalRes.exitCode !== ModalExitCode.COMPLETED){ return; }
|
||||
|
||||
@@ -98,14 +96,14 @@ export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(
|
||||
|
||||
if(error){
|
||||
logRenderError("Error occured while creating playlist", error);
|
||||
notification.notifyError({ title: "Erreur lors de la création de la playlist", desc: "Une erreur est survenue lors de la création de la playlist." });
|
||||
notification.notifyError({ title: "playlist.error-playlist-creation-title", desc: "playlist.error-playlist-creation-desc" });
|
||||
return;
|
||||
}
|
||||
|
||||
setPlaylists([result, ...playlists$.value]);
|
||||
|
||||
const notifRes = await notification.notifySuccess({ title: "Playlist créée !", desc: "La playlist a été créée avec succès. Tu peut maintenant synchroniser ses maps !", duration: 8000, actions: [
|
||||
{ id: "sync", title: "Synchroniser les maps" }
|
||||
const notifRes = await notification.notifySuccess({ title: "playlist.playlist-created-title", desc: "playlist.playlist-created-desc", duration: 8000, actions: [
|
||||
{ id: "sync", title: "playlist.synchronize-maps" }
|
||||
]});
|
||||
|
||||
if(notifRes === "sync"){
|
||||
@@ -127,12 +125,12 @@ export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(
|
||||
|
||||
if(error){
|
||||
logRenderError("Error occured while synchronizing playlists", error);
|
||||
notification.notifyError({ title: "Erreur lors de la synchronisation des playlists", desc: "Une erreur est survenue lors de la synchronisation des playlists." });
|
||||
notification.notifyError({ title: "playlist.error-playlists-synchronization-title", desc: "playlist.error-playlists-synchronization-desc" });
|
||||
return;
|
||||
}
|
||||
|
||||
if(result.every(res => res.current === res.total)){
|
||||
notification.notifySuccess({ title: "Playlists synchronisées !", desc: "Les playlists et leurs maps ont été téléchargées.", duration: 5000 });
|
||||
notification.notifySuccess({ title: "playlist.playlists-synchronized-title", desc: "playlist.playlists-synchronized-desc", duration: 5000 });
|
||||
}
|
||||
},
|
||||
exportPlaylists: async () => {
|
||||
@@ -159,11 +157,11 @@ export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(
|
||||
|
||||
if(error){
|
||||
logRenderError("Error occured while exporting playlists", error);
|
||||
notification.notifyError({ title: "Erreur lors de l'exportation des playlists", desc: "Une erreur est survenue lors de l'exportation des playlists." });
|
||||
notification.notifyError({ title: "playlist.playlists-export-error-title", desc: "playlist.playlists-export-error-desc" });
|
||||
return;
|
||||
}
|
||||
|
||||
notification.notifySuccess({ title: "Playlists exportées !", desc: "Les playlists et leurs maps ont été exportées.", duration: 5000 });
|
||||
notification.notifySuccess({ title: "playlist.playlists-exported-title", desc: exportMaps ? "playlist.playlists-exported-desc" : "playlist.playlists-with-maps-exported-desc", duration: 5000 });
|
||||
|
||||
progess.hide(true);
|
||||
},
|
||||
@@ -229,7 +227,7 @@ export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(
|
||||
|
||||
return lastValueFrom(obs$).then(res => {
|
||||
if(res.current === res.total){
|
||||
notification.notifySuccess({ title: "Playlist synchronisée !", desc: "La playlist et ses maps on été téléchargées.", duration: 5000 })
|
||||
notification.notifySuccess({ title: "playlist.playlists-synchronized-title", desc: "playlist.playlists-synchronized-desc", duration: 5000 })
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -255,7 +253,7 @@ export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(
|
||||
|
||||
if(error){
|
||||
logRenderError("Error occured while deleting playlist", error);
|
||||
notification.notifyError({ title: "Erreur lors de la suppression de la playlist", desc: "Une erreur est survenue lors de la suppression de la playlist." });
|
||||
notification.notifyError({ title: "playlist.playlist-delete-error-title", desc: "playlist.playlist-delete-error-desc" });
|
||||
progess.hide(true);
|
||||
return;
|
||||
}
|
||||
@@ -268,6 +266,8 @@ export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(
|
||||
}
|
||||
}
|
||||
|
||||
notification.notifySuccess({ title: "playlist.playlists-deleted-title", desc: "playlist.playlists-deleted-desc", duration: 5000 });
|
||||
|
||||
progess.hide(true);
|
||||
};
|
||||
|
||||
@@ -314,7 +314,7 @@ export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(
|
||||
}
|
||||
|
||||
const tmpPlaylist: LocalBPList = { ...playlist, playlistTitle: needClone ? `${playlist.playlistTitle} (${t("Clone")})` : playlist.playlistTitle };
|
||||
const modalRes = await modals.openModal(EditPlaylistModal, { noStyle: true, data: { version, maps$, playlist: tmpPlaylist } });
|
||||
const modalRes = await modals.openModal(EditPlaylistModal, { noStyle: true, data: { maps$, playlist: tmpPlaylist } });
|
||||
|
||||
if(modalRes.exitCode !== ModalExitCode.COMPLETED){ return; }
|
||||
|
||||
@@ -322,7 +322,7 @@ export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(
|
||||
|
||||
if(error){
|
||||
logRenderError("Error occured while editing playlist", error);
|
||||
notification.notifyError({ title: "Erreur lors de la modification de la playlist", desc: "Une erreur est survenue lors de la modification de la playlist." });
|
||||
notification.notifyError({ title: "playlist.playlist-edit-error-title", desc: "playlist.playlist-edit-error-desc" });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -338,8 +338,8 @@ export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(
|
||||
|
||||
setPlaylists(newPlaylists);
|
||||
|
||||
const notifRes = await notification.notifySuccess({ title: "Playlist modifiée !", desc: "La playlist a été modifiée avec succès. Tu peut maintenant synchroniser ses maps !", duration: 8000, actions: [
|
||||
{ id: "sync", title: "Synchroniser les maps" }
|
||||
const notifRes = await notification.notifySuccess({ title: "playlist.playlist-edited-title", desc: "playlist.playlist-edited-desc", duration: 8000, actions: [
|
||||
{ id: "sync", title: "playlist.synchronize-maps" }
|
||||
]});
|
||||
|
||||
if(notifRes === "sync"){
|
||||
@@ -409,7 +409,7 @@ export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(
|
||||
{(() => {
|
||||
if(playlistsLoading){
|
||||
return (
|
||||
<BsContentLoader className="w-full h-full flex justify-center flex-col items-center" value$={loadPercent$} text="Chargement des playlists"/>
|
||||
<BsContentLoader className="w-full h-full flex justify-center flex-col items-center" value$={loadPercent$} text="playlist.playlists-loading"/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -432,10 +432,10 @@ export const LocalPlaylistsListPanel = forwardRef<LocalPlaylistsListRef, Props>(
|
||||
return (
|
||||
<div className="h-full flex flex-col items-center justify-center flex-wrap gap-1 text-gray-800 dark:text-gray-200">
|
||||
<BsmImage className="h-32" image={BeatConflict} />
|
||||
<span className="font-bold">Aucune playlist</span>
|
||||
<span className="font-bold">{t("playlist.no-playlists")}</span>
|
||||
<BsmButton
|
||||
className="font-bold rounded-md p-2"
|
||||
text="Télécharger des playlists"
|
||||
text="playlist.download-playlists"
|
||||
typeColor="primary"
|
||||
withBar={false}
|
||||
onClick={e => {
|
||||
|
||||
@@ -16,6 +16,7 @@ import { useObservable } from 'renderer/hooks/use-observable.hook';
|
||||
import { BsmBasicSpinner } from 'renderer/components/shared/bsm-basic-spinner/bsm-basic-spinner.component';
|
||||
import defaultImage from "../../../../../assets/images/default-version-img.jpg";
|
||||
import equal from 'fast-deep-equal';
|
||||
import { useTranslation } from 'renderer/hooks/use-translation.hook';
|
||||
|
||||
export type PlaylistItemComponentProps = {
|
||||
title?: string;
|
||||
@@ -62,6 +63,7 @@ export const PlaylistItem = memo(({ title,
|
||||
onClickEdit
|
||||
}: PlaylistItemComponentProps) => {
|
||||
|
||||
const t = useTranslation();
|
||||
const color = useThemeColor("first-color");
|
||||
|
||||
const [hovered, setHovered] = useState(false);
|
||||
@@ -82,8 +84,6 @@ export const PlaylistItem = memo(({ title,
|
||||
return duration > 3600 ? dateFormat(duration * 1000, "h:MM:ss") : dateFormat(duration * 1000, "MM:ss");
|
||||
})();
|
||||
|
||||
// TODO : Translate
|
||||
|
||||
return (
|
||||
<motion.li className='relative flex-grow basis-0 min-w-80 h-28 cursor-pointer group' onHoverStart={() => setHovered(() => true)} onHoverEnd={() => setHovered(() => false)}>
|
||||
<GlowEffect visible={selected || hovered}/>
|
||||
@@ -101,7 +101,7 @@ export const PlaylistItem = memo(({ title,
|
||||
</div>
|
||||
<div className="h-full py-2.5 text-white">
|
||||
<h1 className="font-bold text-xl tracking-wide line-clamp-1 w-fit">{title}</h1>
|
||||
<p className="text-xs font-bold">Créé par <span className="brightness-200" style={{color}}>{author}</span></p>
|
||||
<p className="text-xs font-bold">{t("playlist.created-by")} <span className="brightness-200" style={{color}}>{author}</span></p>
|
||||
|
||||
<div className="flex flex-row flex-wrap w-full gap-2 mt-1">
|
||||
{ nbMapsText && <div className="flex items-center text-sm h-5 gap-0.5"> <MapIcon className='h-full aspect-square'/> <span className="mb-0.5">{nbMapsText}</span> </div> }
|
||||
@@ -117,7 +117,7 @@ export const PlaylistItem = memo(({ title,
|
||||
|
||||
<motion.div className="flex flex-col justify-center items-center flex-wrap gap-0.5 size-full px-1 *:size-6 *:!bg-inherit *:p-0.5 *:rounded-md" animate={{opacity: hovered || isDownloading ? 1 : 0}} transition={{duration: 0}}>
|
||||
{(isDownloading || isInQueue) && onClickCancelDownload ? (
|
||||
<Tippy content={isDownloading ? "Arreter le téléchargement" : "Annuler le téléchargement"} placement="left" theme="default">
|
||||
<Tippy content={isDownloading ? t("playlist.stop-download") : t("playlist.cancel-download")} placement="left" theme="default">
|
||||
<BsmButton
|
||||
icon="close"
|
||||
className="hover:!bg-main-color-1 text-red-500 !p-0"
|
||||
@@ -131,7 +131,7 @@ export const PlaylistItem = memo(({ title,
|
||||
isDownloading ? (
|
||||
<BsmBasicSpinner className="hover:!bg-main-color-1" spinnerClassName="brightness-75 dark:brightness-200" style={{ color }} thikness="3px"/>
|
||||
) : !isInQueue ? (
|
||||
<Tippy content="Synchronizer la playlist" placement="left" theme="default">
|
||||
<Tippy content={t("playlist.synchronize-playlist")} placement="left" theme="default">
|
||||
<BsmButton
|
||||
icon="sync"
|
||||
className="hover:!bg-main-color-1"
|
||||
@@ -148,7 +148,7 @@ export const PlaylistItem = memo(({ title,
|
||||
isDownloading ? (
|
||||
<BsmBasicSpinner className="hover:!bg-main-color-1" spinnerClassName="brightness-75 dark:brightness-200" style={{ color }} thikness="3px"/>
|
||||
) : !isInQueue ? (
|
||||
<Tippy content="Télécharger la playlist" placement="left" theme="default">
|
||||
<Tippy content={t("playlist.download-playlist")} placement="left" theme="default">
|
||||
<BsmButton
|
||||
icon="download"
|
||||
className="hover:!bg-main-color-1"
|
||||
@@ -162,7 +162,7 @@ export const PlaylistItem = memo(({ title,
|
||||
|
||||
) : (<></>)}
|
||||
{onClickEdit ? (
|
||||
<Tippy content="Editer la playlist" placement="left" theme="default">
|
||||
<Tippy content={t("playlist.edit-playlist")} placement="left" theme="default">
|
||||
<BsmButton
|
||||
icon="edit"
|
||||
className="hover:!bg-main-color-1"
|
||||
@@ -173,7 +173,7 @@ export const PlaylistItem = memo(({ title,
|
||||
/>
|
||||
</Tippy>
|
||||
) : (<></>)}
|
||||
{onClickOpenFile ? <Tippy content="Afficher le fichier" placement="left" theme="default">
|
||||
{onClickOpenFile ? <Tippy content={t("playlist.open-file")} placement="left" theme="default">
|
||||
<BsmButton
|
||||
icon="folder"
|
||||
className="hover:!bg-main-color-1"
|
||||
@@ -183,7 +183,7 @@ export const PlaylistItem = memo(({ title,
|
||||
withBar={false}
|
||||
/>
|
||||
</Tippy> : (<></>)}
|
||||
{(onClickDelete && !isDownloading && !isInQueue) ? <Tippy content="Supprimer" placement="left" theme="default">
|
||||
{(onClickDelete && !isDownloading && !isInQueue) ? <Tippy content={t("misc.delete")} placement="left" theme="default">
|
||||
<BsmButton
|
||||
icon="trash"
|
||||
className="hover:!bg-main-color-1 text-red-500"
|
||||
|
||||
@@ -5,28 +5,29 @@ import { BsmImage } from "renderer/components/shared/bsm-image.component";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { ModalComponent, ModalExitCode } from "renderer/services/modale.service";
|
||||
import BeatRunning from "../../../../../assets/images/apngs/beat-running.png";
|
||||
import Tippy from "@tippyjs/react";
|
||||
|
||||
export const LinkPlaylistModal: ModalComponent<boolean> = ({ resolver }) => {
|
||||
const t = useTranslation();
|
||||
const [keepMaps, setKeepMaps] = useState(true);
|
||||
|
||||
// TODO : Translate
|
||||
|
||||
return (
|
||||
<form className="text-gray-800 dark:text-gray-200">
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">Lier les playlists</h1>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t("playlist.link-playlists")}</h1>
|
||||
<BsmImage className="mx-auto h-24" image={BeatRunning} />
|
||||
<p className="max-w-sm w-full">La liaison des playlists permet de partager les playlists entre toute les version. Une fois liée, cette version profitera des playlists partagées</p>
|
||||
<p className="max-w-sm w-full text-sm italic mt-2">L'ajout et la suppression de playlists sera également partagé</p>
|
||||
<p className="max-w-sm w-full">{t("playlist.link-playlist-desc")}</p>
|
||||
<p className="max-w-sm w-full text-sm italic mt-2">{t("playlist.link-playlist-info")}</p>
|
||||
<div className="relative h-5 flex my-3 items-center">
|
||||
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1.5 z-[1]" checked={keepMaps} onChange={setKeepMaps} />
|
||||
<span className="text-sm mb-0.5 cursor-help" title="Conserver les playlists déplacera les playlists de la version actuelle dans le dossier des playlists partagées. Dans le cas contraire elles seront perdues">
|
||||
Conserver les playlists
|
||||
</span>
|
||||
<Tippy content={t("playlist.keep-playlists-tip")} placement="top" theme="default">
|
||||
<span className="text-sm mb-0.5 cursor-help">
|
||||
{t("playlist.keep-playlists")}
|
||||
</span>
|
||||
</Tippy>
|
||||
</div>
|
||||
<div className="grid grid-flow-col grid-cols-2 gap-4 mt-4">
|
||||
<BsmButton typeColor="cancel" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.CANCELED })} withBar={false} text="misc.cancel" />
|
||||
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.COMPLETED, data: keepMaps })} withBar={false} text="Lier les playlists" />
|
||||
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.COMPLETED, data: keepMaps })} withBar={false} text="playlist.link-playlists" />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
||||
+6
-8
@@ -8,8 +8,6 @@ import BeatConflict from "../../../../../../assets/images/apngs/beat-conflict.pn
|
||||
import { BPList } from "shared/models/playlists/playlist.interface";
|
||||
import Tippy from "@tippyjs/react";
|
||||
|
||||
// TODO : Translate
|
||||
|
||||
export const DeletePlaylistModal: ModalComponent<boolean, BPList[]> = ({ resolver, options: { data }}) => {
|
||||
|
||||
const t = useTranslation();
|
||||
@@ -20,21 +18,21 @@ export const DeletePlaylistModal: ModalComponent<boolean, BPList[]> = ({ resolve
|
||||
return (
|
||||
<form className="text-gray-800 dark:text-gray-200">
|
||||
{!isMultiple ? (
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">Supprimer la playlist ?</h1>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t("playlist.delete-playlist-ask")}</h1>
|
||||
) : (
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">Supprimer les playlists ?</h1>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t("playlist.delete-playlists-ask")}</h1>
|
||||
)}
|
||||
<BsmImage className="mx-auto h-24" image={BeatConflict} />
|
||||
{!isMultiple ? (
|
||||
<p className="max-w-sm w-full">{`Est-tu sûr de vouloir supprimer la playlist "${data.at(0)?.playlistTitle}" ?`}</p>
|
||||
<p className="max-w-sm w-full">{t("playlist.delete-playlist-desc", { playlistTitle: data.at(0)?.playlistTitle })}</p>
|
||||
) : (
|
||||
<p className="max-w-sm w-full">{`Est-tu sûr de vouloir supprimer les ${data.length} playlists ?`}</p>
|
||||
<p className="max-w-sm w-full">{t("playlist.delete-playlists-desc", { nb: `${data?.length ?? 0}` })}</p>
|
||||
)}
|
||||
|
||||
<div className="flex items-center relative py-2 gap-1">
|
||||
<BsmCheckbox className="h-5 relative z-[1]" checked={deleteMaps} onChange={val => setDeleteMaps(() => val)} />
|
||||
<Tippy placement="top" content={isMultiple ? "Si activé, toutes les maps de des playlists seront supprimées" : "Si activé, toutes les maps de la playlist seront supprimées"} theme="default">
|
||||
<span className="italic cursor-help">Supprimer les maps</span>
|
||||
<Tippy placement="top" content={isMultiple ? t("playlist.delete-playlists-maps-tip") : t("playlist.delete-playlist-maps-tip")} theme="default">
|
||||
<span className="italic cursor-help">{t("playlist.delete-maps")}</span>
|
||||
</Tippy>
|
||||
</div>
|
||||
<div className="grid grid-flow-col grid-cols-2 gap-4 mt-2">
|
||||
|
||||
+9
-11
@@ -26,8 +26,6 @@ type Props = {
|
||||
const MAX_NPS = 17;
|
||||
const MIN_NPS = 0;
|
||||
|
||||
// TODO : Translate
|
||||
|
||||
export function DownloadPlaylistFilterPanel({ className, params, onChange, onSubmit }: Props) {
|
||||
|
||||
const t = useTranslation();
|
||||
@@ -116,15 +114,15 @@ export function DownloadPlaylistFilterPanel({ className, params, onChange, onSub
|
||||
<h2 className="mb-0.5 uppercase text-sm">{t("maps.map-filter-panel.specificities")}</h2>
|
||||
<div className="flex flex-row h-6 gap-1">
|
||||
<BsmCheckbox className="relative h-full aspect-square" checked={curated} onChange={setCurated}/>
|
||||
<span>Curated</span>
|
||||
<span>{t("playlist.curated")}</span>
|
||||
</div>
|
||||
<div className="flex flex-row h-6 gap-1">
|
||||
<BsmCheckbox className="relative h-full aspect-square" checked={verified} onChange={setVerified}/>
|
||||
<span>Verified mapper</span>
|
||||
<span>{t("playlist.verified-mapper")}</span>
|
||||
</div>
|
||||
<div className="flex flex-row h-6 gap-1">
|
||||
<BsmCheckbox className="relative h-full aspect-square" checked={includeEmpty} onChange={setIncludeEmpty}/>
|
||||
<span>Include empty playlists</span>
|
||||
<span>{t("playlist.empty-playlists")}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-end grow shrink-0 min-w-64 gap-2">
|
||||
@@ -133,7 +131,7 @@ export function DownloadPlaylistFilterPanel({ className, params, onChange, onSub
|
||||
<span className=" text-sm font-bold text-center mt-2.5">{t("maps.map-filter-panel.nps")}</span>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<DateRangePicker label="Start date — End date" value={dateRange} onChange={setDateRange} classNames={{
|
||||
<DateRangePicker label={t("playlist.date-picker.start-date-end-date")} value={dateRange} onChange={setDateRange} classNames={{
|
||||
inputWrapper: "pointer-events-none bg-light-main-color-1 hover:bg-light-main-color-1 dark:bg-main-color-1 dark:hover:bg-main-color-1 hover:brightness-75",
|
||||
input: "pointer-events-auto",
|
||||
selectorButton: "pointer-events-auto",
|
||||
@@ -142,11 +140,11 @@ export function DownloadPlaylistFilterPanel({ className, params, onChange, onSub
|
||||
base: "w-[var(--calendar-width)] pb-2",
|
||||
wrapper: "flex flex-row flex-wrap -my-2.5 py-2.5 px-3 gap-1 overflow-scroll max-w-[var(--calendar-width)] w-[var(--calendar-width)]"
|
||||
}}>
|
||||
<CustomRadio value="days_0">All</CustomRadio>
|
||||
<CustomRadio value="days_1">Last 24h</CustomRadio>
|
||||
<CustomRadio value="weeks_1">Last week</CustomRadio>
|
||||
<CustomRadio value="months_1">Last month</CustomRadio>
|
||||
<CustomRadio value="months_3">Last 3 months</CustomRadio>
|
||||
<CustomRadio value="days_0">{t("playlist.date-picker.all")}</CustomRadio>
|
||||
<CustomRadio value="days_1">{t("playlist.date-picker.last-24h")}</CustomRadio>
|
||||
<CustomRadio value="weeks_1">{t("playlist.date-picker.last-week")}</CustomRadio>
|
||||
<CustomRadio value="months_1">{t("playlist.date-picker.last-month")}</CustomRadio>
|
||||
<CustomRadio value="months_3">{t("playlist.date-picker.3-last-month")}</CustomRadio>
|
||||
</RadioGroup>
|
||||
}/>
|
||||
</div>
|
||||
|
||||
+4
-4
@@ -6,6 +6,7 @@ import { cn } from "renderer/helpers/css-class.helpers";
|
||||
import { useConstant } from "renderer/hooks/use-constant.hook";
|
||||
import { PlaylistSearchParams, BsvSearchOrder } from "shared/models/maps/beat-saver.model";
|
||||
import { DownloadPlaylistFilterPanel, FilterPlaylistSearchParams } from "./download-playlist-filter-panel.component";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
@@ -13,14 +14,13 @@ type Props = {
|
||||
onSubmit?: (value: Omit<PlaylistSearchParams, "page">) => void;
|
||||
}
|
||||
|
||||
// TODO : Translate
|
||||
|
||||
export function DownloadPlaylistModalHeader({ className, value, onSubmit }: Props) {
|
||||
|
||||
const t = useTranslation();
|
||||
const dropDownFilterRef = useRef(null);
|
||||
|
||||
const [query, setQuery] = useState<string>(value?.q || "");
|
||||
const [order, setOrder] = useState<BsvSearchOrder>(value?.sortOrder || BsvSearchOrder.Latest);
|
||||
const [order, setOrder] = useState<BsvSearchOrder>(value?.sortOrder || BsvSearchOrder.Relevance);
|
||||
const [filter, setFilter] = useState<FilterPlaylistSearchParams>({});
|
||||
|
||||
const searchParams: Omit<PlaylistSearchParams, "page"> = { q: query, sortOrder: order };
|
||||
@@ -54,7 +54,7 @@ export function DownloadPlaylistModalHeader({ className, value, onSubmit }: Prop
|
||||
<BsmDropdownButton ref={dropDownFilterRef} buttonClassName="flex items-center justify-center h-full rounded-full px-2 py-1 !bg-light-main-color-1 dark:!bg-main-color-1" icon="filter" text="pages.version-viewer.maps.search-bar.filters-btn" withBar={false}>
|
||||
<DownloadPlaylistFilterPanel className="z-10 translate-y-1" params={filter} onSubmit={handleFilterSubmit}/>
|
||||
</BsmDropdownButton>
|
||||
<input className="h-full bg-theme-1 rounded-full px-2 grow pb-0.5" type="text" placeholder="Rechercher une playlist" value={query} onChange={e => setQuery(e.target.value)} />
|
||||
<input className="h-full bg-theme-1 rounded-full px-2 grow pb-0.5" type="text" placeholder={t("playlist.search-playlist")} value={query} onChange={e => setQuery(e.target.value)} />
|
||||
<BsmButton className="shrink-0 rounded-full py-1 px-3 !bg-theme-1 flex justify-center items-center capitalize" icon="search" text="modals.download-maps.search-btn" withBar={false} onClick={() => submit(searchParams)} />
|
||||
<BsmSelect className="bg-theme-1 rounded-full px-1 pb-0.5 text-center cursor-pointer" options={sortOptions} selected={order} onChange={handleOrderChange}/>
|
||||
</form>
|
||||
|
||||
+6
-5
@@ -19,13 +19,14 @@ import BeatWaiting from "../../../../../../../assets/images/apngs/beat-waiting.p
|
||||
import BeatConflict from "../../../../../../../assets/images/apngs/beat-conflict.png"
|
||||
import { cn } from "renderer/helpers/css-class.helpers"
|
||||
import { VirtualScroll } from "renderer/components/shared/virtual-scroll/virtual-scroll.component"
|
||||
|
||||
// TODO : Translate
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook"
|
||||
|
||||
export const DownloadPlaylistModal: ModalComponent<void, {version: BSVersion, ownedPlaylists$: Observable<LocalBPListsDetails[]>, ownedMaps$: Observable<BsmLocalMap[]>}> = (
|
||||
{ resolver, options: { data: { version, ownedPlaylists$, ownedMaps$ }} }
|
||||
{ options: { data: { version, ownedPlaylists$, ownedMaps$ }} }
|
||||
) => {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
const modal = useService(ModalService);
|
||||
const beatSaver = useService(BeatSaverService);
|
||||
const playlistDownloader = useService(PlaylistDownloaderService);
|
||||
@@ -109,7 +110,7 @@ export const DownloadPlaylistModal: ModalComponent<void, {version: BSVersion, ow
|
||||
<div className="w-full flex flex-col justify-center items-center mt-44">
|
||||
<BsmImage className={cn(["size-32", !error && "spin-loading"])} image={error ? BeatConflict : BeatWaiting} />
|
||||
<p className="text-lg font-bold px-10 text-center">
|
||||
{error ? "Une erreur est survenue lors du chargement des playlists" : "Chargement des playlists..."}
|
||||
{error ? t("playlist.error-occur-while-loading-playlists") : t("playlist.playlists-loading")}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
@@ -119,7 +120,7 @@ export const DownloadPlaylistModal: ModalComponent<void, {version: BSVersion, ow
|
||||
<div className="w-full flex flex-col justify-center items-center mt-44">
|
||||
<BsmImage className="size-32" image={BeatConflict} />
|
||||
<p className="text-lg font-bold px-10 text-center">
|
||||
Aucune playlist trouvée
|
||||
{t("playlist.no-playlists-found")}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
+13
-10
@@ -7,6 +7,7 @@ import { SteamDownloaderService } from "renderer/services/bs-version-download/st
|
||||
import { IpcService } from "renderer/services/ipc.service";
|
||||
import { lastValueFrom } from "rxjs";
|
||||
import { logRenderError } from "renderer";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
type OutProps = {
|
||||
playlistTitle: string;
|
||||
@@ -25,6 +26,8 @@ export const EditPlaylistInfosModal: ModalComponent<OutProps, Props> = ({ resolv
|
||||
isEdit
|
||||
}}}) => {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
const steamDownloader = useService(SteamDownloaderService);
|
||||
const ipc = useService(IpcService);
|
||||
|
||||
@@ -51,34 +54,34 @@ export const EditPlaylistInfosModal: ModalComponent<OutProps, Props> = ({ resolv
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="text-gray-800 dark:text-gray-200">
|
||||
<div className="text-gray-800 dark:text-gray-200 min-w-80">
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center mb-4 px-4">
|
||||
{isEdit ? "Modifier la playlist" : "Créer une playlist"}
|
||||
{isEdit ? t("playlist.edit-playlist") : t("playlist.create-a-playlist")}
|
||||
</h1>
|
||||
<div className="w-full flex flex-col justify-center items-center">
|
||||
<button className="flex justify-center items-center relative size-36 border-2 border-gray-400 bg-theme-1 rounded-md overflow-hidden" onClick={handleClickImage}>
|
||||
{base64 ? (
|
||||
<BsmImage className="absolute size-full cursor-pointer" base64={base64} />
|
||||
) : (
|
||||
<span className="absolute size-full flex justify-center items-center p-2">Choisir une image</span>
|
||||
<span className="absolute size-full flex justify-center items-center p-2">{t("playlist.choose-image")}</span>
|
||||
)}
|
||||
</button>
|
||||
<div className="w-full">
|
||||
<label className="font-bold cursor-pointer tracking-wide" htmlFor="playlist-title">Titre</label>
|
||||
<input id="playlist-title" type="text" className="w-full bg-theme-1 px-1 py-0.5 rounded-md outline-none h-9" value={title} placeholder="Titre de la playlist" onChange={e => setTitle(e.target.value)}/>
|
||||
<label className="font-bold cursor-pointer tracking-wide" htmlFor="playlist-title">{t("playlist.title")}</label>
|
||||
<input id="playlist-title" type="text" className="w-full bg-theme-1 px-1 py-0.5 rounded-md outline-none h-9" value={title} placeholder={t("playlist.playlist-title")} onChange={e => setTitle(e.target.value)}/>
|
||||
</div>
|
||||
<div className="w-full mt-1.5">
|
||||
<label className="font-bold cursor-pointer tracking-wide" htmlFor="playlist-desc">Description</label>
|
||||
<textarea id="playlist-desc" className="w-full bg-theme-1 px-1 py-0.5 rounded-md outline-none max-h-40 min-h-8" value={description} placeholder="Description de la playlist" onChange={e => setDescription(e.target.value)} />
|
||||
<label className="font-bold cursor-pointer tracking-wide" htmlFor="playlist-desc">{t("playlist.description")}</label>
|
||||
<textarea id="playlist-desc" className="w-full bg-theme-1 px-1 py-0.5 rounded-md outline-none max-h-40 min-h-8" value={description} placeholder={t("playlist.playlist-description")} onChange={e => setDescription(e.target.value)} />
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<label className="font-bold cursor-pointer tracking-wide" htmlFor="playlist-author">Auteur</label>
|
||||
<input id="playlist-author" type="text" className="w-full bg-theme-1 px-1 py-0.5 rounded-md outline-none h-9" value={author} placeholder="Auteur de la playlist" onChange={e => setAuthor(e.target.value)}/>
|
||||
<label className="font-bold cursor-pointer tracking-wide" htmlFor="playlist-author">{t("playlist.author")}</label>
|
||||
<input id="playlist-author" type="text" className="w-full bg-theme-1 px-1 py-0.5 rounded-md outline-none h-9" value={author} placeholder={t("playlist.playlist-author")} onChange={e => setAuthor(e.target.value)}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-flow-col grid-cols-2 gap-4 mt-4 h-8">
|
||||
<BsmButton typeColor="cancel" className="rounded-md flex justify-center items-center transition-all h-full" onClick={() => resolver({ exitCode: ModalExitCode.CANCELED })} withBar={false} text="misc.cancel" />
|
||||
<BsmButton typeColor="primary" className="rounded-md flex justify-center items-center transition-all h-full" onClick={submit} withBar={false} text="Enregistrer" />
|
||||
<BsmButton typeColor="primary" className="rounded-md flex justify-center items-center transition-all h-full" onClick={submit} withBar={false} text="playlist.save" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
+12
-13
@@ -39,14 +39,13 @@ import BeatWaiting from "../../../../../../../assets/images/apngs/beat-waiting.p
|
||||
import BeatConflict from "../../../../../../../assets/images/apngs/beat-conflict.png";
|
||||
|
||||
type Props = {
|
||||
version?: BSVersion;
|
||||
maps$: Observable<BsmLocalMap[]>;
|
||||
playlist?: LocalBPList;
|
||||
}
|
||||
|
||||
type PlaylistMap = { map: (BsmLocalMap|SongDetails|BsvMapDetail), difficulties?: BPListDifficulty[] };
|
||||
|
||||
export const EditPlaylistModal: ModalComponent<BPList, Props> = ({ resolver, options: { data: { version, maps$, playlist } } }) => {
|
||||
export const EditPlaylistModal: ModalComponent<BPList, Props> = ({ resolver, options: { data: { maps$, playlist } } }) => {
|
||||
|
||||
const t = useTranslation();
|
||||
const color = useThemeColor("first-color");
|
||||
@@ -460,7 +459,7 @@ export const EditPlaylistModal: ModalComponent<BPList, Props> = ({ resolver, opt
|
||||
</button>
|
||||
{(() => {
|
||||
if(!playlistMaps || !localMaps){
|
||||
return <div className="flex items-center justify-center w-full h-full">Loading...</div>
|
||||
return <div className="flex items-center justify-center w-full h-full">{t("playlist.loading")}</div>
|
||||
}
|
||||
else{
|
||||
return (
|
||||
@@ -468,7 +467,7 @@ export const EditPlaylistModal: ModalComponent<BPList, Props> = ({ resolver, opt
|
||||
<div className="grow flex flex-row min-h-0 gap-2.5">
|
||||
<div className="flex flex-col grow basis-0 min-w-0">
|
||||
<form className="h-8 flex flex-row gap-2 w-full mb-1.5 min-w-0" onSubmit={e => {e.preventDefault(); handleNewSearch()}}>
|
||||
<BsmSelect className="bg-theme-1 h-full rounded-full text-center pb-0.5" options={[{ text: "Installée(s)", value: 0 }, { text: "BeatSaver", value: 1 }]} onChange={setAvailableMapsSource}/>
|
||||
<BsmSelect className="bg-theme-1 h-full rounded-full text-center pb-0.5" options={[{ text: t("playlist.installed"), value: 0 }, { text: "BeatSaver", value: 1 }]} onChange={setAvailableMapsSource}/>
|
||||
<input className="h-full bg-light-main-color-1 dark:bg-main-color-1 rounded-full px-2 grow pb-0.5 min-w-0" type="text" placeholder={t("pages.version-viewer.maps.search-bar.search-placeholder")} value={availableMapsSearch} onChange={e => setAvailableMapsSearch(() => e.target.value)} />
|
||||
{availableMapsSource === 1 && (
|
||||
<BsmButton className="h-full aspect-square z-[1] flex justify-center p-1 rounded-full min-w-0 shrink-0 !bg-light-main-color-1 dark:!bg-main-color-1" icon="search" onClick={handleNewSearch} withBar={false}/>
|
||||
@@ -488,7 +487,7 @@ export const EditPlaylistModal: ModalComponent<BPList, Props> = ({ resolver, opt
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center size-full">
|
||||
<BsmImage className="size-24 spin-loading" image={BeatWaiting}/>
|
||||
<span className="text-sm italic leading-4">Loading...</span>
|
||||
<span className="text-sm italic leading-4">{t("playlist.loading")}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -497,7 +496,7 @@ export const EditPlaylistModal: ModalComponent<BPList, Props> = ({ resolver, opt
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center size-full">
|
||||
<BsmImage className="size-20" image={BeatConflict}/>
|
||||
<span className="text-sm italic leading-4 w-3/4 text-center">Aucune map trouvée</span>
|
||||
<span className="text-sm italic leading-4 w-3/4 text-center">{t("playlist.no-map-found")}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -514,16 +513,16 @@ export const EditPlaylistModal: ModalComponent<BPList, Props> = ({ resolver, opt
|
||||
</div>
|
||||
|
||||
<div className="w-full h-4 flex justify-start">
|
||||
<span className="text-xs italic leading-4">Hold shift or ctrl to select multiples maps</span>
|
||||
<span className="text-xs italic leading-4">{t("playlist.edit-playlist-shortcuts")}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="shrink-0 flex flex-col gap-2.5 pb-4 pt-10">
|
||||
<Tippy content="Ajouter à la playlist" theme="default" placement="left">
|
||||
<Tippy content={t("playlist.add-to-playlist")} theme="default" placement="left">
|
||||
<button className="grow w-9 rounded-md cursor-pointer transition-transform duration-150 hover:brightness-110 active:scale-95" style={{backgroundColor: color, color: getCorrectTextColor(color)}} type="button" onClick={addMapsToPlaylist}>
|
||||
<ChevronTopIcon className="origin-center rotate-90"/>
|
||||
</button>
|
||||
</Tippy>
|
||||
<Tippy content="Enlever de la playlist" theme="default" placement="right">
|
||||
<Tippy content={t("playlist.remove-from-playlist")} theme="default" placement="right">
|
||||
<button className="grow w-9 rounded-md cursor-pointer transition-transform duration-150 hover:brightness-110 active:scale-95" style={{backgroundColor: color, color: getCorrectTextColor(color)}} type="button" onClick={removeMapsFromPlaylist}>
|
||||
<ChevronTopIcon className="origin-center -rotate-90"/>
|
||||
</button>
|
||||
@@ -542,7 +541,7 @@ export const EditPlaylistModal: ModalComponent<BPList, Props> = ({ resolver, opt
|
||||
return(
|
||||
<div className="flex flex-col items-center justify-center size-full">
|
||||
<BsmImage className="size-24 spin-loading" image={BeatWaiting}/>
|
||||
<span className="text-sm italic leading-4">Loading...</span>
|
||||
<span className="text-sm italic leading-4">{t("playlist.loading")}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -550,7 +549,7 @@ export const EditPlaylistModal: ModalComponent<BPList, Props> = ({ resolver, opt
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center size-full">
|
||||
<BsmImage className="size-20" image={BeatConflict}/>
|
||||
<span className="text-sm italic leading-4 w-3/4 text-center">La playlist est vide</span>
|
||||
<span className="text-sm italic leading-4 w-3/4 text-center">{t("playlist.playlist-is-empty")}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -592,8 +591,8 @@ export const EditPlaylistModal: ModalComponent<BPList, Props> = ({ resolver, opt
|
||||
</div>
|
||||
</div>
|
||||
<footer className="flex justify-center items-center gap-2 h-8 mt-2.5">
|
||||
<BsmButton className="rounded-md text-center h-full grow basis-0 flex justify-center items-center" typeColor="cancel" text="Annuler" withBar={false} onClick={() => resolver({ exitCode: ModalExitCode.CANCELED })}/>
|
||||
<BsmButton className="rounded-md text-center h-full grow basis-0 flex justify-center items-center" typeColor="primary" text="Continuer" withBar={false} onClick={handleContinue}/>
|
||||
<BsmButton className="rounded-md text-center h-full grow basis-0 flex justify-center items-center" typeColor="cancel" text="misc.cancel" withBar={false} onClick={() => resolver({ exitCode: ModalExitCode.CANCELED })}/>
|
||||
<BsmButton className="rounded-md text-center h-full grow basis-0 flex justify-center items-center" typeColor="primary" text="playlist.continue" withBar={false} onClick={handleContinue}/>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
|
||||
+7
-9
@@ -8,8 +8,6 @@ import BeatConflict from "../../../../../../assets/images/apngs/beat-conflict.pn
|
||||
import { BPList } from "shared/models/playlists/playlist.interface";
|
||||
import Tippy from "@tippyjs/react";
|
||||
|
||||
// TODO : Translate
|
||||
|
||||
export const ExportPlaylistModal: ModalComponent<boolean, BPList[]> = ({ resolver, options: { data }}) => {
|
||||
|
||||
const t = useTranslation();
|
||||
@@ -20,30 +18,30 @@ export const ExportPlaylistModal: ModalComponent<boolean, BPList[]> = ({ resolve
|
||||
return (
|
||||
<form className="max-w-sm text-gray-800 dark:text-gray-200">
|
||||
{!isMultiple ? (
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">Exporter la playlist ?</h1>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t("playlist.export-playlist-ask")}</h1>
|
||||
) : (
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">Exporter les playlists ?</h1>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t("playlist.export-playlists-ask")}</h1>
|
||||
)}
|
||||
<BsmImage className="mx-auto h-24" image={BeatConflict} />
|
||||
{!isMultiple ? (
|
||||
<p className="w-full">{`Est-tu sûr de vouloir exporter la playlist "${data.at(0)?.playlistTitle}" ?`}</p>
|
||||
<p className="w-full">{t("playlist.export-playlist-desc", { playlistTitle: data.at(0)?.playlistTitle })}</p>
|
||||
) : (
|
||||
<p className="w-full">{`Est-tu sûr de vouloir exporter les ${data.length} playlists ?`}</p>
|
||||
<p className="w-full">{t("playlist.export-playlists-desc", { nb: `${data.length ?? 0}` })}</p>
|
||||
)}
|
||||
|
||||
<div className="flex items-center relative py-2 gap-1">
|
||||
<BsmCheckbox className="h-5 relative z-[1]" checked={exportMaps} onChange={val => setExportMaps(() => val)} />
|
||||
<Tippy
|
||||
placement="top"
|
||||
content={isMultiple ? "Si activé, toutes les maps des playlists seront également exportées" : "Si activé, toutes les maps de la playlist seront également exportées"}
|
||||
content={isMultiple ? t("playlist.export-playlists-maps-tip") : t("playlist.export-playlist-maps-tip")}
|
||||
theme="default"
|
||||
>
|
||||
<span className="italic cursor-help">Exporter les maps</span>
|
||||
<span className="italic cursor-help">{t("playlist.export-maps")}</span>
|
||||
</Tippy>
|
||||
</div>
|
||||
<div className="grid grid-flow-col grid-cols-2 gap-4 mt-2">
|
||||
<BsmButton typeColor="cancel" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.CANCELED })} withBar={false} text="misc.cancel" />
|
||||
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.COMPLETED, data: exportMaps })} withBar={false} text="Exporter" />
|
||||
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.COMPLETED, data: exportMaps })} withBar={false} text="playlist.export" />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
||||
+8
-7
@@ -2,21 +2,22 @@ import { ModalComponent, ModalExitCode } from "renderer/services/modale.service"
|
||||
import BeatConflict from "../../../../../../assets/images/apngs/beat-conflict.png";
|
||||
import { BsmImage } from "renderer/components/shared/bsm-image.component";
|
||||
import { BsmButton } from "renderer/components/shared/bsm-button.component";
|
||||
|
||||
// TODO : Translate
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
export const NeedCloneEditPlaylistModal: ModalComponent<void, void> = ({ resolver }) => {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
return (
|
||||
<form className="text-gray-800 dark:text-gray-200 flex flex-col gap-2 max-w-sm">
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">Attention</h1>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t("playlist.need-clone-title")}</h1>
|
||||
<BsmImage className="mx-auto h-24" image={BeatConflict} />
|
||||
<p className="w-full">Cette playlist a été téléchargée depuis un site externe et contient un lien de synchronisation.</p>
|
||||
<p className="w-full">Pour éviter de perdre vos modifications lors d'une synchronisation, la playlist va être dupliquée et son lien de synchronisation supprimé.</p>
|
||||
<p className="w-full">Vous pourrez ensuite, si vous le souhaitez, supprimer la playlist originale.</p>
|
||||
<p className="w-full">{t("playlist.need-clone-desc-1")}</p>
|
||||
<p className="w-full">{t("playlist.need-clone-desc-2")}</p>
|
||||
<p className="w-full">{t("playlist.need-clone-desc-3")}</p>
|
||||
<div className="grid grid-flow-col grid-cols-2 gap-2 mt-2 h-8">
|
||||
<BsmButton typeColor="cancel" className="rounded-md flex justify-center items-center transition-all h-full" onClick={() => resolver({ exitCode: ModalExitCode.CANCELED })} withBar={false} text="misc.cancel" />
|
||||
<BsmButton typeColor="primary" className="rounded-md flex justify-center items-center transition-all h-full" onClick={() => resolver({ exitCode: ModalExitCode.COMPLETED })} withBar={false} text="J'ai compris" />
|
||||
<BsmButton typeColor="primary" className="rounded-md flex justify-center items-center transition-all h-full" onClick={() => resolver({ exitCode: ModalExitCode.COMPLETED })} withBar={false} text="playlist.understood" />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
||||
+5
-4
@@ -19,6 +19,7 @@ import { BsmImage } from "renderer/components/shared/bsm-image.component";
|
||||
import { cn } from "renderer/helpers/css-class.helpers";
|
||||
import { VirtualScroll } from "renderer/components/shared/virtual-scroll/virtual-scroll.component";
|
||||
import { MapItemComponentPropsMapper } from "shared/mappers/map/map-item-component-props.mapper";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
type Props = {
|
||||
version: BSVersion;
|
||||
@@ -26,10 +27,10 @@ type Props = {
|
||||
installedMaps$: Observable<BsmLocalMap[]>;
|
||||
}
|
||||
|
||||
// TODO : Translate
|
||||
|
||||
export const BsvPlaylistDetailsModal: ModalComponent<void, Props> = ({ resolver, options: { data: { playlist, installedMaps$, version } } }) => {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
const beatsaver = useService(BeatSaverService);
|
||||
const mapsDownloader = useService(MapsDownloaderService);
|
||||
|
||||
@@ -111,7 +112,7 @@ export const BsvPlaylistDetailsModal: ModalComponent<void, Props> = ({ resolver,
|
||||
<div className="w-full flex flex-col justify-center items-center mt-10">
|
||||
<BsmImage className={cn(["size-32", !error && "spin-loading"])} image={error ? BeatConflict : BeatWaiting} />
|
||||
<p className="text-lg font-bold px-10 text-center">
|
||||
{error ? "Une erreur est survenue lors du chargement de la playlist" : "Chargement des maps..."}
|
||||
{error ? t("playlist.error-occur-while-loading-playlist") : t("playlist.loading-maps")}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@@ -122,7 +123,7 @@ export const BsvPlaylistDetailsModal: ModalComponent<void, Props> = ({ resolver,
|
||||
<div className="w-full flex flex-col justify-center items-center mt-10">
|
||||
<BsmImage className="size-32" image={BeatConflict} />
|
||||
<p className="text-lg font-bold px-10 text-center">
|
||||
Aucune map trouvée pour cette playlist
|
||||
{t("playlist.no-maps-found-for-playlist")}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
+7
-6
@@ -16,8 +16,7 @@ import { useConstant } from "renderer/hooks/use-constant.hook";
|
||||
import { useCallback } from "react";
|
||||
import { VirtualScroll } from "renderer/components/shared/virtual-scroll/virtual-scroll.component";
|
||||
import { MapItemComponentPropsMapper } from "shared/mappers/map/map-item-component-props.mapper";
|
||||
|
||||
// TODO : Translate
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
interface Props {
|
||||
version: BSVersion;
|
||||
@@ -27,6 +26,8 @@ interface Props {
|
||||
|
||||
export const LocalPlaylistDetailsModal: ModalComponent<void, Props> = ({resolver, options}) => {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
const playlistDownloader = useService(PlaylistDownloaderService);
|
||||
|
||||
const localPlaylist = useObservable(() => options.data.localPlaylist$, null);
|
||||
@@ -90,7 +91,7 @@ export const LocalPlaylistDetailsModal: ModalComponent<void, Props> = ({resolver
|
||||
<div className="grow flex justify-center items-center flex-col">
|
||||
<BsmImage image={BeatConflict} className="size-28"/>
|
||||
<div className="text-white font-bold w-fit space-y-1.5 flex flex-col justify-center items-center -translate-y-5">
|
||||
<p>La Playlist ne contient aucune map</p>
|
||||
<p>{t("playlist.playlist-contain-no-maps")}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -101,8 +102,8 @@ export const LocalPlaylistDetailsModal: ModalComponent<void, Props> = ({resolver
|
||||
<div className="grow flex justify-center items-center flex-col">
|
||||
<BsmImage image={BeatConflict} className="size-28"/>
|
||||
<div className="text-white font-bold w-fit space-y-1.5 flex flex-col justify-center items-center -translate-y-5">
|
||||
<p>Aucune maps installée pour cette playlist</p>
|
||||
<BsmButton withBar={false} onClick={installPlaylist} className="rounded-md h-8 flex items-center justify-center px-4" typeColor="primary" text="Télécharger.les.maps"/>
|
||||
<p>{t("playlist.no-map-installed-for-playlist")}</p>
|
||||
<BsmButton withBar={false} onClick={installPlaylist} className="rounded-md h-8 flex items-center justify-center px-4" typeColor="primary" text="playlist.download-maps"/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -113,7 +114,7 @@ export const LocalPlaylistDetailsModal: ModalComponent<void, Props> = ({resolver
|
||||
<div className="grow flex justify-center items-center flex-col">
|
||||
<BsmImage image={BeatConflict} className="size-28"/>
|
||||
<div className="text-white font-bold w-fit space-y-1.5 flex flex-col justify-center items-center -translate-y-5">
|
||||
<p>La Playlist est en attente de téléchargment</p>
|
||||
<p>{t("playlist.playlist-is-waiting-to-download")}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
+3
-1
@@ -8,6 +8,7 @@ import { NpsIcon } from "renderer/components/svgs/icons/nps-icon.component";
|
||||
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
|
||||
import { ThemeColorGradientSpliter } from "renderer/components/shared/theme-color-gradient-spliter.component";
|
||||
import { CrossIcon } from "renderer/components/svgs/icons/cross-icon.component";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
export type PlaylistDetailsTemplateProps = {
|
||||
title: string;
|
||||
@@ -26,6 +27,7 @@ export type PlaylistDetailsTemplateProps = {
|
||||
|
||||
export function PlaylistDetailsTemplate({title, imagebase64, imageUrl, author, description, nbMaps, duration, nbMappers, minNps, maxNps, children, onClose}: PlaylistDetailsTemplateProps) {
|
||||
|
||||
const t = useTranslation();
|
||||
const color = useThemeColor("first-color");
|
||||
|
||||
const nbMapsText = nbMaps ? Intl.NumberFormat(undefined, { notation: "compact" }).format(nbMaps).trim() : null;
|
||||
@@ -54,7 +56,7 @@ export function PlaylistDetailsTemplate({title, imagebase64, imageUrl, author, d
|
||||
<div className="h-full px-3 text-white z-[1]">
|
||||
<h1 className="font-bold text-xl tracking-wide line-clamp-1 w-fit">{title}</h1>
|
||||
<p className="line-clamp-2" title={description}>{description}</p>
|
||||
<p className="text-xs font-bold">Créé par <span className="brightness-200" style={{color}}>{author}</span></p>
|
||||
<p className="text-xs font-bold">{t("playlist.created-by")} <span className="brightness-200" style={{color}}>{author}</span></p>
|
||||
|
||||
<div className="flex flex-row flex-wrap w-full gap-2 mt-1">
|
||||
{ nbMapsText && <div className="flex items-center text-sm h-5 gap-0.5"> <MapIcon className='h-full aspect-square'/> <span className="mb-0.5">{nbMapsText}</span> </div> }
|
||||
|
||||
+9
-6
@@ -4,6 +4,7 @@ import { BsmImage } from "renderer/components/shared/bsm-image.component";
|
||||
import BeatConflict from "../../../../../../../assets/images/apngs/beat-conflict.png";
|
||||
import { Observable } from "rxjs";
|
||||
import { useObservable } from "renderer/hooks/use-observable.hook";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
type Props = {
|
||||
isPlaylistInQueue$: Observable<boolean>;
|
||||
@@ -14,6 +15,8 @@ type Props = {
|
||||
|
||||
export function PlaylistHeaderState({isPlaylistInQueue$, isPlaylistDownloading$, isMissingMaps$, installPlaylist}: Props) {
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
const isPlaylistInQueue = useObservable(() => isPlaylistInQueue$, false);
|
||||
const isPlaylistDownloading = useObservable(() => isPlaylistDownloading$, false);
|
||||
const isMissingMaps = useObservable(() => isMissingMaps$, false);
|
||||
@@ -25,10 +28,10 @@ export function PlaylistHeaderState({isPlaylistInQueue$, isPlaylistDownloading$,
|
||||
<>
|
||||
<BsmImage image={BeatConflict} className="size-24"/>
|
||||
<div className="text-white font-bold w-fit space-y-1.5 flex flex-col justify-center items-center">
|
||||
<p>La playlist est en cours de téléchargement</p>
|
||||
<p>{t("playlist.playlist-is-downloading")}</p>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if(isPlaylistInQueue) {
|
||||
@@ -36,18 +39,18 @@ export function PlaylistHeaderState({isPlaylistInQueue$, isPlaylistDownloading$,
|
||||
<>
|
||||
<BsmImage image={BeatConflict} className="size-24"/>
|
||||
<div className="text-white font-bold w-fit space-y-1.5 flex flex-col justify-center items-center">
|
||||
<p>La Playlist est en attente de téléchargment</p>
|
||||
<p>{t("playlist.playlist-is-waiting-to-download")}</p>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<BsmImage image={BeatConflict} className="size-24"/>
|
||||
<div className="text-white font-bold w-fit space-y-1.5 flex flex-col justify-center items-center">
|
||||
<p>Certaines maps de cette playlist sont manquantes</p>
|
||||
<BsmButton withBar={false} onClick={installPlaylist} className="rounded-md h-8 flex items-center justify-center px-4" typeColor="primary" text="Télécharger.les.maps.manquantes"/>
|
||||
<p>{t("playlist.some-playlist-maps-are-missing")}</p>
|
||||
<BsmButton withBar={false} onClick={installPlaylist} className="rounded-md h-8 flex items-center justify-center px-4" typeColor="primary" text="playlist.download-missing-maps"/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
+6
-8
@@ -5,8 +5,6 @@ import { ModalComponent, ModalExitCode } from "renderer/services/modale.service"
|
||||
import BeatConflict from "../../../../../../assets/images/apngs/beat-conflict.png";
|
||||
import { BPList } from "shared/models/playlists/playlist.interface";
|
||||
|
||||
// TODO : Translate
|
||||
|
||||
export const SyncPlaylistModal: ModalComponent<boolean, BPList[]> = ({ resolver, options: { data }}) => {
|
||||
|
||||
const t = useTranslation();
|
||||
@@ -14,23 +12,23 @@ export const SyncPlaylistModal: ModalComponent<boolean, BPList[]> = ({ resolver,
|
||||
return (
|
||||
<form className="max-w-sm text-gray-800 dark:text-gray-200 overflow-hidden">
|
||||
{data.length === 1 ? (
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">Synchroniser la playlist ?</h1>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t("playlist.synchronize-playlist-ask")}</h1>
|
||||
) : (
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">Synchroniser les playlists ?</h1>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t("playlist.synchronize-playlists-ask")}</h1>
|
||||
)}
|
||||
<BsmImage className="mx-auto h-24" image={BeatConflict} />
|
||||
|
||||
{data.length === 1 ? (
|
||||
<p className="w-full">{`Est-tu sûr de vouloir synchroniser la playlist "${data.at(0)?.playlistTitle}" ?`}</p>
|
||||
<p className="w-full">{t("playlist.synchronize-playlist-desc", { playlistTitle: data.at(0)?.playlistTitle })}</p>
|
||||
) : (
|
||||
<p className="w-full">{`Est-tu sûr de vouloir synchroniser les ${data.length} playlists ?`}</p>
|
||||
<p className="w-full">{t("playlist.synchronize-playlists-desc", { nb: `${data.length ?? 0}` })}</p>
|
||||
)}
|
||||
|
||||
<p className="w-full py-2 italic text-sm">Cette action met à jour les playlists et télécharge les maps manquantes; cela peut durer plusieurs minutes.</p>
|
||||
<p className="w-full py-2 italic text-sm">{t("playlist.synchronize-playlist-tip")}</p>
|
||||
|
||||
<div className="grid grid-flow-col grid-cols-2 gap-4 mt-2">
|
||||
<BsmButton typeColor="cancel" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.CANCELED })} withBar={false} text="misc.cancel" />
|
||||
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.COMPLETED })} withBar={false} text="Synchroniser" />
|
||||
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.COMPLETED })} withBar={false} text="playlist.synchronize" />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
||||
@@ -5,27 +5,28 @@ import { BsmImage } from "renderer/components/shared/bsm-image.component";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { ModalComponent, ModalExitCode } from "renderer/services/modale.service";
|
||||
import BeatConflict from "../../../../../assets/images/apngs/beat-conflict.png";
|
||||
import Tippy from "@tippyjs/react";
|
||||
|
||||
export const UnlinkPlaylistModal: ModalComponent<boolean> = ({ resolver }) => {
|
||||
const t = useTranslation();
|
||||
const [keepMaps, setKeepMaps] = useState(true);
|
||||
|
||||
// TODO : Translate
|
||||
|
||||
return (
|
||||
<form className="text-gray-800 dark:text-gray-200">
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">Délier les playlists</h1>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t("playlist.unlink-playlists")}</h1>
|
||||
<BsmImage className="mx-auto h-24" image={BeatConflict} />
|
||||
<p className="max-w-sm w-full">Attention, délier les playlists ne permettra plus l'utilisation des playlists paratagées pour cette version.</p>
|
||||
<p className="max-w-sm w-full">{t("playlist.unlink-playlist-desc")}</p>
|
||||
<div className="relative h-5 flex my-3 items-center">
|
||||
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1.5 z-[1]" checked={keepMaps} onChange={setKeepMaps} />
|
||||
<span className="text-sm mb-0.5 cursor-help" title="Conserver les playlists créera une copie des playlists partagées pour la version actuelle. Dans le cas contraire, aucune playlist ne sera conservée pour cette version.">
|
||||
Conserver les playlists
|
||||
</span>
|
||||
<Tippy content={t("playlist.unlink-keep-playlists-tip")} placement="top" theme="default">
|
||||
<span className="text-sm mb-0.5 cursor-help">
|
||||
{t("playlist.keep-playlists")}
|
||||
</span>
|
||||
</Tippy>
|
||||
</div>
|
||||
<div className="grid grid-flow-col grid-cols-2 gap-4 mt-4">
|
||||
<BsmButton typeColor="cancel" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.CANCELED })} withBar={false} text="misc.cancel" />
|
||||
<BsmButton typeColor="error" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.COMPLETED, data: keepMaps })} withBar={false} text="Délier les playlists" />
|
||||
<BsmButton typeColor="error" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.COMPLETED, data: keepMaps })} withBar={false} text="playlist.unlink-playlists" />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useService } from "./use-service.hook";
|
||||
import { useObservable } from "./use-observable.hook";
|
||||
|
||||
export function useTranslation(): (translationKey: string, args?: Record<string, string>) => string {
|
||||
|
||||
|
||||
const i18nService = useService(I18nService);
|
||||
|
||||
useObservable(() => i18nService.currentLanguage$);
|
||||
@@ -12,7 +12,6 @@ export function useTranslation(): (translationKey: string, args?: Record<string,
|
||||
if (!key) {
|
||||
return key;
|
||||
}
|
||||
const tranlatables = key.split(" ");
|
||||
return tranlatables.map(key => i18nService.translate(key, args)).join(" ");
|
||||
return i18nService.translate(key, args);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user