Skip to content

Commit 3e09ab2

Browse files
committed
[gephi-lite] error management
2 parents d42a9bd + e97d68b commit 3e09ab2

8 files changed

Lines changed: 83 additions & 33 deletions

File tree

packages/gephi-lite/src/components/modals/open/CloudFileModal.tsx

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
55

66
import { CloudFile } from "../../../core/cloud/types";
77
import { useCloudProvider } from "../../../core/cloud/useCloudProvider";
8+
import { errorToString } from "../../../core/errors";
89
import { useNotifications } from "../../../core/notifications";
910
import { useConnectedUser } from "../../../core/user";
1011
import { displayDateTime } from "../../../utils/date";
@@ -18,13 +19,14 @@ const PAGINATION_SIZE = 12;
1819
interface OpenCloudFileFormProps {
1920
id?: string;
2021
onStatusChange: (status: AsyncStatus) => void;
22+
status: AsyncStatus;
2123
}
2224

23-
export const OpenCloudFileForm: FC<OpenCloudFileFormProps> = ({ id, onStatusChange }) => {
25+
export const OpenCloudFileForm: FC<OpenCloudFileFormProps> = ({ id, onStatusChange, status }) => {
2426
const [user] = useConnectedUser();
2527
const { t } = useTranslation();
2628
const { notify } = useNotifications();
27-
const { loading, error, getFiles, openFile } = useCloudProvider();
29+
const { loading, getFiles, openFile } = useCloudProvider();
2830
// list files retrived from the cloud
2931
const [files, setFiles] = useState<Array<Omit<CloudFile, "format">>>([]);
3032
// the selected file by the user
@@ -58,13 +60,8 @@ export const OpenCloudFileForm: FC<OpenCloudFileFormProps> = ({ id, onStatusChan
5860
message: t("graph.open.github.success", { filename: selected.filename }).toString(),
5961
});
6062
} catch (e) {
61-
onStatusChange({ type: "error" });
63+
onStatusChange({ type: "error", message: errorToString(e) });
6264
console.error(e);
63-
notify({
64-
type: "error",
65-
message: t("graph.open.github.error"),
66-
title: t("gephi-lite.title"),
67-
});
6865
}
6966
}
7067
},
@@ -81,7 +78,9 @@ export const OpenCloudFileForm: FC<OpenCloudFileFormProps> = ({ id, onStatusChan
8178
onSubmit(selected);
8279
}}
8380
>
84-
{error && <p className="text-center text-danger">{t("graph.open.github.error")}</p>}
81+
{status.type === "error" && (
82+
<p className="text-center text-danger">{status.message || t("graph.open.github.error")}</p>
83+
)}
8584

8685
{files.length > 0 && (
8786
<>

packages/gephi-lite/src/components/modals/open/LocalFileModal.tsx

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { FC, useCallback, useEffect, useState } from "react";
22
import { useTranslation } from "react-i18next";
33
import { PiFolderOpen } from "react-icons/pi";
44

5-
import { useFile, useFileActions } from "../../../core/context/dataContexts";
5+
import { useFileActions } from "../../../core/context/dataContexts";
6+
import { errorToString } from "../../../core/errors";
67
import { ModalProps } from "../../../core/modals/types";
78
import { useNotifications } from "../../../core/notifications";
89
import type { AsyncStatus } from "../../../utils/promises";
@@ -13,17 +14,17 @@ import { Modal } from "../../modals";
1314
interface OpenLocalFileFormProps {
1415
id?: string;
1516
onStatusChange: (status: AsyncStatus) => void;
17+
status: AsyncStatus;
1618
}
17-
export const OpenLocalFileForm: FC<OpenLocalFileFormProps> = ({ id, onStatusChange }) => {
19+
export const OpenLocalFileForm: FC<OpenLocalFileFormProps> = ({ id, onStatusChange, status }) => {
1820
const { t } = useTranslation();
1921
const { open } = useFileActions();
2022
const { notify } = useNotifications();
2123
const [file, setFile] = useState<File | null>(null);
22-
const {
23-
status: { type: importStateType },
24-
} = useFile();
2524

26-
useEffect(() => {}, []);
25+
useEffect(() => {
26+
if (file === null) onStatusChange({ type: "idle" });
27+
}, [file, onStatusChange]);
2728

2829
const onSubmit = useCallback(
2930
async (file: File) => {
@@ -42,13 +43,8 @@ export const OpenLocalFileForm: FC<OpenLocalFileFormProps> = ({ id, onStatusChan
4243
message: t("graph.open.local.success", { filename: file.name }),
4344
});
4445
} catch (e) {
45-
onStatusChange({ type: "error" });
46+
onStatusChange({ type: "error", message: errorToString(e) });
4647
console.error(e);
47-
notify({
48-
type: "error",
49-
message: t("graph.open.local.error"),
50-
title: t("gephi-lite.title"),
51-
});
5248
}
5349
},
5450
[open, notify, t, onStatusChange],
@@ -69,14 +65,16 @@ export const OpenLocalFileForm: FC<OpenLocalFileFormProps> = ({ id, onStatusChan
6965
helpText={t("graph.open.local.dragndrop_text")}
7066
accept={{ "application/graph": [".gexf", ".graphml"], "application/json": [".json"] }}
7167
>
72-
{importStateType === "error" && <p className="text-center text-danger">{t("graph.open.local.error")}</p>}
68+
{status.type === "error" && (
69+
<p className="text-center text-danger">{status.message || t("graph.open.local.error")}</p>
70+
)}
7371
{!file && (
7472
<button className="gl-btn gl-btn-outline mb-2">
7573
<PiFolderOpen /> {t("graph.open.local.button_text")}
7674
</button>
7775
)}
7876
</DropInput>
79-
{importStateType === "loading" && <Loader />}
77+
{status.type === "loading" && <Loader />}
8078
</form>
8179
);
8280
};
@@ -92,7 +90,7 @@ export const OpenLocalFileModal: FC<ModalProps<unknown>> = ({ cancel }) => {
9290

9391
return (
9492
<Modal title={t("graph.open.local.title")}>
95-
<OpenLocalFileForm id={"localFileForm"} onStatusChange={(s) => setStatus(s)} />
93+
<OpenLocalFileForm id={"localFileForm"} onStatusChange={(s) => setStatus(s)} status={status} />
9694
<div className="gl-gap-2 d-flex">
9795
<button title={t("common.cancel")} className="gl-btn gl-btn-outline" onClick={() => cancel()}>
9896
{t("common.cancel")}

packages/gephi-lite/src/components/modals/open/OpenModal.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type OpenCollectionMenuItem = MenuItem<{
1313
component: ComponentType<{
1414
id?: string;
1515
onStatusChange: (status: AsyncStatus) => void;
16+
status: AsyncStatus;
1617
}>;
1718
}>;
1819

@@ -60,7 +61,7 @@ export const OpenModal: FC<ModalProps<{ initialOpenedTab?: string }>> = ({
6061
onSelectedChange={(item) => setSelectedOpen(item)}
6162
/>
6263
<div className="selected-component-wrapper">
63-
<selectedOpen.component id="openForm" onStatusChange={setStatus} />
64+
<selectedOpen.component id="openForm" onStatusChange={setStatus} status={status} />
6465
</div>
6566
</>
6667
<div className="gl-gap-2 d-flex">
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const Errors = {
2+
IMPORT_BAD_VERSION: (args: { version: string }) =>
3+
`Your file is from an older version of gephi-lite (${args.version}) which is not compatible with the actual version`,
4+
IMPORT_BAD_FORMAT: (args: { extension: string; fileName: string }) =>
5+
`Extension ${args.extension} for file ${args.fileName} is not recognized`,
6+
};
7+
8+
export type ErrorCode = keyof typeof Errors;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import i18next from "i18next";
2+
3+
import { type ErrorCode, Errors } from "./codes";
4+
5+
export * from "./codes";
6+
7+
export class GephiLiteError<T extends ErrorCode> extends Error {
8+
code: T;
9+
params: Parameters<(typeof Errors)[T]>[0];
10+
11+
/**
12+
* Gephi Lite Error constructor.
13+
*
14+
* @param code The code of the error
15+
* @param params Additional parameters that will be passed to the I18N function
16+
*/
17+
constructor(code: T, params: Parameters<(typeof Errors)[T]>[0]) {
18+
const formatMessage: (typeof Errors)[T] = Errors[code];
19+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
20+
super(formatMessage(params as unknown as any));
21+
this.name = "GephiLiteError";
22+
this.code = code;
23+
this.params = params;
24+
this.stack = new Error().stack;
25+
}
26+
}
27+
28+
/**
29+
* Given an error, it produces one string.
30+
* Usefull with notification
31+
*/
32+
export function errorToString(error: unknown): string {
33+
let message = "An unknown error occured";
34+
if (error instanceof GephiLiteError) {
35+
// Note: the "as ErrorCode" is for the i18n-checker to discover the i18n keys
36+
message = i18next.t(`error.${error.code as ErrorCode}`, error.params as { [key: string]: unknown });
37+
} else if (error instanceof Error && error.message) {
38+
message = error.message;
39+
}
40+
return message;
41+
}

packages/gephi-lite/src/core/file/utils.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import graphml from "graphology-graphml/browser";
55
import { parse as parseVersion } from "semver";
66

77
import { config } from "../../config";
8+
import { GephiLiteError } from "../errors";
89
import { userAtom } from "../user";
910
import { FileFormat, FileTypeWithoutFormat, GephiLiteFileFormat, fileFormatExt } from "./types";
1011

@@ -91,16 +92,14 @@ export async function extractGraphFromFile(
9192
const jsonContent = gephiLiteParse(fileContent);
9293
if ("type" in jsonContent && jsonContent.type === "gephi-lite") {
9394
const version = parseVersion(jsonContent.version);
94-
console.log("version", version, config.version);
95-
if (version && version.major === config.version.major && version.minor === config.version.minor) {
95+
if (version) {
96+
if (version.major !== config.version.major || version.minor !== config.version.minor) {
97+
throw new GephiLiteError("IMPORT_BAD_VERSION", { version: version?.toString() });
98+
}
9699
return {
97100
format: "gephi-lite",
98101
data: jsonContent,
99102
};
100-
} else {
101-
throw new Error(
102-
`Your file is from an older version of gephi-lite (${version?.toString()}) which is not compatible with the actual version`,
103-
);
104103
}
105104
} else {
106105
return {
@@ -110,7 +109,7 @@ export async function extractGraphFromFile(
110109
}
111110
}
112111
}
113-
throw new Error(`Extension ${extension} for file ${fileName} is not recognized`);
112+
throw new GephiLiteError("IMPORT_BAD_FORMAT", { extension, fileName });
114113
}
115114

116115
/**

packages/gephi-lite/src/locales/dev.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,14 +258,16 @@
258258
"deprecated": {
259259
"gexf_search_params": "The `gexf=` url params to open a graph file has been renamed as `file=` in version 0.4.0. Using gexf params is deprecated and might be removed in future version. Please use `file=` instead."
260260
},
261+
"IMPORT_BAD_FORMAT": "Extension {{extension}} for file {{fileName}} is not recognized",
262+
"IMPORT_BAD_VERSION": "Your file is from an older version of Gephi-Lite ({{version}}) which is not compatible with the actual version",
261263
"form": {
262264
"field_already_exists": "The column with label \"{{label}}\" already uses the id \"{{id}}\".",
263265
"max": "Field {{name}} should be less than or equal to {{max}}",
264266
"min": "Field {{name}} should be greater than or equal to {{min}}",
265267
"required": "Field {{name}} is required",
266268
"unique": "Value must be unique, and a data with the same value already exist"
267269
},
268-
"message": "Sorry, we are working to make gephi-lite better, but bugs still happened...<br />It will help us if you can open an issue on GitHub with a description of what you were doing.",
270+
"message": "Sorry, we are working to make Gephi-Lite better, but bugs still happened...<br />It will help us if you can open an issue on GitHub with a description of what you were doing.",
269271
"not_found": {
270272
"paragraph": "You may have mistyped the address or the page may have moved.",
271273
"subtitle": "The page you were looking for doesn't exist.",

packages/gephi-lite/src/locales/fr.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@
280280
"deprecated": {
281281
"gexf_search_params": "Le paramètre d'URL `gexf=` pour ouvrir un fichier automatiquement a été renommé `file=` dans la version 0.4.0. Utiliser le paramètre gexf est déprécié, et pourrait ne plus fonctionner dans une version future. Veuillez utiliser `file=` à la place."
282282
},
283+
"IMPORT_BAD_FORMAT": "L'extension {{extension}} du fichier {{fileName}} n'est pas reconnue",
284+
"IMPORT_BAD_VERSION": "Votre fichier provient d'une version précédente de Gephi-Lite ({{version}}) qui n'est pas compatible avec la version courante",
283285
"form": {
284286
"field_already_exists": "La colonne avec le label \"{{label}}\" utilise déjà l'identifiant \"{{id}}\".",
285287
"max": "Le champ {{name}} doit être inférieur ou égal à {{max}}",

0 commit comments

Comments
 (0)