Skip to content

Commit

Permalink
feat(gui): implement progress-bar
Browse files Browse the repository at this point in the history
  • Loading branch information
SARDONYX-sard committed Nov 3, 2023
1 parent da3c914 commit 1b23e2c
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 44 deletions.
1 change: 1 addition & 0 deletions cspell.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"tauri",
"thiserror",
"Unhide",
"unlisten",
"unoptimized",
"walkdir",
"Warhammer",
Expand Down
123 changes: 100 additions & 23 deletions frontend/src/components/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Checkbox from "@mui/material/Checkbox";
import ClearAllIcon from "@mui/icons-material/ClearAll";
import ConvertButton from "./buttons/convert_btn";
import Grid from "@mui/material/Unstable_Grid2";
import SlideshowIcon from "@mui/icons-material/Slideshow";
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
import toast from "react-hot-toast";
import type { SubmitHandler } from "react-hook-form";
Expand All @@ -23,6 +24,8 @@ import {
} from "@/components/lists/select_log_level";
import { SelectPathButton } from "@/components/buttons/path_selector";
import { UnhideDarBtn } from "@/components/buttons/unhide_dar_btn";
import { listen } from "@tauri-apps/api/event";
import LinearWithValueLabel from "./progress_bar";

type FormProps = {
src: string;
Expand All @@ -35,6 +38,8 @@ type FormProps = {
logLevel: LogLevel;
runParallel: boolean;
hideDar: boolean;
showProgress: boolean;
progress: number;
};

export function ConvertForm() {
Expand All @@ -49,10 +54,12 @@ export function ConvertForm() {
modAuthor: localStorage.getItem("modAuthor") ?? "",
mappingPath: localStorage.getItem("mappingPath") ?? "",
mapping1personPath: localStorage.getItem("mapping1personPath") ?? "",
runParallel: localStorage.getItem("runParallel") === "true",
hideDar: localStorage.getItem("hideDar") === "true",
loading: false as boolean,
logLevel: selectLogLevel(localStorage.getItem("logLevel") ?? "error"),
runParallel: localStorage.getItem("runParallel") === "true",
hideDar: localStorage.getItem("hideDar") === "true",
showProgress: localStorage.getItem("showProgress") === "true",
progress: 0,
} satisfies FormProps,
});

