Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(gui): implement progress-bar #21

Merged
merged 1 commit into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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