Skip to content

Commit 9ebdca6

Browse files
authored
Soft-delete exported directory by move to custom trash bin folder (#130)
Add feature to soft-delete (move) exported comic folder to user defined trash bin path. The trash bin path can be configured in `config.yaml`.
2 parents 4e687aa + 798b3e1 commit 9ebdca6

File tree

25 files changed

+339
-41
lines changed

25 files changed

+339
-41
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ After preview & press button to export, User can defined export folder, and expo
5959
| ![Export UI](screenshots/export.png) | ![Custom Export](screenshots/export_custom.png) |
6060
| ------------------------------------ | ----------------------------------------------- |
6161

62+
#### Soft Delete after export
63+
64+
If user has configured a trash bin (specified for this program), then they can tick "Soft delete after export" option.
65+
66+
This option will move current working comic folder to user defined trash bin when a successful export process completed.
67+
6268
### Quick Export (Komga Only)
6369

6470
Ignore preview section and generate `.cbz` with `komga` folder structure directly.
@@ -102,6 +108,8 @@ You should use absolute paths as possible. If folder is missing, then program wi
102108
| `default` | struct | storing default values for program |
103109
| `default.export-folder` | string | default export folder path, if empty string, then create inside input directory |
104110
| `default.comic-folder` | string | default folder location when choose folder to create comicinfo |
111+
| `trash-bin` | struct | Store trash bin definition for soft-deletion |
112+
| `trash-bin.path` | string | path of program trash bin, if empty string, then no soft-deletion operation |
105113

106114
## Data
107115

config-default.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
default:
22
export-folder:
33
comic-folder:
4+
5+
trash-bin:
6+
path:

config-example.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
default:
22
export-folder: ./my-export
33
comic-folder: ./my-input
4+
5+
trash-bin:
6+
path: "./.trash"

frontend/src/App.tsx

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ function App() {
4444

4545
const [sessionData, setSessionData] = useState<SessionData>({
4646
exportMethod: ExportMethod.DEFAULT_WRAP_CBZ,
47+
deleteAfterExport: false,
4748
});
4849

4950
/** Controller of modal. */
@@ -152,17 +153,6 @@ function App() {
152153
setInfo(temp);
153154
}
154155

155-
/**
156-
* Handle change for export method.
157-
* @param val the new value of export method
158-
*/
159-
function handleExportMethodChange(val: ExportMethod) {
160-
setSessionData({
161-
...sessionData,
162-
exportMethod: val,
163-
});
164-
}
165-
166156
return (
167157
<div id="App" className="container-fluid">
168158
{/* Modal Part */}
@@ -222,7 +212,9 @@ function App() {
222212
originalDirectory={inputDir}
223213
modalControl={modalController}
224214
exportMethod={sessionData.exportMethod}
225-
setExportMethod={handleExportMethodChange}
215+
setExportMethod={(val) => setSessionData({ ...sessionData, exportMethod: val })}
216+
deleteAfterExport={sessionData.deleteAfterExport}
217+
setDeleteAfterExport={(val) => setSessionData({ ...sessionData, deleteAfterExport: val })}
226218
/>
227219
)}
228220
{mode === AppMode.HELP && <HelpPanel backToHome={backToHomePanel} />}

frontend/src/components/ColoredRadio.tsx renamed to frontend/src/components/ColoredFormCheck.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { ChangeEvent } from "react";
22
import { Form } from "react-bootstrap";
3-
import "./ColoredRadio.css";
3+
import "./ColoredFormCheck.css";
44

55
type supportedColors = "dark-red" | "dark-orange" | "dark-yellow" | "dark-green" | "dark-blue";
66

7-
type ColoredRadioProps = {
7+
type ColoredFormCheckProps = {
88
/** HTML element ID. */
99
id?: string;
1010
/** Radio button group usage. */
@@ -21,11 +21,11 @@ type ColoredRadioProps = {
2121
onChange?: (evt: ChangeEvent<HTMLInputElement>) => void;
2222
};
2323

24-
/** Component of a colored radio button. */
25-
export default function ColoredRadio(props: Readonly<ColoredRadioProps>) {
24+
/** Form check element with custom color. */
25+
function ColoredFormCheck(props: Readonly<ColoredFormCheckProps & { type: "radio" | "checkbox" }>) {
2626
return (
2727
<Form.Check
28-
type={"radio"}
28+
type={props.type}
2929
label={props.label}
3030
name={props.name}
3131
id={props.id}
@@ -35,3 +35,13 @@ export default function ColoredRadio(props: Readonly<ColoredRadioProps>) {
3535
/>
3636
);
3737
}
38+
39+
/** Component of a colored radio button. */
40+
export function ColoredRadio(props: Readonly<ColoredFormCheckProps>) {
41+
return <ColoredFormCheck {...props} type="radio" />;
42+
}
43+
44+
/** Component of a colored checkbox. */
45+
export function ColoredCheckBox(props: Readonly<ColoredFormCheckProps>) {
46+
return <ColoredFormCheck {...props} type="checkbox" />;
47+
}

frontend/src/controls/SessionData.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ export enum ExportMethod {
1111
export type SessionData = {
1212
/** Method to export. This will keep save last option until program close. */
1313
exportMethod: ExportMethod;
14+
/** Delete input folder after export */
15+
deleteAfterExport: boolean;
1416
};

frontend/src/pages/exportPanel.tsx

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Button from "react-bootstrap/Button";
88
import Form from "react-bootstrap/Form";
99

1010
// Project Component
11-
import ColoredRadio from "../components/ColoredRadio";
11+
import { ColoredCheckBox, ColoredRadio } from "../components/ColoredFormCheck";
1212
import FolderSelector from "../components/FolderSelector";
1313
import { ModalControl } from "../controls/ModalControl";
1414

@@ -18,6 +18,7 @@ import {
1818
ExportCbzWithDefaultWrap,
1919
ExportCbzWithWrap,
2020
GetDefaultOutputDirectory,
21+
RunSoftDelete,
2122
} from "../../wailsjs/go/application/App";
2223
import { comicinfo } from "../../wailsjs/go/models";
2324
import { ExportMethod } from "../controls/SessionData";
@@ -35,6 +36,10 @@ type ExportProps = {
3536
exportMethod: ExportMethod;
3637
/** Method to set export method as react hook. */
3738
setExportMethod: (val: ExportMethod) => void;
39+
/** Delete after export. */
40+
deleteAfterExport: boolean;
41+
/** Method to set delete after export as react hook. */
42+
setDeleteAfterExport: (val: boolean) => void;
3843
};
3944

4045
/**
@@ -47,6 +52,8 @@ export default function ExportPanel({
4752
modalControl,
4853
exportMethod,
4954
setExportMethod,
55+
deleteAfterExport,
56+
setDeleteAfterExport,
5057
}: Readonly<ExportProps>) {
5158
// Since this is the final step, could ignore the interaction with App.tsx
5259
const [defaultDir, setDefaultDir] = useState<string>("");
@@ -114,12 +121,32 @@ export default function ExportPanel({
114121

115122
// Start running
116123
promise.then((msg) => {
124+
console.log(`cbz return: '${msg}'`);
125+
117126
if (msg !== "") {
118127
modalControl.showErr(msg);
119-
} else {
128+
return;
129+
}
130+
131+
// Early return if no need to do anything
132+
if (!deleteAfterExport) {
120133
modalControl.completeAndReset();
134+
return;
135+
}
136+
137+
// Run soft deletion if necessary
138+
if (deleteAfterExport) {
139+
console.log("Start soft deletion");
140+
141+
RunSoftDelete().then((errMsg) => {
142+
if (errMsg !== "") {
143+
modalControl.showErr(errMsg);
144+
return;
145+
}
146+
147+
modalControl.completeAndReset();
148+
});
121149
}
122-
console.log(`cbz return: '${msg}'`);
123150
});
124151
}
125152

@@ -180,6 +207,18 @@ export default function ExportPanel({
180207
Export
181208
</Button>
182209
</div>
210+
211+
{/* Checkbox to soft deletion */}
212+
<div className="d-flex justify-content-center mt-2">
213+
<ColoredCheckBox
214+
id="soft-deletion"
215+
name="soft-deletion"
216+
color="dark-red"
217+
label="Soft Delete after export"
218+
checked={deleteAfterExport}
219+
onChange={() => setDeleteAfterExport(!deleteAfterExport)}
220+
/>
221+
</div>
183222
</div>
184223
);
185224
}

frontend/wailsjs/go/application/App.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,5 @@ export function GetDirectory(arg1:string):Promise<application.DirectoryResp>;
2828
export function OpenFolder(arg1:string):Promise<void>;
2929

3030
export function QuickExportKomga(arg1:string):Promise<string>;
31+
32+
export function RunSoftDelete():Promise<string>;

frontend/wailsjs/go/application/App.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,7 @@ export function OpenFolder(arg1) {
5353
export function QuickExportKomga(arg1) {
5454
return window['go']['application']['App']['QuickExportKomga'](arg1);
5555
}
56+
57+
export function RunSoftDelete() {
58+
return window['go']['application']['App']['RunSoftDelete']();
59+
}

0 commit comments

Comments
 (0)