214 lines
5.3 KiB
JavaScript
214 lines
5.3 KiB
JavaScript
import { __ID__, filePath } from "../consts.mjs";
|
|
import { determineFileExtension, getFile, hashFile, lastModifiedAt, uploadFile, uploadJson } from "../utils/fs.mjs";
|
|
import { DBConnectorMixin } from "./mixins/DBConnector.mjs";
|
|
|
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
|
|
|
export class ImageApp extends
|
|
DBConnectorMixin(
|
|
HandlebarsApplicationMixin(
|
|
ApplicationV2
|
|
)) {
|
|
|
|
// #region Options
|
|
static dbType = `Image`;
|
|
static dbPath = `storage/db/images.json`;
|
|
|
|
static DEFAULT_OPTIONS = {
|
|
tag: `form`,
|
|
classes: [
|
|
__ID__,
|
|
`ImageApp`,
|
|
],
|
|
position: {
|
|
width: 600,
|
|
},
|
|
window: {
|
|
contentClasses: [
|
|
`standard-form`,
|
|
],
|
|
},
|
|
form: {
|
|
handler: this.#onSubmit,
|
|
submitOnChange: false,
|
|
closeOnSubmit: true,
|
|
},
|
|
actions: {
|
|
removeEditingImage: this.#removeEditingImage,
|
|
},
|
|
};
|
|
|
|
static PARTS = {
|
|
header: { template: filePath(`templates/ImageApp/header.hbs`) },
|
|
preview: { template: filePath(`templates/ImageApp/preview.hbs`) },
|
|
form: { template: filePath(`templates/ImageApp/form.hbs`) },
|
|
footer: { template: filePath(`templates/partials/footer.hbs`) },
|
|
};
|
|
// #endregion Options
|
|
|
|
// #region Instance Data
|
|
/** The artist that is being edited, or the default artist values */
|
|
_doc = { path: ``, tags: [], artists: [] };
|
|
|
|
get title() {
|
|
if (this._docID) {
|
|
if (this._doc.name) {
|
|
return _loc(
|
|
`TB.apps.ImageApp.title.edit-named`,
|
|
{ name: this._doc.name },
|
|
);
|
|
} else {
|
|
return _loc(`TB.apps.ImageApp.title.edit-generic`);
|
|
}
|
|
};
|
|
return _loc(`TB.apps.ImageApp.title.upload`);
|
|
};
|
|
// #endregion Instance Data
|
|
|
|
// #region Lifecycle
|
|
_onFirstRender() {
|
|
if (this._doc?.name) {
|
|
this._updateFrame({ window: { title: this.title } });
|
|
};
|
|
};
|
|
|
|
_onRender(context, options) {
|
|
if (options.parts?.includes(`header`)) {
|
|
this.element.querySelector(`input[type="file"]`)?.addEventListener(
|
|
`change`,
|
|
this.#changeImage.bind(this),
|
|
);
|
|
};
|
|
};
|
|
|
|
/** @this {ImageApp} */
|
|
static async #onSubmit(event, element, formData) {
|
|
const data = formData.object;
|
|
|
|
// Validate the DB hasn't been updated since opening
|
|
if (!(await this.isAbleToSave())) { return };
|
|
|
|
|
|
// Upload new image to server
|
|
if (!this._docID) {
|
|
const hash = this._doc.hash;
|
|
const extension = determineFileExtension(this.#file);
|
|
const path = `storage/tokens/${hash}.${extension}`;
|
|
|
|
if (!this.#file) {
|
|
// TODO: ERROR
|
|
return;
|
|
}
|
|
const file = new File(
|
|
[this.#file],
|
|
`${hash}.${extension}`,
|
|
{ type: this.#file.type },
|
|
);
|
|
await uploadFile(`tokens`, file);
|
|
|
|
const images = await getFile(this.constructor.dbPath);
|
|
images[hash] = {
|
|
name: data.name,
|
|
tags: data.tags,
|
|
artists: data.artists,
|
|
path: this.#file ? path : ``,
|
|
};
|
|
await uploadJson(`db`, `images.json`, images);
|
|
}
|
|
else {
|
|
const images = await getFile(this.constructor.dbPath);
|
|
Object.assign(images[this._docID], {
|
|
name: data.name,
|
|
tage: data.tags,
|
|
artists: data.artists,
|
|
});
|
|
await uploadJson(`db`, `images.json`, images);
|
|
};
|
|
};
|
|
// #endregion Lifecycle
|
|
|
|
// #region Data Prep
|
|
#lastArtistEdit = null;
|
|
#artistCache = [];
|
|
async _prepareContext() {
|
|
|
|
// Get all of the available artists for the dropdown
|
|
const lastModified = await lastModifiedAt(`storage/db/artists.json`);
|
|
if (
|
|
this.#lastArtistEdit == null
|
|
|| lastModified !== this.#lastArtistEdit
|
|
) {
|
|
this.#artistCache = [];
|
|
const artistDB = await getFile(`storage/db/artists.json`);
|
|
for (const [id, artist] of Object.entries(artistDB)) {
|
|
this.#artistCache.push({ value: id, label: artist.name });
|
|
};
|
|
};
|
|
|
|
const ctx = {
|
|
meta: {
|
|
idp: this.id,
|
|
},
|
|
imageTypes: Object.values(CONST.IMAGE_FILE_EXTENSIONS).join(`,`),
|
|
imageURL: this._doc.path.startsWith(`blob`)
|
|
? this._doc.path
|
|
: filePath(this._doc.path),
|
|
docID: this._docID,
|
|
image: this._doc,
|
|
artists: this.#artistCache,
|
|
};
|
|
|
|
return ctx;
|
|
};
|
|
// #endregion Data Prep
|
|
|
|
// #region Actions
|
|
/** @type {File | null} */
|
|
#file = null;
|
|
async #changeImage(event) {
|
|
/** @type {File} */
|
|
const file = this.#file = event.target.files[0];
|
|
|
|
// Prevent memory leaks
|
|
URL.revokeObjectURL(this._doc.path);
|
|
|
|
if (!file) {
|
|
this.#file = null;
|
|
this._docID = null;
|
|
this._doc = { path: ``, artists: [], tags: [] };
|
|
this._updateFrame({ window: { title: this.title } });
|
|
await this.render({ parts: [`header`, `preview`, `form`] });
|
|
return;
|
|
};
|
|
|
|
// Ensure we don't already have the file uploaded
|
|
const extension = determineFileExtension(file);
|
|
const hash = await hashFile(file);
|
|
const path = `storage/tokens/${hash}.${extension}`;
|
|
const lastModified = await lastModifiedAt(path);
|
|
if (lastModified) {
|
|
this._docID = hash;
|
|
this._doc.hash = hash;
|
|
await this._fetchDocument();
|
|
this._updateFrame({ window: { title: this.title } });
|
|
await this.render({ parts: [`header`, `preview`, `form`] });
|
|
return;
|
|
};
|
|
|
|
// Temporarily blob the image so it can be displayed in-app
|
|
const url = URL.createObjectURL(file);
|
|
this._doc.path = url;
|
|
this._doc.hash = hash;
|
|
await this.render({ parts: [`preview`] });
|
|
};
|
|
|
|
/** @this {ImageApp} */
|
|
static async #removeEditingImage() {
|
|
this.#file = null;
|
|
this._docID = null;
|
|
this._doc = { path: ``, artists: [], tags: [] };
|
|
this._updateFrame({ window: { title: this.title } });
|
|
await this.render({ parts: [`header`, `preview`, `form`] });
|
|
};
|
|
// #endregion Actions
|
|
};
|