Import des persos du système précédent
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Chroniques de l'Étrange — Système FoundryVTT
|
||||
*
|
||||
* Chroniques de l'Étrange est un jeu de rôle édité par Antre-Monde Éditions.
|
||||
* Ce système FoundryVTT est une implémentation indépendante et n'est pas
|
||||
* affilié à Antre-Monde Éditions,
|
||||
* mais a été réalisé avec l'autorisation d'Antre-Monde Éditions.
|
||||
*
|
||||
* @author LeRatierBretonnien
|
||||
* @copyright 2024–2026 LeRatierBretonnien
|
||||
* @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
*/
|
||||
|
||||
import { parseLegacyJson } from "../../migration/migrator.js"
|
||||
|
||||
const MIGRATION_TEMPLATE = "systems/fvtt-chroniques-de-l-etrange/templates/apps/cde-migration-app.html"
|
||||
|
||||
/**
|
||||
* Dialog for importing legacy CDE actor JSON files into the current system.
|
||||
*
|
||||
* Accessible via the System Settings menu (registerMenu).
|
||||
* Supports multi-file selection and shows a preview table before importing.
|
||||
*/
|
||||
export class CDEMigrationApp extends foundry.applications.api.HandlebarsApplicationMixin(
|
||||
foundry.applications.api.ApplicationV2
|
||||
) {
|
||||
static DEFAULT_OPTIONS = {
|
||||
id: "cde-migration-app",
|
||||
classes: ["cde-migration-app"],
|
||||
tag: "div",
|
||||
window: {
|
||||
title: "CDE.MigrationTitle",
|
||||
icon: "fas fa-file-import",
|
||||
resizable: false,
|
||||
},
|
||||
position: { width: 560, height: "auto" },
|
||||
actions: {
|
||||
clearFiles: CDEMigrationApp.#clearFiles,
|
||||
doImport: CDEMigrationApp.#doImport,
|
||||
},
|
||||
}
|
||||
|
||||
static PARTS = {
|
||||
form: { template: MIGRATION_TEMPLATE },
|
||||
}
|
||||
|
||||
/** @type {Array<{name: string, type: string, img: string, system: object, items: object[], _srcFile: string}>} */
|
||||
#pending = []
|
||||
|
||||
/** @type {string[]} - error messages per file */
|
||||
#errors = []
|
||||
|
||||
async _prepareContext(options) {
|
||||
return {
|
||||
pending: this.#pending,
|
||||
errors: this.#errors,
|
||||
hasPending: this.#pending.length > 0,
|
||||
hasErrors: this.#errors.length > 0,
|
||||
count: this.#pending.length,
|
||||
}
|
||||
}
|
||||
|
||||
/** After render, wire up the file input. */
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options)
|
||||
const input = this.element.querySelector(".cde-migration-file-input")
|
||||
input?.addEventListener("change", this.#onFileChange.bind(this))
|
||||
|
||||
const dropZone = this.element.querySelector(".cde-migration-drop-zone")
|
||||
if (dropZone) {
|
||||
dropZone.addEventListener("dragover", (e) => { e.preventDefault(); dropZone.classList.add("is-dragover") })
|
||||
dropZone.addEventListener("dragleave", () => dropZone.classList.remove("is-dragover"))
|
||||
dropZone.addEventListener("drop", (e) => {
|
||||
e.preventDefault()
|
||||
dropZone.classList.remove("is-dragover")
|
||||
this.#processFiles(Array.from(e.dataTransfer.files))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async #onFileChange(event) {
|
||||
const files = Array.from(event.target.files ?? [])
|
||||
event.target.value = ""
|
||||
await this.#processFiles(files)
|
||||
}
|
||||
|
||||
async #processFiles(files) {
|
||||
for (const file of files) {
|
||||
if (!file.name.endsWith(".json")) {
|
||||
this.#errors.push(game.i18n.format("CDE.MigrationErrorNotJson", { file: file.name }))
|
||||
continue
|
||||
}
|
||||
try {
|
||||
const text = await file.text()
|
||||
const actors = parseLegacyJson(text)
|
||||
for (const actor of actors) {
|
||||
actor._srcFile = file.name
|
||||
// Avoid duplicates by name
|
||||
if (!this.#pending.some(p => p.name === actor.name)) {
|
||||
this.#pending.push(actor)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
this.#errors.push(game.i18n.format("CDE.MigrationErrorParse", { file: file.name, error: err.message }))
|
||||
}
|
||||
}
|
||||
this.render()
|
||||
}
|
||||
|
||||
static async #clearFiles() {
|
||||
this.#pending = []
|
||||
this.#errors = []
|
||||
this.render()
|
||||
}
|
||||
|
||||
static async #doImport() {
|
||||
if (!this.#pending.length) return
|
||||
|
||||
const created = []
|
||||
const failed = []
|
||||
|
||||
for (const data of this.#pending) {
|
||||
try {
|
||||
const { _srcFile, ...actorData } = data
|
||||
const actor = await Actor.create(actorData)
|
||||
created.push(actor.name)
|
||||
} catch (err) {
|
||||
failed.push(`${data.name}: ${err.message}`)
|
||||
console.error(`CHRONIQUESDELETRANGE | Migration failed for "${data.name}":`, err)
|
||||
}
|
||||
}
|
||||
|
||||
this.#pending = []
|
||||
this.#errors = failed
|
||||
this.render()
|
||||
|
||||
if (created.length) {
|
||||
ui.notifications.info(
|
||||
game.i18n.format("CDE.MigrationSuccess", { count: created.length, names: created.join(", ") })
|
||||
)
|
||||
}
|
||||
if (failed.length) {
|
||||
ui.notifications.warn(
|
||||
game.i18n.format("CDE.MigrationPartialError", { count: failed.length })
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user