Add first iteration of the Image DB editor
This commit is contained in:
parent
773c506570
commit
c7c02702d7
13 changed files with 425 additions and 7 deletions
197
module/apps/Image.mjs
Normal file
197
module/apps/Image.mjs
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
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: {},
|
||||
};
|
||||
|
||||
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 } });
|
||||
};
|
||||
|
||||
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),
|
||||
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._docID = null;
|
||||
this._doc = { path: ``, artists: [], tags: [] };
|
||||
this._updateFrame({ window: { title: this.title } });
|
||||
await this.render({ parts: [`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: [`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`] });
|
||||
};
|
||||
// #endregion Actions
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue