Improve toolbox architecture and initial bundle loading
All checks were successful
Deploy Sokko G / deploy (push) Successful in 15s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 15s
This commit is contained in:
parent
f76cddcc95
commit
b221257616
34 changed files with 8358 additions and 6744 deletions
173
website/src/features/toolboxes/storage/moduleDataCompactors.js
Normal file
173
website/src/features/toolboxes/storage/moduleDataCompactors.js
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
// Rôle : fournit les compacteurs de données par type d'outil toolbox.
|
||||
export function createModuleDataCompactors(deps) {
|
||||
return {
|
||||
notepad(value) {
|
||||
const normalized = deps.normalizeNotepadData(value);
|
||||
const compact = {};
|
||||
if (normalized.html && normalized.text) compact.html = normalized.html;
|
||||
if (normalized.text) compact.text = normalized.text;
|
||||
if (normalized.updatedAt) compact.updatedAt = normalized.updatedAt;
|
||||
if (normalized.drawingMode === "permanent") compact.drawingMode = "permanent";
|
||||
if (normalized.drawingMode === "permanent" && normalized.drawings.strokes.length) compact.drawings = normalized.drawings;
|
||||
return Object.keys(compact).length ? compact : null;
|
||||
},
|
||||
checklist(value) {
|
||||
const normalized = deps.normalizeChecklistData(value);
|
||||
const sections = normalized.sections.map(deps.compactChecklistSectionForStorage).filter((section) => section.items.length);
|
||||
if (!sections.length) return null;
|
||||
const settings = {
|
||||
...(normalized.hideCompletedSections ? { hideCompletedSections: true } : {}),
|
||||
...(normalized.hideCompletedSectionsFully ? { hideCompletedSectionsFully: true } : {})
|
||||
};
|
||||
if (sections.length === 1 && !sections[0].title && typeof sections[0].hideWhenComplete !== "boolean") return { ...settings, items: sections[0].items };
|
||||
return { ...settings, sections };
|
||||
},
|
||||
images(value) {
|
||||
const images = (Array.isArray(value?.images) ? value.images : [])
|
||||
.filter((image) => image?.dataUrl)
|
||||
.map((image) => {
|
||||
const compact = { id: image.id || deps.uid("image"), dataUrl: image.dataUrl };
|
||||
if (image.label) compact.label = image.label;
|
||||
return compact;
|
||||
});
|
||||
return images.length ? { images } : null;
|
||||
},
|
||||
imageAnnotation(value) {
|
||||
const normalized = deps.normalizeImageAnnotationData(value);
|
||||
if (!normalized.image) return null;
|
||||
const compact = {
|
||||
image: normalized.image,
|
||||
markers: normalized.markers.map((marker) => {
|
||||
const compact = { id: marker.id, x: marker.x, y: marker.y };
|
||||
if (marker.label) compact.label = marker.label;
|
||||
return compact;
|
||||
})
|
||||
};
|
||||
if (normalized.drawings.strokes.length) compact.drawings = normalized.drawings;
|
||||
return compact;
|
||||
},
|
||||
links(value) {
|
||||
const links = deps.normalizeLinksData(value).links.map((link) => {
|
||||
const compact = { id: link.id, url: link.url };
|
||||
if (link.title) compact.title = link.title;
|
||||
return compact;
|
||||
});
|
||||
return links.length ? { links } : null;
|
||||
},
|
||||
counters(value) {
|
||||
const counters = deps.normalizeCountersData(value).counters.map((counter) => ({
|
||||
id: counter.id,
|
||||
label: counter.label,
|
||||
value: counter.value
|
||||
}));
|
||||
return counters.length ? { counters } : null;
|
||||
},
|
||||
combos(value) {
|
||||
const normalized = deps.normalizeCombosData(value);
|
||||
const compact = {
|
||||
combos: normalized.combos.map((combo) => {
|
||||
const compactCombo = { id: combo.id, name: combo.name, inputs: combo.inputs };
|
||||
if (combo.category) compactCombo.category = combo.category;
|
||||
if (combo.device) compactCombo.device = combo.device;
|
||||
return compactCombo;
|
||||
})
|
||||
};
|
||||
if (normalized.device !== "playstation") compact.device = normalized.device;
|
||||
if (normalized.collapsedCategories.length) compact.collapsedCategories = normalized.collapsedCategories;
|
||||
if (normalized.categoryOrder.length) compact.categoryOrder = normalized.categoryOrder;
|
||||
if (!compact.combos.length) delete compact.combos;
|
||||
return compact.combos ? compact : null;
|
||||
},
|
||||
calculator(value) {
|
||||
const normalized = deps.normalizeCalculatorData(value);
|
||||
const entries = normalized.entries.map((entry) => {
|
||||
const compact = { id: entry.id, label: entry.label, value: entry.value };
|
||||
if (entry.parentId) compact.parentId = entry.parentId;
|
||||
return compact;
|
||||
});
|
||||
if (!entries.length && !normalized.scrollResults) return null;
|
||||
return normalized.scrollResults ? { entries, scrollResults: true } : { entries };
|
||||
},
|
||||
table(value) {
|
||||
const normalized = deps.normalizeTableData(value);
|
||||
const compact = { cells: normalized.cells };
|
||||
if (normalized.rows !== 10) compact.rows = normalized.rows;
|
||||
if (normalized.columns !== 6) compact.columns = normalized.columns;
|
||||
if (Object.keys(normalized.rowLabels).length) compact.rowLabels = normalized.rowLabels;
|
||||
if (Object.keys(normalized.columnLabels).length) compact.columnLabels = normalized.columnLabels;
|
||||
return Object.keys(compact.cells).length || compact.rows || compact.columns || compact.rowLabels || compact.columnLabels ? compact : null;
|
||||
},
|
||||
timer(value) {
|
||||
const normalized = deps.normalizeTimerData(value);
|
||||
const compact = {};
|
||||
if (normalized.activeTab !== "stopwatch") compact.activeTab = normalized.activeTab;
|
||||
if (normalized.scrollResults) compact.scrollResults = true;
|
||||
if (normalized.sortResults) compact.sortResults = true;
|
||||
if (normalized.stopwatch.elapsedMs || normalized.stopwatch.startedAt || normalized.stopwatch.laps.length) {
|
||||
compact.stopwatch = {
|
||||
...(normalized.stopwatch.elapsedMs ? { elapsedMs: normalized.stopwatch.elapsedMs } : {}),
|
||||
...(normalized.stopwatch.startedAt ? { startedAt: normalized.stopwatch.startedAt } : {}),
|
||||
...(normalized.stopwatch.laps.length ? { laps: normalized.stopwatch.laps } : {})
|
||||
};
|
||||
}
|
||||
if (normalized.countdowns.length) {
|
||||
compact.countdowns = normalized.countdowns.map((countdown) => {
|
||||
const { alertMode, autoRefresh, startMode, startTime, ...compactCountdown } = countdown;
|
||||
if (alertMode !== "off") compactCountdown.alertMode = alertMode;
|
||||
if (autoRefresh) compactCountdown.autoRefresh = true;
|
||||
if (startMode === "time" && startTime) {
|
||||
compactCountdown.startMode = startMode;
|
||||
compactCountdown.startTime = startTime;
|
||||
}
|
||||
return compactCountdown;
|
||||
});
|
||||
}
|
||||
return Object.keys(compact).length ? compact : null;
|
||||
},
|
||||
taskPlanner(value) {
|
||||
const normalized = deps.normalizeTaskPlannerData(value);
|
||||
const compact = {};
|
||||
if (normalized.weeklyResetDay !== deps.defaultTaskPlannerWeeklyResetDay) compact.weeklyResetDay = normalized.weeklyResetDay;
|
||||
if (normalized.resetTime !== deps.defaultTaskPlannerResetTime) compact.resetTime = normalized.resetTime;
|
||||
if (normalized.lastResetAt) compact.lastResetAt = normalized.lastResetAt;
|
||||
if (normalized.hideCompleted) compact.hideCompleted = true;
|
||||
if (normalized.collapsedCategories.length) compact.collapsedCategories = normalized.collapsedCategories;
|
||||
if (normalized.tasks.length) {
|
||||
compact.tasks = normalized.tasks.map((task) => {
|
||||
const compactTask = { id: task.id, title: task.title, type: task.type };
|
||||
if (task.description) compactTask.description = task.description;
|
||||
if (task.category) compactTask.category = task.category;
|
||||
if (task.checked) compactTask.checked = true;
|
||||
if (task.checked && task.checkedAt) compactTask.checkedAt = task.checkedAt;
|
||||
if (task.type === "daily" && task.dailyResetTime && task.dailyResetTime !== normalized.resetTime) compactTask.dailyResetTime = task.dailyResetTime;
|
||||
if (task.type === "weekly" && task.weeklyResetDay !== undefined) compactTask.weeklyResetDay = task.weeklyResetDay;
|
||||
return compactTask;
|
||||
});
|
||||
}
|
||||
if (normalized.categoryOrder.length) compact.categoryOrder = normalized.categoryOrder;
|
||||
if (normalized.relations.length) {
|
||||
compact.relations = normalized.relations.map((relation) => {
|
||||
const compactRelation = { id: relation.id, fromTaskId: relation.fromTaskId, toTaskId: relation.toTaskId };
|
||||
if (relation.prerequisite) compactRelation.prerequisite = true;
|
||||
return compactRelation;
|
||||
});
|
||||
}
|
||||
return Object.keys(compact).length ? compact : null;
|
||||
},
|
||||
equipmentPlanner(value) {
|
||||
const normalized = deps.normalizeEquipmentPlannerData(value);
|
||||
if (!normalized.equipments.length) return null;
|
||||
const compact = {
|
||||
types: normalized.types.map((type) => {
|
||||
const compactType = { id: type.id, title: type.title };
|
||||
if (type.icon !== "shield") compactType.icon = type.icon;
|
||||
if (type.collapsed) compactType.collapsed = true;
|
||||
return compactType;
|
||||
}),
|
||||
equipments: normalized.equipments.map(deps.compactEquipmentPlannerEquipmentForStorage)
|
||||
};
|
||||
if (normalized.typeOrder.length) compact.typeOrder = normalized.typeOrder;
|
||||
return compact;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
// Rôle : normalise, compacte et sérialise les données toolbox persistées.
|
||||
import { getDefaultModuleTitle } from "../toolboxModuleRegistry.js";
|
||||
import { createModuleDataCompactors } from "./moduleDataCompactors.js";
|
||||
|
||||
const ID_PREFIXES = {
|
||||
tbx: "t",
|
||||
mod: "m",
|
||||
|
|
@ -71,21 +74,6 @@ const TOOLBOX_ICON_FILES = [
|
|||
"tower.png",
|
||||
"wheel.png"
|
||||
];
|
||||
const DEFAULT_MODULE_TITLES = {
|
||||
notepad: "Bloc notes",
|
||||
checklist: "Checklist",
|
||||
images: "Images",
|
||||
links: "Liens",
|
||||
counters: "Compteurs",
|
||||
combos: "Combos",
|
||||
calculator: "Calculateur",
|
||||
table: "Tableau",
|
||||
timer: "Timer",
|
||||
taskPlanner: "Planificateur de tâches",
|
||||
equipmentPlanner: "Planificateur d'équipements",
|
||||
imageAnnotation: "Annotation d'images"
|
||||
};
|
||||
|
||||
export const TOOLBOX_ICONS = TOOLBOX_ICON_FILES.map((file) => `${TOOLBOX_ICON_BASE}${file}`);
|
||||
export const DEFAULT_TOOLBOX_ICON = `${TOOLBOX_ICON_BASE}toolbox.png`;
|
||||
|
||||
|
|
@ -102,10 +90,6 @@ export function uid(prefix) {
|
|||
return `${ID_PREFIXES[prefix] || "x"}${randomToken()}`;
|
||||
}
|
||||
|
||||
function getDefaultModuleTitle(type) {
|
||||
return DEFAULT_MODULE_TITLES[type] || "Outil";
|
||||
}
|
||||
|
||||
export function normalizeToolboxIcon(icon) {
|
||||
const value = String(icon || "").trim();
|
||||
const file = value.replace(TOOLBOX_ICON_BASE, "").split("/").pop();
|
||||
|
|
@ -859,7 +843,7 @@ function normalizeEquipmentPlannerEquipment(equipment, typeIds) {
|
|||
})
|
||||
.filter(Boolean)
|
||||
.filter((link, index, links) => links.findIndex((item) => item.pairKey === link.pairKey) === index)
|
||||
.map(({ pairKey, ...link }) => link);
|
||||
.map(({ pairKey: _pairKey, ...link }) => link);
|
||||
const categories = [
|
||||
...characteristics.map((trait) => trait.category),
|
||||
...socketItems.flatMap((socketItem) => socketItem.bonuses.map((bonus) => bonus.category))
|
||||
|
|
@ -999,7 +983,7 @@ export function summarizeEquipmentPlannerData(data) {
|
|||
});
|
||||
|
||||
const numericTotals = [...totals.values()]
|
||||
.map(({ iconSources, sourceKinds, color, sourceKind, ...total }) => ({
|
||||
.map(({ iconSources: _iconSources, sourceKinds: _sourceKinds, color, sourceKind, ...total }) => ({
|
||||
...total,
|
||||
...(color ? { color } : {}),
|
||||
sourceKind
|
||||
|
|
@ -1122,195 +1106,28 @@ function compactEquipmentPlannerEquipmentForStorage(equipment) {
|
|||
return compact;
|
||||
}
|
||||
|
||||
const MODULE_DATA_COMPACTORS = createModuleDataCompactors({
|
||||
uid,
|
||||
normalizeNotepadData,
|
||||
normalizeChecklistData,
|
||||
normalizeLinksData,
|
||||
normalizeCountersData,
|
||||
normalizeCombosData,
|
||||
normalizeCalculatorData,
|
||||
normalizeTableData,
|
||||
normalizeTimerData,
|
||||
normalizeTaskPlannerData,
|
||||
normalizeEquipmentPlannerData,
|
||||
normalizeImageAnnotationData,
|
||||
compactChecklistSectionForStorage,
|
||||
compactEquipmentPlannerEquipmentForStorage,
|
||||
defaultTaskPlannerWeeklyResetDay: DEFAULT_TASK_PLANNER_WEEKLY_RESET_DAY,
|
||||
defaultTaskPlannerResetTime: DEFAULT_TASK_PLANNER_RESET_TIME
|
||||
});
|
||||
|
||||
export function compactModuleDataForStorage(type, value) {
|
||||
if (type === "notepad") {
|
||||
const normalized = normalizeNotepadData(value);
|
||||
const compact = {};
|
||||
if (normalized.html && normalized.text) compact.html = normalized.html;
|
||||
if (normalized.text) compact.text = normalized.text;
|
||||
if (normalized.updatedAt) compact.updatedAt = normalized.updatedAt;
|
||||
if (normalized.drawingMode === "permanent") compact.drawingMode = "permanent";
|
||||
if (normalized.drawingMode === "permanent" && normalized.drawings.strokes.length) compact.drawings = normalized.drawings;
|
||||
return Object.keys(compact).length ? compact : null;
|
||||
}
|
||||
if (type === "checklist") {
|
||||
const normalized = normalizeChecklistData(value);
|
||||
const sections = normalized.sections.map(compactChecklistSectionForStorage).filter((section) => section.items.length);
|
||||
if (!sections.length) return null;
|
||||
const settings = {
|
||||
...(normalized.hideCompletedSections ? { hideCompletedSections: true } : {}),
|
||||
...(normalized.hideCompletedSectionsFully ? { hideCompletedSectionsFully: true } : {})
|
||||
};
|
||||
if (sections.length === 1 && !sections[0].title && typeof sections[0].hideWhenComplete !== "boolean") return { ...settings, items: sections[0].items };
|
||||
return { ...settings, sections };
|
||||
}
|
||||
if (type === "images") {
|
||||
const images = (Array.isArray(value?.images) ? value.images : [])
|
||||
.filter((image) => image?.dataUrl)
|
||||
.map((image) => {
|
||||
const compact = { id: image.id || uid("image"), dataUrl: image.dataUrl };
|
||||
if (image.label) compact.label = image.label;
|
||||
return compact;
|
||||
});
|
||||
return images.length ? { images } : null;
|
||||
}
|
||||
if (type === "imageAnnotation") {
|
||||
const normalized = normalizeImageAnnotationData(value);
|
||||
if (!normalized.image) return null;
|
||||
const compact = {
|
||||
image: normalized.image,
|
||||
markers: normalized.markers.map((marker) => {
|
||||
const compact = { id: marker.id, x: marker.x, y: marker.y };
|
||||
if (marker.label) compact.label = marker.label;
|
||||
return compact;
|
||||
})
|
||||
};
|
||||
if (normalized.drawings.strokes.length) compact.drawings = normalized.drawings;
|
||||
return compact;
|
||||
}
|
||||
if (type === "links") {
|
||||
const links = normalizeLinksData(value).links.map((link) => {
|
||||
const compact = {
|
||||
id: link.id,
|
||||
url: link.url
|
||||
};
|
||||
if (link.title) compact.title = link.title;
|
||||
return compact;
|
||||
});
|
||||
return links.length ? { links } : null;
|
||||
}
|
||||
if (type === "counters") {
|
||||
const counters = normalizeCountersData(value).counters.map((counter) => ({
|
||||
id: counter.id,
|
||||
label: counter.label,
|
||||
value: counter.value
|
||||
}));
|
||||
return counters.length ? { counters } : null;
|
||||
}
|
||||
if (type === "combos") {
|
||||
const normalized = normalizeCombosData(value);
|
||||
const compact = {
|
||||
combos: normalized.combos.map((combo) => {
|
||||
const compactCombo = {
|
||||
id: combo.id,
|
||||
name: combo.name,
|
||||
inputs: combo.inputs
|
||||
};
|
||||
if (combo.category) compactCombo.category = combo.category;
|
||||
if (combo.device) compactCombo.device = combo.device;
|
||||
return compactCombo;
|
||||
})
|
||||
};
|
||||
if (normalized.device !== "playstation") compact.device = normalized.device;
|
||||
if (normalized.collapsedCategories.length) compact.collapsedCategories = normalized.collapsedCategories;
|
||||
if (normalized.categoryOrder.length) compact.categoryOrder = normalized.categoryOrder;
|
||||
if (!compact.combos.length) delete compact.combos;
|
||||
return compact.combos ? compact : null;
|
||||
}
|
||||
if (type === "calculator") {
|
||||
const normalized = normalizeCalculatorData(value);
|
||||
const entries = normalized.entries.map((entry) => {
|
||||
const compact = {
|
||||
id: entry.id,
|
||||
label: entry.label,
|
||||
value: entry.value
|
||||
};
|
||||
if (entry.parentId) compact.parentId = entry.parentId;
|
||||
return compact;
|
||||
});
|
||||
if (!entries.length && !normalized.scrollResults) return null;
|
||||
return normalized.scrollResults ? { entries, scrollResults: true } : { entries };
|
||||
}
|
||||
if (type === "table") {
|
||||
const normalized = normalizeTableData(value);
|
||||
const compact = { cells: normalized.cells };
|
||||
if (normalized.rows !== 10) compact.rows = normalized.rows;
|
||||
if (normalized.columns !== 6) compact.columns = normalized.columns;
|
||||
if (Object.keys(normalized.rowLabels).length) compact.rowLabels = normalized.rowLabels;
|
||||
if (Object.keys(normalized.columnLabels).length) compact.columnLabels = normalized.columnLabels;
|
||||
return Object.keys(compact.cells).length || compact.rows || compact.columns || compact.rowLabels || compact.columnLabels ? compact : null;
|
||||
}
|
||||
if (type === "timer") {
|
||||
const normalized = normalizeTimerData(value);
|
||||
const compact = {};
|
||||
if (normalized.activeTab !== "stopwatch") compact.activeTab = normalized.activeTab;
|
||||
if (normalized.scrollResults) compact.scrollResults = true;
|
||||
if (normalized.sortResults) compact.sortResults = true;
|
||||
if (normalized.stopwatch.elapsedMs || normalized.stopwatch.startedAt || normalized.stopwatch.laps.length) {
|
||||
compact.stopwatch = {
|
||||
...(normalized.stopwatch.elapsedMs ? { elapsedMs: normalized.stopwatch.elapsedMs } : {}),
|
||||
...(normalized.stopwatch.startedAt ? { startedAt: normalized.stopwatch.startedAt } : {}),
|
||||
...(normalized.stopwatch.laps.length ? { laps: normalized.stopwatch.laps } : {})
|
||||
};
|
||||
}
|
||||
if (normalized.countdowns.length) {
|
||||
compact.countdowns = normalized.countdowns.map((countdown) => {
|
||||
const { alertMode, autoRefresh, startMode, startTime, ...compactCountdown } = countdown;
|
||||
if (alertMode !== "off") compactCountdown.alertMode = alertMode;
|
||||
if (autoRefresh) compactCountdown.autoRefresh = true;
|
||||
if (startMode === "time" && startTime) {
|
||||
compactCountdown.startMode = startMode;
|
||||
compactCountdown.startTime = startTime;
|
||||
}
|
||||
return compactCountdown;
|
||||
});
|
||||
}
|
||||
return Object.keys(compact).length ? compact : null;
|
||||
}
|
||||
if (type === "taskPlanner") {
|
||||
const normalized = normalizeTaskPlannerData(value);
|
||||
const compact = {};
|
||||
if (normalized.weeklyResetDay !== DEFAULT_TASK_PLANNER_WEEKLY_RESET_DAY) compact.weeklyResetDay = normalized.weeklyResetDay;
|
||||
if (normalized.resetTime !== DEFAULT_TASK_PLANNER_RESET_TIME) compact.resetTime = normalized.resetTime;
|
||||
if (normalized.lastResetAt) compact.lastResetAt = normalized.lastResetAt;
|
||||
if (normalized.hideCompleted) compact.hideCompleted = true;
|
||||
if (normalized.collapsedCategories.length) compact.collapsedCategories = normalized.collapsedCategories;
|
||||
if (normalized.tasks.length) {
|
||||
compact.tasks = normalized.tasks.map((task) => {
|
||||
const compactTask = {
|
||||
id: task.id,
|
||||
title: task.title,
|
||||
type: task.type
|
||||
};
|
||||
if (task.description) compactTask.description = task.description;
|
||||
if (task.category) compactTask.category = task.category;
|
||||
if (task.checked) compactTask.checked = true;
|
||||
if (task.checked && task.checkedAt) compactTask.checkedAt = task.checkedAt;
|
||||
if (task.type === "daily" && task.dailyResetTime && task.dailyResetTime !== normalized.resetTime) compactTask.dailyResetTime = task.dailyResetTime;
|
||||
if (task.type === "weekly" && task.weeklyResetDay !== undefined) compactTask.weeklyResetDay = task.weeklyResetDay;
|
||||
return compactTask;
|
||||
});
|
||||
}
|
||||
if (normalized.categoryOrder.length) compact.categoryOrder = normalized.categoryOrder;
|
||||
if (normalized.relations.length) {
|
||||
compact.relations = normalized.relations.map((relation) => {
|
||||
const compactRelation = {
|
||||
id: relation.id,
|
||||
fromTaskId: relation.fromTaskId,
|
||||
toTaskId: relation.toTaskId
|
||||
};
|
||||
if (relation.prerequisite) compactRelation.prerequisite = true;
|
||||
return compactRelation;
|
||||
});
|
||||
}
|
||||
return Object.keys(compact).length ? compact : null;
|
||||
}
|
||||
if (type === "equipmentPlanner") {
|
||||
const normalized = normalizeEquipmentPlannerData(value);
|
||||
if (!normalized.equipments.length) return null;
|
||||
const compact = {
|
||||
types: normalized.types.map((type) => {
|
||||
const compactType = { id: type.id, title: type.title };
|
||||
if (type.icon !== "shield") compactType.icon = type.icon;
|
||||
if (type.collapsed) compactType.collapsed = true;
|
||||
return compactType;
|
||||
}),
|
||||
equipments: normalized.equipments.map(compactEquipmentPlannerEquipmentForStorage)
|
||||
};
|
||||
if (normalized.typeOrder.length) compact.typeOrder = normalized.typeOrder;
|
||||
return compact;
|
||||
}
|
||||
return value;
|
||||
const compactor = MODULE_DATA_COMPACTORS[type];
|
||||
return compactor ? compactor(value) : value;
|
||||
}
|
||||
|
||||
export function prepareModuleDataForStorage(toolboxes, toolboxId, moduleId, value, moduleType = "") {
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export function useIndexedToolboxes(onError) {
|
|||
}
|
||||
loadStore();
|
||||
return () => { cancelled = true; };
|
||||
}, []);
|
||||
}, [onError]);
|
||||
|
||||
function persistToolboxes(nextToolboxes) {
|
||||
const normalized = compactToolboxesForStorage(nextToolboxes).map(normalizeToolbox).filter(Boolean);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue