mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
[feature-136] add translations
This commit is contained in:
@@ -2,6 +2,7 @@ import { motion } from "framer-motion"
|
||||
import { forwardRef } from "react"
|
||||
import { useThemeColor } from "renderer/hooks/use-theme-color.hook"
|
||||
import { BsmIcon } from "../svgs/bsm-icon.component"
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook"
|
||||
|
||||
type Props = {
|
||||
className?: string,
|
||||
@@ -13,6 +14,7 @@ type Props = {
|
||||
|
||||
export const LinkButton = motion(forwardRef((props: Props, ref) => {
|
||||
|
||||
const t = useTranslation()
|
||||
const color = useThemeColor("first-color");
|
||||
|
||||
const linkedColor = (() => {
|
||||
@@ -22,7 +24,7 @@ export const LinkButton = motion(forwardRef((props: Props, ref) => {
|
||||
})()
|
||||
|
||||
return (
|
||||
<div ref={ref} className={props.className} title={props.title} onClick={e => {e.preventDefault(); !props.disabled && props.onClick?.()}} style={{pointerEvents: props.disabled ? "none" : "auto"}}>
|
||||
<div ref={ref} className={props.className} title={props.title ? t(props.title) : undefined} onClick={e => {e.preventDefault(); !props.disabled && props.onClick?.()}} style={{pointerEvents: props.disabled ? "none" : "auto"}}>
|
||||
<span className="absolute top-0 left-0 h-full w-full rounded-full brightness-50 opacity-75 dark:opacity-20 dark:filter-none" style={{backgroundColor: linkedColor}}/>
|
||||
<BsmIcon className="p-1 absolute top-0 left-0 h-full w-full !bg-transparent -rotate-45 brightness-150" icon={props.linked ? "link" : "unlink"} style={{color: linkedColor}} />
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -120,7 +120,7 @@ export function MapsPlaylistsPanel({version}: Props) {
|
||||
initial={{rotate: 0}}
|
||||
className="block p-0.5 h-[calc(100%-5px)] aspect-square blur-0 hover:brightness-75"
|
||||
linked={mapsLinked}
|
||||
title={t(mapsLinked ? "pages.version-viewer.maps.tabs.maps.actions.link-maps.tooltips.unlink" : "pages.version-viewer.maps.tabs.maps.actions.link-maps.tooltips.link")}
|
||||
title={mapsLinked ? "pages.version-viewer.maps.tabs.maps.actions.link-maps.tooltips.unlink" : "pages.version-viewer.maps.tabs.maps.actions.link-maps.tooltips.link"}
|
||||
onClick={() => onClickLink(index)}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -23,13 +23,16 @@ export const DeleteModelsModal: ModalComponent<void, {models: BsmLocalModel[], l
|
||||
|
||||
const isMultiple = data.models.length > 1;
|
||||
|
||||
const title = useConstant(() => isMultiple ? t("models.modals.delete-models.title") : t("models.modals.delete-model.title"));
|
||||
const desc = useConstant(() => isMultiple ? t("models.modals.delete-models.desc", {nb: `${data.models.length}`}) : t("models.modals.delete-model.desc", {modelName: data.models[0].model?.name ?? data.models[0].fileName}));
|
||||
const linkedAnnotation = useConstant(() => data.linked ? (isMultiple ? t("models.modals.delete-models.linked-annotation") : t("models.modals.delete-model.linked-annotation")) : undefined);
|
||||
|
||||
return (
|
||||
// TODO TRANSLATIONS
|
||||
<form className="text-gray-800 dark:text-gray-200">
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{isMultiple ? "Supprimer les modèles" : "Supprimer le modèle"}</h1>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{title}</h1>
|
||||
<BsmImage className="mx-auto h-24" image={BeatConflict}/>
|
||||
<p className="max-w-sm w-full">{isMultiple ? `Es-tu sur de vouloir supprimer les ${data.models.length} modèles ?` : `Es-tu sur de vouloir supprimer le modèle ${data.models[0].model?.name ?? data.models[0].fileName}`}</p>
|
||||
{data.linked && <p className="text-sm italic mt-2 cursor-help w-fit" title="TODO TITLE">{isMultiple ? "Ces modèles seront égalements supprimées des versions utilisant les maps partagées" : "Ce modèle sera également supprimée des versions utilisant les maps partagées"}</p>}
|
||||
<p className="max-w-sm w-full">{desc}</p>
|
||||
{data.linked && <p className="text-sm italic mt-2 w-fit">{linkedAnnotation}</p>}
|
||||
{!isMultiple && (
|
||||
<div className="flex items-center relative py-2 gap-1 mt-1">
|
||||
<BsmCheckbox className="h-5 relative z-[1]" checked={remember} onChange={(val) => setRemember(val)}/>
|
||||
|
||||
+27
-19
@@ -29,17 +29,18 @@ export const DownloadModelsModal: ModalComponent<void, {version: BSVersion, type
|
||||
const modelSaber = useService(ModelSaberService);
|
||||
const os = useService(OsDiagnosticService);
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
const modelTypesOptions = useConstant<BsmSelectOption<MSModelType>[]>(() => MODEL_TYPES.map(type => ({
|
||||
text: type.toString(), // TODO TRANSLATE
|
||||
text: `models.types.plural.${type}`,
|
||||
value: type
|
||||
})));
|
||||
|
||||
const querySortsOptions = useConstant<BsmSelectOption<MSGetSort>[]>(() => MS_GET_QUERY_SORTS.map(sort => ({
|
||||
text: sort.toString(), // TODO TRANSLATE
|
||||
text: `models.sorts.${sort}`,
|
||||
value: sort
|
||||
})));
|
||||
|
||||
const t = useTranslation();
|
||||
const currentDownload = useObservable(modelsDownloader.currentDownload$(), null);
|
||||
const downloadQueue = useObservable(modelsDownloader.getQueue$(), []);
|
||||
const [msModels, msModels$] = useBehaviorSubject<MSModel[]>([]);
|
||||
@@ -127,43 +128,50 @@ export const DownloadModelsModal: ModalComponent<void, {version: BSVersion, type
|
||||
return !!ownedModels.some(owned => owned.hash === model.hash);
|
||||
}
|
||||
|
||||
const filterTipsHTML = useConstant(() => (
|
||||
const renderFilterTips = useConstant(() => (
|
||||
<div className="w-fit flex">
|
||||
<table className="w-fit whitespace-nowrap grow shrink-0 text-sm">
|
||||
<tbody>
|
||||
<tr className="font-bold"><td className="mr-3 block">Tag</td><td>Description</td></tr>
|
||||
<tr><td className="mr-3 block">author:</td><td>Only show models by the specified author.</td></tr>
|
||||
<tr><td className="mr-3 block">hash:</td><td>Only show models with the specified hash.</td></tr>
|
||||
<tr><td className="mr-3 block">tag:</td><td>Only show models with the specified tag.</td></tr>
|
||||
<tr><td className="mr-3 block">name:</td><td>Only show models with the specified name.</td></tr>
|
||||
<tr><td className="mr-3 block">discordid:</td><td>Only show models by the user with the specified discordid.</td></tr>
|
||||
<tr><td className="mr-3 block">status:</td><td>Only show models with the specified approval status. (profile only, and only for the author)</td></tr>
|
||||
<tr className="font-bold"><td className="mr-3 block">{t("models.modals.download-models.search-tips.header.tag")}</td><td>{t("models.modals.download-models.search-tips.header.desc")}</td></tr>
|
||||
<tr><td className="mr-3 block">author:</td><td>{t("models.modals.download-models.search-tips.author-desc")}</td></tr>
|
||||
<tr><td className="mr-3 block">tag:</td><td>{t("models.modals.download-models.search-tips.tag-desc")}</td></tr>
|
||||
<tr><td className="mr-3 block">hash:</td><td>{t("models.modals.download-models.search-tips.hash-desc")}</td></tr>
|
||||
<tr><td className="mr-3 block">name:</td><td>{t("models.modals.download-models.search-tips.name-desc")}</td></tr>
|
||||
<tr><td className="mr-3 block">discordid:</td><td>{t("models.modals.download-models.search-tips.discordid-desc")}</td></tr>
|
||||
<tr><td className="mr-3 block">status:</td><td>{t("models.modals.download-models.search-tips.status-desc")}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
))
|
||||
));
|
||||
|
||||
const renderLoadingStatus = () => {
|
||||
if(isLoading) { return <span className="text-lg">{t("models.panel.grid.loading")}</span> }
|
||||
if(isOnline) { return <span className="text-lg">{t("models.modals.download-models.no-models")}</span> }
|
||||
if(!isOnline) { return <span className="text-lg">{t("models.modals.download-models.no-internet")}</span> }
|
||||
return <span className="text-lg">{t("models.modals.download-models.error-occured")}</span>
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="text-gray-800 dark:text-gray-200 flex flex-col max-w-[95vw] w-[970px] h-[85vh] gap-3" onSubmit={e => {e.preventDefault(); search()}}>
|
||||
<div className="flex h-9 gap-2 shrink-0">
|
||||
<BsmSelect className="bg-light-main-color-1 dark:bg-main-color-1 rounded-full px-1 pb-0.5 text-center" options={modelTypesOptions} selected={currentType} onChange={(value) => currentType$.next(value)}/>
|
||||
<BsmSelect className="bg-light-main-color-1 dark:bg-main-color-1 rounded-full px-1 pb-0.5 text-center capitalize" options={modelTypesOptions} selected={currentType} onChange={(value) => currentType$.next(value)}/>
|
||||
<div className="h-ful grow relative flex justify-center items-center">
|
||||
<input className="h-full w-full bg-light-main-color-1 dark:bg-main-color-1 rounded-full px-2 pb-0.5" type="text" name="" id="" placeholder="TODO TRANSLATE Rechercher un modèle" value={searhInput} onChange={e => searhInput$.next(e.target.value)}/>
|
||||
<Tippy placement="bottom" content={filterTipsHTML} allowHTML={true} maxWidth={Infinity}>
|
||||
<input className="h-full w-full bg-light-main-color-1 dark:bg-main-color-1 rounded-full px-2 pb-0.5" type="text" name="" id="" placeholder={t("models.modals.download-models.search-placeholder")} value={searhInput} onChange={e => searhInput$.next(e.target.value)}/>
|
||||
<Tippy placement="bottom" content={renderFilterTips} allowHTML={true} maxWidth={Infinity}>
|
||||
<div className="absolute right-0 h-full w-fit p-1 cursor-pointer">
|
||||
<BsmButton className="h-full rounded-full p-1 aspect-square" typeColor="primary" icon="info" withBar={false}/>
|
||||
</div>
|
||||
</Tippy>
|
||||
|
||||
</div>
|
||||
<BsmButton className="shrink-0 rounded-full py-1 px-3 !bg-light-main-color-1 dark:!bg-main-color-1 flex justify-center items-center capitalize" icon="search" type="submit" text="modals.download-maps.search-btn" withBar={false} onClick={e => {e.preventDefault(); search()}}/>
|
||||
<BsmSelect className="bg-light-main-color-1 dark:bg-main-color-1 rounded-full px-1 pb-0.5 text-center" options={querySortsOptions} onChange={(value) => currentSort$.next(value)}/>
|
||||
<BsmButton className="shrink-0 rounded-full py-1 px-3 !bg-light-main-color-1 dark:!bg-main-color-1 flex justify-center items-center capitalize" icon="search" type="submit" text="models.modals.download-models.search-btn" withBar={false} onClick={e => {e.preventDefault(); search()}}/>
|
||||
<BsmSelect className="bg-light-main-color-1 dark:bg-main-color-1 rounded-full px-1 pb-0.5 text-center capitalize" options={querySortsOptions} onChange={(value) => currentSort$.next(value)}/>
|
||||
</div>
|
||||
<ul className="w-full grow flex content-start flex-wrap gap-4 pt-1.5 px-2 overflow-y-scroll overflow-x-hidden scrollbar-thin scrollbar-thumb-rounded-full scrollbar-thumb-neutral-900 z-0" >
|
||||
{msModels.length === 0 ? (
|
||||
<div className="w-full h-full flex flex-col items-center justify-center">
|
||||
<img className={`w-32 h-32 ${isLoading && "spin-loading"}`} src={isLoading ? BeatWaitingImg : BeatConflictImg} alt=" "/>
|
||||
<span className="text-lg">{t(isLoading ? "Chargement des modèles" : isOnline ? "Aucun modèles trouvés" : "Pas d'internet")}</span> {/** TODO TRANSLATE **/}
|
||||
<img className={`w-32 h-32 ${isLoading && "spin-loading"}`} src={(isLoading && !error) ? BeatWaitingImg : BeatConflictImg} alt=" "/>
|
||||
{renderLoadingStatus()}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
|
||||
@@ -15,17 +15,17 @@ export const LinkModelsModal: ModalComponent<boolean, MSModelType> = ({resolver,
|
||||
|
||||
return (
|
||||
<form className="text-gray-800 dark:text-gray-200">
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t(`modals.models.link-models.${data}.title`)}</h1>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t(`models.modals.link-models.${data}.title`)}</h1>
|
||||
<BsmImage className="mx-auto h-24" image={BeatRunning} />
|
||||
<p className="max-w-sm w-full">{t(`modals.models.link-models.${data}.desc`)}</p>
|
||||
<p className="max-w-sm w-full text-sm italic mt-2">{t(`modals.models.link-models.${data}.info`)}</p>
|
||||
<p className="max-w-sm w-full">{t(`models.modals.link-models.${data}.desc`)}</p>
|
||||
<p className="max-w-sm w-full text-sm italic mt-2">{t(`models.modals.link-models.${data}.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={t(`modals.models.link-models.${data}.keep-models.title`)}>{t(`modals.models.link-models.${data}.keep-models.label`)}</span>
|
||||
<span className="text-sm mb-0.5 cursor-help" title={t(`models.modals.link-models.${data}.keep-models.title`)}>{t(`models.modals.link-models.${data}.keep-models.label`)}</span>
|
||||
</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={`modals.models.link-models.${data}.valid-btn`}/>
|
||||
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={() => resolver({exitCode: ModalExitCode.COMPLETED, data: keepMaps})} withBar={false} text={`models.modals.link-models.${data}.valid-btn`}/>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
|
||||
@@ -14,16 +14,16 @@ export const UnlinkModelsModal: ModalComponent<boolean, MSModelType> = ({resolve
|
||||
|
||||
return (
|
||||
<form className="text-gray-800 dark:text-gray-200">
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t(`modals.models.unlink-models.${data}.title`)}</h1>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t(`models.modals.unlink-models.${data}.title`)}</h1>
|
||||
<BsmImage className="mx-auto h-24" image={BeatConflict} />
|
||||
<p className="max-w-sm w-full">{t(`modals.models.unlink-models.${data}.desc`)}</p>
|
||||
<p className="max-w-sm w-full">{t(`models.modals.unlink-models.${data}.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={t(`modals.models.unlink-models.${data}.keep-models.title`)}>{t(`modals.models.unlink-models.${data}.keep-models.label`)}</span>
|
||||
<span className="text-sm mb-0.5 cursor-help" title={t(`models.modals.unlink-models.${data}.keep-models.title`)}>{t(`models.modals.unlink-models.${data}.keep-models.label`)}</span>
|
||||
</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={`modals.models.unlink-models.${data}.valid-btn`}/>
|
||||
<BsmButton typeColor="error" className="rounded-md text-center transition-all" onClick={() => resolver({exitCode: ModalExitCode.COMPLETED, data: keepMaps})} withBar={false} text={`models.modals.unlink-models.${data}.valid-btn`}/>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
|
||||
@@ -15,6 +15,7 @@ import { BsmIconType } from "../svgs/bsm-icon.component";
|
||||
import { BsmButton } from "../shared/bsm-button.component";
|
||||
import { BsmBasicSpinner } from "../shared/bsm-basic-spinner/bsm-basic-spinner.component";
|
||||
import { isValidUrl } from "shared/helpers/url.helpers";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
type Props<T> = {
|
||||
selected?: boolean,
|
||||
@@ -29,6 +30,7 @@ type Props<T> = {
|
||||
|
||||
function modelItem<T = unknown>(props: Props<T>) {
|
||||
|
||||
const t = useTranslation();
|
||||
const color = useThemeColor("first-color");
|
||||
const [hovered, setHovered] = useState(false);
|
||||
const [idContentCopied, setIdContentCopied] = useState<string>(null);
|
||||
@@ -59,19 +61,19 @@ function modelItem<T = unknown>(props: Props<T>) {
|
||||
return [...new Set(props.tags)];
|
||||
})();
|
||||
|
||||
const actionButtons = (): {text: string, icon: BsmIconType, action: () => void, iconColor?: string}[] => {
|
||||
const buttons: {text: string, icon: BsmIconType, action: () => void, iconColor?: string}[] = [];
|
||||
const actionButtons = (): {icon: BsmIconType, action: () => void, iconColor?: string}[] => {
|
||||
const buttons: {icon: BsmIconType, action: () => void, iconColor?: string}[] = [];
|
||||
|
||||
if(props.onDownload && !props.onCancelDownload){
|
||||
buttons.push({ text: "Download", icon: "download", action: () => props.onDownload(props.callbackValue) }); // TODO TRANSLATE
|
||||
buttons.push({ icon: "download", action: () => props.onDownload(props.callbackValue) });
|
||||
}
|
||||
|
||||
if(props.onDelete){
|
||||
buttons.push({ text: "Delete", icon: "trash", action: () => props.onDelete(props.callbackValue) }); // TODO TRANSLATE
|
||||
buttons.push({ icon: "trash", action: () => props.onDelete(props.callbackValue) });
|
||||
}
|
||||
|
||||
if(props.onCancelDownload){
|
||||
buttons.push({ text: "Cancel download", icon: "cross", action: () => props.onCancelDownload(props.callbackValue), iconColor: "red" }); // TODO TRANSLATE
|
||||
buttons.push({ icon: "cross", action: () => props.onCancelDownload(props.callbackValue), iconColor: "red" });
|
||||
}
|
||||
|
||||
return buttons;
|
||||
@@ -86,7 +88,6 @@ function modelItem<T = unknown>(props: Props<T>) {
|
||||
}
|
||||
|
||||
return (
|
||||
// TODO TRANSLATE ALL TEXT
|
||||
<motion.li className={`relative flex-grow min-w-[14rem] h-56 cursor-pointer ${props.className ?? ""}`} onHoverStart={() => setHovered(() => true)} onHoverEnd={() => setHovered(() => false)} onClick={props.onClick}>
|
||||
<GlowEffect visible={props.selected || hovered}/>
|
||||
<div className="absolute top-0 left-0 w-full h-full rounded-lg overflow-hidden blur-none bg-black shadow-sm shadow-black">
|
||||
@@ -108,17 +109,17 @@ function modelItem<T = unknown>(props: Props<T>) {
|
||||
<li key={tag} className="inline-block mr-1 text-xs bg-white text-black px-1 rounded-full p-0.5">{tag}</li>
|
||||
))}
|
||||
</ul>
|
||||
<Tippy placement="top" content={idContentCopied === "hash" ? "Copied!" : "Copy hash"} followCursor="horizontal" plugins={[followCursor]} hideOnClick={false}>
|
||||
<Tippy placement="top" content={idContentCopied === "hash" ? t("misc.copied") : t("misc.copy")} followCursor="horizontal" plugins={[followCursor]} hideOnClick={false}>
|
||||
<span className="mt-0.5 cursor-copy block w-full max-w-fit overflow-hidden whitespace-nowrap text-ellipsis bg-main-color-1 rounded-md p-1 uppercase text-xs" onClick={() => copyContent(`${props.hash}`, 'hash')}>{props.hash}</span>
|
||||
</Tippy>
|
||||
<div className="flex gap-1">
|
||||
{props.id && (
|
||||
<Tippy placement="top" content={idContentCopied === "id" ? "Copied!" : "Copy id"} followCursor="horizontal" plugins={[followCursor]} hideOnClick={false}>
|
||||
<Tippy placement="top" content={idContentCopied === "id" ? t("misc.copied") : t("misc.copy")} followCursor="horizontal" plugins={[followCursor]} hideOnClick={false}>
|
||||
<span className="cursor-copy w-fit shrink-0 max-w-full overflow-hidden whitespace-nowrap text-ellipsis bg-main-color-1 rounded-md p-1 uppercase text-xs" onClick={() => copyContent(`${props.id}`, 'id')}>{props.id}</span>
|
||||
</Tippy>
|
||||
)}
|
||||
{props.path && (
|
||||
<Tippy placement="top" content={idContentCopied === "path" ? "Copied!" : "Copy path"} followCursor="horizontal" plugins={[followCursor]} hideOnClick={false}>
|
||||
<Tippy placement="top" content={idContentCopied === "path" ? t("misc.copied") : t("misc.copy")} followCursor="horizontal" plugins={[followCursor]} hideOnClick={false}>
|
||||
<span className="cursor-copy w-fit max-w-full overflow-hidden whitespace-nowrap text-ellipsis bg-main-color-1 rounded-md p-1 text-xs" onClick={() => copyContent(`${props.path}`, 'path')}>{props.path}</span>
|
||||
</Tippy>
|
||||
)}
|
||||
|
||||
@@ -20,6 +20,7 @@ import { VersionLinkerAction } from "renderer/services/version-folder-linker.ser
|
||||
import { MODEL_TYPE_FOLDERS } from "shared/models/models/constants";
|
||||
import { useService } from "renderer/hooks/use-service.hook";
|
||||
import { ModelsDownloaderService } from "renderer/services/models-management/models-downloader.service";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
type Props = {
|
||||
className?: string,
|
||||
@@ -34,6 +35,8 @@ export const ModelsGrid = forwardRef(({className, version, type, search, active}
|
||||
const modelsManager = useService(ModelsManagerService);
|
||||
const modelsDownloader = useService(ModelsDownloaderService);
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
const ref = useRef();
|
||||
|
||||
const [models, setModelsLoadObservable,, setModels] = useSwitchableObservable<BsmLocalModel[]>();
|
||||
@@ -156,7 +159,7 @@ export const ModelsGrid = forwardRef(({className, version, type, search, active}
|
||||
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="w-32 h-32 spin-loading" image={BeatWaitingImg}/>
|
||||
<span className="font-bold">Chargement des models...</span> {/** TODO TRANSLATE **/}
|
||||
<span className="font-bold">{t("models.panel.grid.loading")}</span>
|
||||
<TextProgressBar value$={progress$}/>
|
||||
</div>
|
||||
)
|
||||
@@ -166,8 +169,8 @@ export const ModelsGrid = forwardRef(({className, version, type, search, active}
|
||||
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">Aucun modelès</span> {/** TODO TRANSLATE **/}
|
||||
<BsmButton className="font-bold rounded-md p-2" text="Télécharger des modèles" typeColor="primary" withBar={false} onClick={e => {e.preventDefault();}}/>
|
||||
<span className="font-bold">{t("models.panel.grid.no-models")}</span>
|
||||
<BsmButton className="font-bold rounded-md p-2" text="models.panel.grid.download-models" typeColor="primary" withBar={false} onClick={e => {e.preventDefault();}}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { ModelsDownloaderService } from "renderer/services/models-management/mod
|
||||
import { useOnUpdate } from "renderer/hooks/use-on-update.hook";
|
||||
import { NotificationService } from "renderer/services/notification.service";
|
||||
import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
|
||||
export function ModelsPanel({version, isActive, goToMods}: {version?: BSVersion, isActive: boolean, goToMods: () => void}) {
|
||||
|
||||
@@ -20,6 +21,8 @@ export function ModelsPanel({version, isActive, goToMods}: {version?: BSVersion,
|
||||
const notification = useService(NotificationService);
|
||||
const config = useService(ConfigurationService);
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
const ref = useRef();
|
||||
|
||||
const modelsGridRefs = [useRef(null), useRef(null), useRef(null), useRef(null)];
|
||||
@@ -31,9 +34,9 @@ export function ModelsPanel({version, isActive, goToMods}: {version?: BSVersion,
|
||||
|
||||
useOnUpdate(() => {
|
||||
if(!isActive){ return; }
|
||||
//if(config.get("prevented-for-mods-models")){ return; }
|
||||
if(config.get("prevented-for-mods-models")){ return; }
|
||||
config.set("prevented-for-mods-models", true);
|
||||
notification.notifyWarning({ title: "Mods needed", desc: "Be sure to have the mods installed to use models in BeatSaber", actions: [{id: "0", title: "Go to mods"}], duration: 9_000 }).then(res => {
|
||||
notification.notifyWarning({ title: "models.notifications.prevent-for-mods.title", desc: "models.notifications.prevent-for-mods.desc", actions: [{id: "0", title: "models.notifications.prevent-for-mods.go-to-mods"}], duration: 9_000 }).then(res => {
|
||||
if(res !== "0"){ return; }
|
||||
goToMods();
|
||||
});
|
||||
@@ -55,8 +58,8 @@ export function ModelsPanel({version, isActive, goToMods}: {version?: BSVersion,
|
||||
}
|
||||
|
||||
const threeDotsItems: DropDownItem[] = [
|
||||
{text: "TODO TRANSLATE EXPORT", onClick: exportModels, icon: "export"},
|
||||
{text: "TODO TRANSLATE DELETE", onClick: deleteModels, icon: "trash"},
|
||||
{text: "models.panel.actions.drop-down.export", onClick: exportModels, icon: "export"},
|
||||
{text: "models.panel.actions.drop-down.delete", onClick: deleteModels, icon: "trash"},
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -64,7 +67,7 @@ export function ModelsPanel({version, isActive, goToMods}: {version?: BSVersion,
|
||||
<div className="w-full shrink-0 flex h-9 justify-center px-40 gap-2 mb-3 text-main-color-1 dark:text-white">
|
||||
<BsmButton className="flex items-center justify-center w-fit rounded-full px-2 py-1 font-bold" icon="add" text="misc.add" typeColor="primary" withBar={false} onClick={e => {e.preventDefault(); openDownloadModal()}}/>
|
||||
<div className="h-full rounded-full bg-light-main-color-2 dark:bg-main-color-2 grow p-[6px]">
|
||||
<input type="text" className="h-full w-full bg-light-main-color-1 dark:bg-main-color-1 rounded-full px-2" placeholder={"TODO TRANSLATE"} onChange={e => setSearch(e.target.value)} tabIndex={-1}/>
|
||||
<input type="text" className="h-full w-full bg-light-main-color-1 dark:bg-main-color-1 rounded-full px-2" placeholder={t("models.panel.actions.search")} onChange={e => setSearch(e.target.value)} tabIndex={-1}/>
|
||||
</div>
|
||||
<BsmDropdownButton items={threeDotsItems} className="h-full flex aspect-square relative rounded-full z-[1] bg-light-main-color-1 dark:bg-main-color-3" buttonClassName="rounded-full h-full w-full p-[6px]" icon="three-dots" withBar={false} menuTranslationY="6px" align="center"/>
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,8 @@ import { BsContentNavBar, BsContentNavBarTab } from "../shared/bs-content-nav-ba
|
||||
import { useConstant } from "renderer/hooks/use-constant.hook"
|
||||
import { BsmIcon } from "../svgs/bsm-icon.component"
|
||||
import { MODEL_TYPES } from "../../../shared/models/models/constants";
|
||||
import { useService } from "renderer/hooks/use-service.hook"
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook"
|
||||
|
||||
type Props = {
|
||||
className?: string,
|
||||
@@ -20,7 +22,7 @@ export function ModelsTabsNavbar({className, version, tabIndex, onTabChange}: Pr
|
||||
|
||||
const tabs = useConstant<BsContentNavBarTab<MSModelType>[]>(() => {
|
||||
return MODEL_TYPES.map(type => ({
|
||||
text: type, // <= TODO: Translate
|
||||
text: type,
|
||||
extra: type
|
||||
}))
|
||||
})
|
||||
@@ -40,7 +42,9 @@ type TabProps = {
|
||||
|
||||
function ModelTab({version, modelType, active, onClick}: TabProps){
|
||||
|
||||
const modelsManager = useConstant(() => ModelsManagerService.getInstance());
|
||||
const modelsManager = useService(ModelsManagerService);
|
||||
|
||||
const t = useTranslation();
|
||||
|
||||
const [modelsAreLinked, setModelsAreLinked] = useState(false);
|
||||
const [linkBtnDisabled, setLinkBtnDisabled] = useState(false);
|
||||
@@ -73,7 +77,7 @@ function ModelTab({version, modelType, active, onClick}: TabProps){
|
||||
<li className={`relative w-full cursor-pointer flex-1 text-center text-lg font-bold flex justify-center items-center content-center px-7 dim-on-hover ${active ? "dim" : ""}`} onClick={onClick}>
|
||||
<div className="flex flex-col gap-0.5 justify-start items-center text-main-color-1 dark:text-gray-200">
|
||||
<BsmIcon icon={modelType} className="w-7 h-7"/>
|
||||
<span className=" font-thin italic text-xs">{modelType}</span>
|
||||
<span className=" font-thin italic text-xs">{t(`models.types.plural.${modelType}`)}</span>
|
||||
</div>
|
||||
<div className="flex items-center absolute top-1.5 left-1.5">
|
||||
{!!version && (
|
||||
@@ -85,7 +89,7 @@ function ModelTab({version, modelType, active, onClick}: TabProps){
|
||||
initial={{rotate: 0}}
|
||||
className="block w-6 h-6 aspect-square blur-0 cursor-pointer hover:brightness-75"
|
||||
linked={modelsAreLinked}
|
||||
title={"aaa"} //TODO ADD TRANSLATION
|
||||
title={modelsAreLinked ? "models.panel.actions.unlink-models" : "models.panel.actions.link-models"}
|
||||
onClick={onClickLink}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -100,7 +100,7 @@ export class ModelsDownloaderService {
|
||||
}
|
||||
|
||||
public openDownloadModelsModal(version: BSVersion, type?: MSModelType, owned?: BsmLocalModel[]): Promise<ModalResponse<void>>{
|
||||
return this.modal.openModal(DownloadModelsModal, {version, type, owned}); // TODO MODELS
|
||||
return this.modal.openModal(DownloadModelsModal, {version, type, owned});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -118,12 +118,11 @@ export class ModelsManagerService {
|
||||
|
||||
this.progressBar.show(exportProgress$, true);
|
||||
|
||||
lastValueFrom(exportProgress$).catch(e => {
|
||||
this.notifications.notifyError({title: "notifications.types.error"});
|
||||
}).then(() => {
|
||||
// TODO TRANSLATE
|
||||
this.notifications.notifySuccess({title: "TODO TRANSLATE SUCCESS", duration: 3000});
|
||||
this.progressBar.complete();
|
||||
lastValueFrom(exportProgress$).then(() => {
|
||||
this.notifications.notifySuccess({title: "models.notifications.export-success.title", duration: 3000});
|
||||
}).catch(e => {
|
||||
this.notifications.notifyError({title: "notifications.types.error", desc: "notifications.common.msg.error-occurred", duration: 3000});
|
||||
}).finally(() => {
|
||||
this.progressBar.hide(true);
|
||||
})
|
||||
|
||||
@@ -158,8 +157,11 @@ export class ModelsManagerService {
|
||||
if(showProgressBar){ this.progressBar.show(progress$); }
|
||||
|
||||
return lastValueFrom(progress$)
|
||||
.catch(() => false)
|
||||
.then(() => true)
|
||||
.catch(() => {
|
||||
this.notifications.notifyError({title: "notifications.types.error", desc: "notifications.common.msg.error-occurred", duration: 3000});
|
||||
return false;
|
||||
})
|
||||
.finally(() => this.progressBar.hide(true));
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user