193 lines
4.4 KiB
JavaScript
193 lines
4.4 KiB
JavaScript
import { __ID__, filePath } from "../consts.mjs";
|
|
import { getFile, lastModifiedAt, uploadJson } from "../utils/fs.mjs";
|
|
import { promptViaTemplate } from "../utils/dialogs.mjs";
|
|
|
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
|
const { randomID } = foundry.utils;
|
|
|
|
export class ArtistApp extends HandlebarsApplicationMixin(ApplicationV2) {
|
|
|
|
// #region Options
|
|
static DEFAULT_OPTIONS = {
|
|
tag: `form`,
|
|
classes: [
|
|
__ID__,
|
|
`ArtistApp`,
|
|
],
|
|
position: {
|
|
width: 350,
|
|
},
|
|
window: {
|
|
contentClasses: [
|
|
`standard-form`,
|
|
],
|
|
},
|
|
form: {
|
|
handler: this.#onSubmit,
|
|
submitOnChange: false,
|
|
closeOnSubmit: true,
|
|
},
|
|
actions: {
|
|
addNewLink: this.#addNewLink,
|
|
deleteLink: this.#deleteLink,
|
|
},
|
|
};
|
|
|
|
static PARTS = {
|
|
form: {
|
|
template: filePath(`templates/ArtistApp/form.hbs`),
|
|
},
|
|
linkList: {
|
|
template: filePath(`templates/ArtistApp/linkList.hbs`),
|
|
},
|
|
footer: {
|
|
template: filePath(`templates/ArtistApp/footer.hbs`),
|
|
},
|
|
};
|
|
// #endregion Options
|
|
|
|
// #region Instance Data
|
|
/** @type { null | string } */
|
|
#artistID = null;
|
|
|
|
/** The artist that is being edited, or the default artist values */
|
|
#artist = { name: ``, links: [] };
|
|
|
|
/** @type { null | string } */
|
|
#lastModified = null;
|
|
|
|
constructor({ artistID = null, ...opts } = {}) {
|
|
super(opts);
|
|
this.#artistID = artistID;
|
|
};
|
|
|
|
get title() {
|
|
if (this.#artistID && this.#artist.name) {
|
|
return game.i18n.format(
|
|
`TB.apps.ArtistApp.title.edit`,
|
|
{ name: this.#artist.name },
|
|
);
|
|
};
|
|
return game.i18n.localize(`TB.apps.ArtistApp.title.create`);
|
|
};
|
|
// #endregion Instance Data
|
|
|
|
// #region Lifecycle
|
|
/** Whether or not the database values have been initialized */
|
|
#connected = false;
|
|
|
|
/**
|
|
* This fetches the DB data that we care about for the purposes of being able
|
|
* to create/edit entries in the Artists DB
|
|
*/
|
|
async #connectToDB() {
|
|
if (this.#connected) { return true };
|
|
this.#lastModified ??= await lastModifiedAt(`storage/db/artists.json`);
|
|
|
|
if (this.#artistID) {
|
|
const artists = await getFile(`storage/db/artists.json`);
|
|
|
|
if (artists[this.#artistID] == null) {
|
|
ui.notifications.error(_loc(
|
|
`TB.notifs.error.artist-ID-404`,
|
|
{ id: this.#artistID },
|
|
));
|
|
return false;
|
|
};
|
|
|
|
Object.assign(this.#artist, artists[this.#artistID]);
|
|
};
|
|
this.#connected = true;
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Ensures that the app is in a state where it is allowed to render
|
|
* before actually letting it render at all.
|
|
*/
|
|
async render(options, ...args) {
|
|
const allowed = await this.#connectToDB();
|
|
if (!allowed) { return this };
|
|
return super.render(options, ...args);
|
|
};
|
|
|
|
/**
|
|
* Makes it so that we update the frame's title to include the Artist name if
|
|
* we're editing an existing artist instead of creating a new one.
|
|
*/
|
|
_onFirstRender() {
|
|
if (this.#artist.name) {
|
|
this._updateFrame({ window: { title: this.title } });
|
|
};
|
|
};
|
|
|
|
/** @this {ArtistApp} */
|
|
static async #onSubmit(event, element, formData) {
|
|
const artist = formData.object;
|
|
|
|
if (artist.name.length === 0) { return };
|
|
artist.links = this.#artist.links;
|
|
artist.links.forEach(link => {
|
|
delete link.isNew;
|
|
});
|
|
|
|
// Validate the DB hasn't been updated since
|
|
if (this.#artistID) {
|
|
const newLastModified = await lastModifiedAt(`storage/db/artists.json`);
|
|
if (newLastModified !== this.#lastModified) {
|
|
ui.notifications.error(
|
|
`TB.notifs.error.db-out-of-date`,
|
|
{ localize: true },
|
|
);
|
|
return;
|
|
};
|
|
};
|
|
|
|
const artists = getFile(`storage/db/artists.json`);
|
|
|
|
let id = this.#artistID;
|
|
if (!this.#artistID) {
|
|
do {
|
|
id = randomID();
|
|
} while (artists[id] != null);
|
|
};
|
|
artists[id] = artist;
|
|
|
|
await uploadJson(`db`, `artists.json`, artists);
|
|
};
|
|
// #endregion Lifecycle
|
|
|
|
// #region Data Prep
|
|
async _prepareContext() {
|
|
const ctx = {
|
|
meta: {
|
|
idp: this.id,
|
|
},
|
|
artist: this.#artist,
|
|
};
|
|
|
|
return ctx;
|
|
};
|
|
// #endregion Data Prep
|
|
|
|
// #region Actions
|
|
/** @this {ArtistApp} */
|
|
static async #addNewLink() {
|
|
const link = await promptViaTemplate(
|
|
`TB.dialogs.Link.title`,
|
|
`templates/Dialogs/Link.hbs`,
|
|
);
|
|
link.isNew = true;
|
|
this.#artist.links.push(link);
|
|
this.render({ parts: [ `linkList` ] });
|
|
};
|
|
|
|
/** @this {ArtistApp} */
|
|
static async #deleteLink(event, element) {
|
|
const index = element.closest(`[data-index]`)?.dataset.index;
|
|
if (index == null) { return };
|
|
this.#artist.links.splice(index, 1);
|
|
this.render({ parts: [ `linkList` ] });
|
|
};
|
|
// #endregion Actions
|
|
};
|