[feat-560] ask user for installation folder/path

This commit is contained in:
silentrald
2024-08-23 22:09:32 +08:00
parent eba2b6fb03
commit 5e6837b8cd
22 changed files with 314 additions and 43 deletions
@@ -0,0 +1,103 @@
import { lastValueFrom } from "rxjs";
import { useEffect, useState } from "react";
import { useTranslation } from "renderer/hooks/use-translation.hook";
import { useService } from "renderer/hooks/use-service.hook";
import { IpcService } from "renderer/services/ipc.service";
import { ModalComponent, ModalExitCode } from "renderer/services/modale.service";
import { BsmButton } from "renderer/components/shared/bsm-button.component";
export const AskInstallPathModal: ModalComponent<{ installPath: string }, {}> = ({ resolver }) => {
const t = useTranslation();
const ipcService = useService(IpcService);
const [installPath, setInstallPath] = useState("");
const [installFolder, setInstallFolder] = useState("");
const [defaultInstallPath, setDefaultInstallPath] = useState("");
useEffect(() => {
lastValueFrom(ipcService.sendV2("bs-installer.default-install-path"))
.then(defaultPath => {
setInstallPath(defaultPath);
setDefaultInstallPath(defaultPath);
setInstallFolder(window.electron.path.basename(defaultPath));
});
}, []);
const selectInstallPath = async () => {
const response = await lastValueFrom(ipcService.sendV2("choose-folder"));
if (response.canceled || !response.filePaths?.length) {
return;
}
const path = response.filePaths[0];
setInstallPath(
window.electron.path.basename(path) === installFolder ?
path :
window.electron.path.join(response.filePaths[0], installFolder)
);
}
const onDefaultButtonPressed = () => {
setInstallPath(defaultInstallPath);
}
const onConfirmButtonPressed = () => {
resolver({
data: { installPath },
exitCode: ModalExitCode.COMPLETED
});
}
return (
<form
className="static min-w-96"
onSubmit={event => {
event.preventDefault();
onConfirmButtonPressed();
}}>
<h1 className="
tracking-wide w-full
uppercase text-3xl text-center text-gray-800 dark:text-gray-200
">
{t("modals.ask-install-path.title")}
</h1>
<div className="
relative rounded-md pl-2 py-1 my-3
flex items-center justify-between
w-full h-8 bg-light-main-color-1 dark:bg-main-color-1
">
<span className="block text-ellipsis overflow-hidden min-w-0" title={installPath}>
{installPath}
</span>
<BsmButton
onClick={selectInstallPath}
className="shrink-1 whitespace-nowrap mr-2 px-2 font-bold italic text-sm rounded-md"
text={"modals.ask-install-path.choose-folder"}
withBar={false}
/>
</div>
<div className="grid grid-flow-col grid-cols-4 row-start-3 gap-4">
<BsmButton
typeColor="cancel"
className="col-start-3 rounded-md text-center transition-all"
onClick={onDefaultButtonPressed}
withBar={false}
text={"modals.ask-install-path.default"}
/>
<BsmButton
typeColor="primary"
className="col-start-4 z-0 px-1 rounded-md text-center transition-all"
type="submit"
withBar={false}
text={"misc.confirm"}
/>
</div>
</form>
)
}
@@ -17,7 +17,7 @@ export function Modal() {
useEffect(() => {
const onEscape = (e: KeyboardEvent) => {
if (e.key !== "Escape") {
if (currentModal.options.closable === false || e.key !== "Escape") {
return;
}
currentModal.resolver({ exitCode: ModalExitCode.CLOSED });
@@ -34,6 +34,20 @@ export function Modal() {
};
}, [currentModal]);
const renderCloseButton = (modal: ModalObject) => {
return (
<div
className="w-2.5 h-2.5 absolute top-2.5 right-1.5 cursor-pointer"
onClick={e => {
e.stopPropagation();
modal.resolver({ exitCode: ModalExitCode.CLOSED });
}}
>
<BsmIcon className="size-full" icon="cross" />
</div>
)
}
const renderModal = (modal: ModalObject) => {
if (!modal?.modal) { return null; }
@@ -44,23 +58,23 @@ export function Modal() {
return (
<div className="relative p-4 text-gray-800 dark:text-gray-200 rounded-md shadow-lg shadow-black bg-gradient-to-br from-light-main-color-3 to-light-main-color-2 dark:from-main-color-3 dark:to-main-color-2">
<ThemeColorGradientSpliter className="absolute top-0 w-full left-0 h-1 rounded-t-md overflow-hidden"/>
<div
className="w-2.5 h-2.5 absolute top-2.5 right-1.5 cursor-pointer"
onClick={e => {
e.stopPropagation();
modal.resolver({ exitCode: ModalExitCode.CLOSED });
}}
>
<BsmIcon className="size-full" icon="cross" />
</div>
{modal.options?.closable === false ? undefined : renderCloseButton(modal)}
<modal.modal resolver={modal.resolver} options={modal.options} />
</div>
)
}
const onOverlayClicked = () => {
if (currentModal.options.closable === false) {
return;
}
currentModal.resolver({ exitCode: ModalExitCode.NO_CHOICE });
}
return (
<AnimatePresence>
{currentModal ? <motion.span key={crypto.randomUUID()} onClick={() => currentModal.resolver({ exitCode: ModalExitCode.NO_CHOICE })} className="fixed size-full bg-black z-[90]" initial={{ opacity: 0 }} animate={{ opacity: currentModal && 0.6 }} exit={{ opacity: 0 }} transition={{ duration: 0.2 }} /> : undefined}
{currentModal ? <motion.span key={crypto.randomUUID()} onClick={onOverlayClicked} className="fixed size-full bg-black z-[90]" initial={{ opacity: 0 }} animate={{ opacity: currentModal && 0.6 }} exit={{ opacity: 0 }} transition={{ duration: 0.2 }} /> : undefined}
{modals?.map(modal => (
<motion.div key={crypto.randomUUID()} className="fixed z-[90] top-1/2 left-1/2" initial={{ y: "100vh", x: "-50%" }} animate={{y: "-50%", scale: modal === currentModal ? 1 : 0, opacity: modal === currentModal ? 1 : 0, display: modal === currentModal ? "block" : ["block", "none"]}} exit={{ y: "100vh" }}>
{renderModal(modal)}