mirror of
https://github.com/Kornstalx/5etools-mirror-2.github.io.git
synced 2025-10-28 20:45:35 -05:00
v1.209.0
This commit is contained in:
263
js/utils-brew/utils-brew-impl-brew.js
Normal file
263
js/utils-brew/utils-brew-impl-brew.js
Normal file
@@ -0,0 +1,263 @@
|
||||
import {BrewUtil2Base} from "./utils-brew-base.js";
|
||||
import {BrewDoc} from "./utils-brew-models.js";
|
||||
|
||||
export class BrewUtil2_ extends BrewUtil2Base {
|
||||
_STORAGE_KEY_LEGACY = "HOMEBREW_STORAGE";
|
||||
_STORAGE_KEY_LEGACY_META = "HOMEBREW_META_STORAGE";
|
||||
|
||||
// Keep these distinct from the OG brew key, so users can recover their old brew if required.
|
||||
_STORAGE_KEY = "HOMEBREW_2_STORAGE";
|
||||
_STORAGE_KEY_META = "HOMEBREW_2_STORAGE_METAS";
|
||||
|
||||
_STORAGE_KEY_CUSTOM_URL = "HOMEBREW_CUSTOM_REPO_URL";
|
||||
_STORAGE_KEY_MIGRATION_VERSION = "HOMEBREW_2_STORAGE_MIGRATION";
|
||||
|
||||
_VERSION = 2;
|
||||
|
||||
_PATH_LOCAL_DIR = "homebrew";
|
||||
_PATH_LOCAL_INDEX = VeCt.JSON_BREW_INDEX;
|
||||
|
||||
IS_EDITABLE = true;
|
||||
PAGE_MANAGE = UrlUtil.PG_MANAGE_BREW;
|
||||
URL_REPO_DEFAULT = VeCt.URL_BREW;
|
||||
URL_REPO_ROOT_DEFAULT = VeCt.URL_ROOT_BREW;
|
||||
DISPLAY_NAME = "homebrew";
|
||||
DISPLAY_NAME_PLURAL = "homebrews";
|
||||
DEFAULT_AUTHOR = "";
|
||||
STYLE_BTN = "btn-info";
|
||||
IS_PREFER_DATE_ADDED = true;
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
_pInit_doBindDragDrop () {
|
||||
document.body.addEventListener("drop", async evt => {
|
||||
if (EventUtil.isInInput(evt)) return;
|
||||
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
|
||||
const files = evt.dataTransfer?.files;
|
||||
if (!files?.length) return;
|
||||
|
||||
const pFiles = [...files].map((file, i) => {
|
||||
if (!/\.json$/i.test(file.name)) return null;
|
||||
|
||||
return new Promise(resolve => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
let json;
|
||||
try {
|
||||
json = JSON.parse(reader.result);
|
||||
} catch (ignored) {
|
||||
return resolve(null);
|
||||
}
|
||||
|
||||
resolve({name: file.name, json});
|
||||
};
|
||||
|
||||
reader.readAsText(files[i]);
|
||||
});
|
||||
});
|
||||
|
||||
const fileMetas = (await Promise.allSettled(pFiles))
|
||||
.filter(({status}) => status === "fulfilled")
|
||||
.map(({value}) => value)
|
||||
.filter(Boolean);
|
||||
|
||||
await this.pAddBrewsFromFiles(fileMetas);
|
||||
|
||||
if (this.isReloadRequired()) this.doLocationReload();
|
||||
});
|
||||
|
||||
document.body.addEventListener("dragover", evt => {
|
||||
if (EventUtil.isInInput(evt)) return;
|
||||
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
async pGetSourceIndex (urlRoot) { return DataUtil.brew.pLoadSourceIndex(urlRoot); }
|
||||
|
||||
getFileUrl (path, urlRoot) { return DataUtil.brew.getFileUrl(path, urlRoot); }
|
||||
|
||||
pLoadTimestamps (urlRoot) { return DataUtil.brew.pLoadTimestamps(urlRoot); }
|
||||
|
||||
pLoadPropIndex (urlRoot) { return DataUtil.brew.pLoadPropIndex(urlRoot); }
|
||||
|
||||
pLoadMetaIndex (urlRoot) { return DataUtil.brew.pLoadMetaIndex(urlRoot); }
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
// region Editable
|
||||
async pGetEditableBrewDoc () {
|
||||
return this._findEditableBrewDoc({brewRaw: await this._pGetBrewRaw()});
|
||||
}
|
||||
|
||||
_findEditableBrewDoc ({brewRaw}) {
|
||||
return brewRaw.find(it => it.head.isEditable);
|
||||
}
|
||||
|
||||
async pGetOrCreateEditableBrewDoc () {
|
||||
const existing = await this.pGetEditableBrewDoc();
|
||||
if (existing) return existing;
|
||||
|
||||
const brew = this._getNewEditableBrewDoc();
|
||||
const brews = [...MiscUtil.copyFast(await this._pGetBrewRaw()), brew];
|
||||
await this.pSetBrew(brews);
|
||||
|
||||
return brew;
|
||||
}
|
||||
|
||||
async pSetEditableBrewDoc (brew) {
|
||||
if (!brew?.head?.docIdLocal || !brew?.body) throw new Error(`Invalid editable brew document!`); // Sanity check
|
||||
await this.pUpdateBrew(brew);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param prop
|
||||
* @param uniqueId
|
||||
* @param isDuplicate If the entity should be a duplicate, i.e. have a new `uniqueId`.
|
||||
*/
|
||||
async pGetEditableBrewEntity (prop, uniqueId, {isDuplicate = false} = {}) {
|
||||
if (!uniqueId) throw new Error(`A "uniqueId" must be provided!`);
|
||||
|
||||
const brew = await this.pGetOrCreateEditableBrewDoc();
|
||||
|
||||
const out = (brew.body?.[prop] || []).find(it => it.uniqueId === uniqueId);
|
||||
if (!out || !isDuplicate) return out;
|
||||
|
||||
if (isDuplicate) out.uniqueId = CryptUtil.uid();
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
async pPersistEditableBrewEntity (prop, ent) {
|
||||
if (!ent.uniqueId) throw new Error(`Entity did not have a "uniqueId"!`);
|
||||
|
||||
const brew = await this.pGetOrCreateEditableBrewDoc();
|
||||
|
||||
const ixExisting = (brew.body?.[prop] || []).findIndex(it => it.uniqueId === ent.uniqueId);
|
||||
if (!~ixExisting) {
|
||||
const nxt = MiscUtil.copyFast(brew);
|
||||
MiscUtil.getOrSet(nxt.body, prop, []).push(ent);
|
||||
|
||||
await this.pUpdateBrew(nxt);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const nxt = MiscUtil.copyFast(brew);
|
||||
nxt.body[prop][ixExisting] = ent;
|
||||
|
||||
await this.pUpdateBrew(nxt);
|
||||
}
|
||||
|
||||
async pRemoveEditableBrewEntity (prop, uniqueId) {
|
||||
if (!uniqueId) throw new Error(`A "uniqueId" must be provided!`);
|
||||
|
||||
const brew = await this.pGetOrCreateEditableBrewDoc();
|
||||
|
||||
if (!brew.body?.[prop]?.length) return;
|
||||
|
||||
const nxt = MiscUtil.copyFast(brew);
|
||||
nxt.body[prop] = nxt.body[prop].filter(it => it.uniqueId !== uniqueId);
|
||||
|
||||
if (nxt.body[prop].length === brew.body[prop]) return; // Silently allow no-op deletes
|
||||
|
||||
await this.pUpdateBrew(nxt);
|
||||
}
|
||||
|
||||
async pAddSource (sourceObj) {
|
||||
const existing = await this.pGetEditableBrewDoc();
|
||||
|
||||
if (existing) {
|
||||
const nxt = MiscUtil.copyFast(existing);
|
||||
const sources = MiscUtil.getOrSet(nxt.body, "_meta", "sources", []);
|
||||
sources.push(sourceObj);
|
||||
|
||||
await this.pUpdateBrew(nxt);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const json = {_meta: {sources: [sourceObj]}};
|
||||
const brew = this._getBrewDoc({json, isEditable: true});
|
||||
const brews = [...MiscUtil.copyFast(await this._pGetBrewRaw()), brew];
|
||||
await this.pSetBrew(brews);
|
||||
}
|
||||
|
||||
async pEditSource (sourceObj) {
|
||||
const existing = await this.pGetEditableBrewDoc();
|
||||
if (!existing) throw new Error(`Editable brew document does not exist!`);
|
||||
|
||||
const nxt = MiscUtil.copyFast(existing);
|
||||
const sources = MiscUtil.get(nxt.body, "_meta", "sources");
|
||||
if (!sources) throw new Error(`Source "${sourceObj.json}" does not exist in editable brew document!`);
|
||||
|
||||
const existingSourceObj = sources.find(it => it.json === sourceObj.json);
|
||||
if (!existingSourceObj) throw new Error(`Source "${sourceObj.json}" does not exist in editable brew document!`);
|
||||
Object.assign(existingSourceObj, sourceObj);
|
||||
|
||||
await this.pUpdateBrew(nxt);
|
||||
}
|
||||
|
||||
async pIsEditableSourceJson (sourceJson) {
|
||||
const brew = await this.pGetEditableBrewDoc();
|
||||
if (!brew) return false;
|
||||
|
||||
const sources = MiscUtil.get(brew.body, "_meta", "sources") || [];
|
||||
return sources.some(it => it.json === sourceJson);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the brews containing a given source to the editable document. If a brew cannot be moved to the editable
|
||||
* document, copy the source to the editable document instead.
|
||||
*/
|
||||
async pMoveOrCopyToEditableBySourceJson (sourceJson) {
|
||||
if (await this.pIsEditableSourceJson(sourceJson)) return;
|
||||
|
||||
// Fetch all candidate brews
|
||||
const brews = (await this._pGetBrewRaw()).filter(brew => (brew.body._meta?.sources || []).some(src => src.json === sourceJson));
|
||||
const brewsLocal = (await this._pGetBrew_pGetLocalBrew()).filter(brew => (brew.body._meta?.sources || []).some(src => src.json === sourceJson));
|
||||
|
||||
// Arbitrarily select one, preferring non-local
|
||||
let brew = brews.find(brew => BrewDoc.isOperationPermitted_moveToEditable({brew}));
|
||||
if (!brew) brew = brewsLocal.find(brew => BrewDoc.isOperationPermitted_moveToEditable({brew, isAllowLocal: true}));
|
||||
|
||||
if (!brew) return;
|
||||
|
||||
if (brew.head.isLocal) return this.pCopyToEditable({brews: [brew]});
|
||||
|
||||
return this.pMoveToEditable({brews: [brew]});
|
||||
}
|
||||
|
||||
async pMoveToEditable ({brews}) {
|
||||
const out = await this.pCopyToEditable({brews});
|
||||
await this.pDeleteBrews(brews);
|
||||
return out;
|
||||
}
|
||||
|
||||
async pCopyToEditable ({brews}) {
|
||||
const brewEditable = await this.pGetOrCreateEditableBrewDoc();
|
||||
|
||||
const cpyBrewEditableDoc = BrewDoc.fromObject(brewEditable, {isCopy: true});
|
||||
brews.forEach((brew, i) => cpyBrewEditableDoc.mutMerge({json: brew.body, isLazy: i !== brews.length - 1}));
|
||||
|
||||
await this.pSetEditableBrewDoc(cpyBrewEditableDoc.toObject());
|
||||
|
||||
return cpyBrewEditableDoc;
|
||||
}
|
||||
|
||||
async pHasEditableSourceJson () {
|
||||
const brewsStored = await this._pGetBrewRaw();
|
||||
if (!brewsStored?.length) return false;
|
||||
|
||||
return brewsStored
|
||||
.map(brew => BrewDoc.fromObject(brew))
|
||||
.some(brew => brew.head.isEditable && !brew.isEmpty());
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
Reference in New Issue
Block a user