Expand Down Expand Up @@ -85,10 +92,25 @@ export function ConvertForm() {
mapping1personPath,
runParallel,
hideDar,
showProgress,
}) => {
setLoading(true);

let unlisten: (() => void) | null = null;
try {
setValue("progress", 0);
let maxNum = 0;
let prog = 0;

unlisten = await listen<{ index: number }>("show-progress", (event) => {
if (maxNum === 0) {
maxNum = event.payload.index;
} else {
prog = event.payload.index;
}
setValue("progress", (prog * 100) / maxNum);
});

const completeInfo = await convertDar2oar({
src,
dist,
Expand All @@ -98,11 +120,16 @@ export function ConvertForm() {
mapping1personPath,
runParallel,
hideDar,
showProgress,
});
toast.success(completeInfo);
setValue("progress", 100);
} catch (err) {
toast.error(`${err}`);
} finally {
if (unlisten) {
unlisten();
}
setLoading(false);
}
};
Expand Down Expand Up @@ -349,33 +376,37 @@ export function ConvertForm() {
<Grid container spacing={2}>
<Grid xs={3}>
<Controller
name="runParallel"
name="showProgress"
control={control}
render={({ field: { value } }) => (
<Tooltip
title={
<p>
Use multi-threading.
<br />
In most cases, it slows down by tens of ms, but may be
effective when there is more weight on CPU processing with
fewer files to copy and more logic parsing of
&quot;_condition.txt&quot;
</p>
<>
<p>Display detail progress</p>
<p>
However, conversion may be delayed by 5~10 seconds or
more.
</p>
</>
}
>
<FormControlLabel
control={
<Checkbox
onClick={() => {
localStorage.setItem("runParallel", `${!value}`);
setValue("runParallel", !value);
setValue("showProgress", !value);
localStorage.setItem("showProgress", `${!value}`);
}}
checked={value}
aria-label="Run Parallel"
aria-label="Show Progress"
/>
}
label="Run Parallel"
label={
<Box component="div" sx={{ display: "flex" }}>
<SlideshowIcon />
ProgressBar
</Box>
}
/>
</Tooltip>
)}
Expand Down Expand Up @@ -403,6 +434,7 @@ export function ConvertForm() {
<Checkbox
onClick={() => {
localStorage.setItem("hideDar", `${!value}`);
setValue("hideDar", !value);
}}
checked={value}
aria-label="Hide DAR"
Expand Down Expand Up @@ -431,14 +463,51 @@ export function ConvertForm() {
</Grid>
</Grid>

<Grid xs={3}>
<Controller
name="logLevel"
control={control}
render={({ field: { value } }) => (
<SelectLogLevel value={value} {...register("logLevel")} />
)}
/>
<Grid container spacing={2}>
<Grid xs={3}>
<Controller
name="runParallel"
control={control}
render={({ field: { value } }) => (
<Tooltip
title={
<p>
Use multi-threading.
<br />
In most cases, it slows down by tens of ms, but may be
effective when there is more weight on CPU processing with
fewer files to copy and more logic parsing of
&quot;_condition.txt&quot;
</p>
}
>
<FormControlLabel
control={
<Checkbox
onClick={() => {
localStorage.setItem("runParallel", `${!value}`);
setValue("runParallel", !value);
}}
checked={value}
aria-label="Run Parallel"
/>
}
label="Run Parallel"
/>
</Tooltip>
)}
/>
</Grid>

<Grid xs={3}>
<Controller
name="logLevel"
control={control}
render={({ field: { value } }) => (
<SelectLogLevel value={value} {...register("logLevel")} />
)}
/>
</Grid>
</Grid>

<Controller
Expand All @@ -450,6 +519,14 @@ export function ConvertForm() {
</Box>
)}
/>

<Controller
name="progress"
control={control}
render={({ field: { value } }) => (
<LinearWithValueLabel progress={value} />
)}
/>
</FormGroup>
</Grid>
);
Expand Down
35 changes: 35 additions & 0 deletions frontend/src/components/progress_bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as React from "react";
import LinearProgress, {
LinearProgressProps,
} from "@mui/material/LinearProgress";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";

function LinearProgressWithLabel(
props: LinearProgressProps & { value: number },
) {
return (
<Box sx={{ display: "flex", alignItems: "center" }}>
<Box sx={{ width: "100%", mr: 1 }}>
<LinearProgress variant="determinate" {...props} />
</Box>
<Box sx={{ minWidth: 35 }}>
<Typography variant="body2" color="text.secondary">{`${Math.round(
props.value,
)}%`}</Typography>
</Box>
</Box>
);
}

type Props = {
progress: number;
};

export default function LinearWithValueLabel({ progress }: Readonly<Props>) {
return (
<Box sx={{ width: "100%" }}>
<LinearProgressWithLabel value={progress} />
</Box>
);
}
43 changes: 23 additions & 20 deletions frontend/src/tauri_cmd/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { invoke } from "@tauri-apps/api";
import { open } from "@tauri-apps/api/dialog";
import { appLogDir } from "@tauri-apps/api/path";
import { open as openShell } from "@tauri-apps/api/shell";
import { selectLogLevel } from "@/components/lists/select_log_level";

export type LogLevel = "trace" | "debug" | "info" | "warn" | "error";

Expand All @@ -14,35 +15,37 @@ type ConverterArgs = {
mapping1personPath?: string;
runParallel?: boolean;
hideDar?: boolean;
showProgress?: boolean;
};

/**
* Convert DAR to OAR
* # Throw Error
*/
export async function convertDar2oar(props: ConverterArgs): Promise<string> {
const darDir = props.src === "" ? undefined : props.src;
const oarDir = props.dist === "" ? undefined : props.dist;
const modName = props.modName === "" ? undefined : props.modName;
const modAuthor = props.modAuthor === "" ? undefined : props.modAuthor;
const mapping1personPath =
props.mapping1personPath === "" ? undefined : props.mapping1personPath;
const mappingPath = props.mappingPath === "" ? undefined : props.mappingPath;
const runParallel = props.runParallel ?? false;
const hideDar = props.hideDar ?? false;

return invoke<string>("convert_dar2oar", {
const args = {
options: {
darDir,
oarDir,
modName,
modAuthor,
mappingPath,
mapping1personPath,
runParallel,
hideDar,
darDir: props.src === "" ? undefined : props.src,
oarDir: props.dist === "" ? undefined : props.dist,
modName: props.modName === "" ? undefined : props.modName,
modAuthor: props.modAuthor === "" ? undefined : props.modAuthor,
mappingPath: props.mappingPath === "" ? undefined : props.mappingPath,
mapping1personPath:
props.mapping1personPath === "" ? undefined : props.mapping1personPath,
runParallel: props.runParallel ?? false,
hideDar: props.hideDar ?? false,
},
});
};

let logLevel = selectLogLevel(localStorage.getItem("logLevel") ?? "");
changeLogLevel(logLevel);

const showProgress = props.showProgress ?? false;
if (showProgress) {
return invoke<string>("convert_dar2oar_with_progress", args);
} else {
return invoke<string>("convert_dar2oar", args);
}
}

export async function changeLogLevel(logLevel?: LogLevel): Promise<void> {
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/utils/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ label.Mui-focused {
.MuiLoadingButton-root:hover {
background-color: var(--hover-convert-btn-color);
}
.MuiLinearProgress-bar {
background-color: var(--theme-color);
}`;
}

Expand Down
34 changes: 34 additions & 0 deletions src-tauri/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use dar2oar_core::{
convert_dar_to_oar,
fs::{async_closure::AsyncClosure, parallel, remove_oar, unhide_dar, ConvertOptions},
};
use tauri::Window;

/// early return with Err() and write log error.
macro_rules! bail {
Expand Down Expand Up @@ -31,6 +32,39 @@ pub(crate) async fn convert_dar2oar(options: GuiConverterOptions<'_>) -> Result<
}
}

#[tauri::command]
pub(crate) async fn convert_dar2oar_with_progress(
window: Window,
options: GuiConverterOptions<'_>,
) -> Result<String, String> {
tracing::debug!("options: {:?}", &options);
let run_parallel = options.run_parallel.unwrap_or_default();

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct Payload {
index: usize,
}
let sender = move |index: usize| {
if let Err(err) = window.emit("show-progress", Payload { index }) {
tracing::error!("{}", err);
};
async move {}
};

let config = ConvertOptions::async_from(options).await;
let res = match run_parallel {
true => parallel::convert_dar_to_oar(config, sender).await,
false => convert_dar_to_oar(config, sender).await,
};
match res {
Ok(complete_msg) => {
tracing::info!("{}", complete_msg);
Ok(complete_msg.to_string())
}
Err(err) => bail!(err),
}
}

#[tauri::command]
pub(crate) async fn change_log_level(log_level: Option<&str>) -> Result<(), String> {
tracing::debug!("Selected log level: {:?}", log_level);
Expand Down
Loading

0 comments on commit 1b23e2c

Please sign in to comment.