fix eslint errors, only allow im- and export of <20 templates

This commit is contained in:
HF 2024-02-04 23:45:38 +01:00
parent ff7ec9b0e9
commit 99f07115b3
8 changed files with 62 additions and 49 deletions

View File

@ -2,7 +2,7 @@
* Item for list of Tamplates
*/
import React, { useRef, useState, useEffect } from 'react';
import React, { useRef, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { t } from 'ttag';
@ -26,7 +26,6 @@ const TemplateItem = ({
if (!previewImg) {
return;
}
console.log('rerendering image', imageId, previewImg);
const bitmap = await createImageBitmap(previewImg);
imgRef.current.getContext('bitmaprenderer')
.transferFromImageBitmap(bitmap);
@ -67,6 +66,7 @@ const TemplateItem = ({
evt.stopPropagation();
startEditing(title);
}}
type="button"
>
{t`Edit`}
</button>
@ -76,6 +76,7 @@ const TemplateItem = ({
dispatch(selectCanvas(canvasId));
dispatch(setViewCoordinates([x + width / 2, y + height / 2]));
}}
type="button"
>
{t`Go to`}
</button>

View File

@ -93,7 +93,7 @@ const TemplateItemEdit = ({
</div>
<div
className="centered-on-img modallink"
onClick={(evt) => fileRef.current?.click()}
onClick={() => fileRef.current?.click()}
>{t`Select File`}</div>
<input
type="file"
@ -114,7 +114,7 @@ const TemplateItemEdit = ({
type="text"
onChange={(evt) => {
const newTitle = evt.target.value;
setTitleUnique(!templateList.some((t) => t.title === newTitle));
setTitleUnique(!templateList.some((z) => z.title === newTitle));
setTitle(evt.target.value);
}}
placeholder={t`Template Name`}
@ -152,8 +152,9 @@ const TemplateItemEdit = ({
co = coordsFromUrl(co) || co;
evt.target.value = co;
const newCoords = co.split('_').map((z) => parseInt(z, 10));
setCoords((!newCoords.some(Number.isNaN) && newCoords.length === 2)
? newCoords : null,
setCoords(
(!newCoords.some(Number.isNaN) && newCoords.length === 2)
? newCoords : null,
);
}}
/></span>
@ -171,35 +172,34 @@ const TemplateItemEdit = ({
stopEditing(initTitle);
templateLoader.deleteTemplate(initTitle);
}}
type="button"
>
{t`Delete`}
</button>
)}
<button
onClick={(evt) => stopEditing(title)}
onClick={() => stopEditing(title)}
type="button"
>
{t`Cancel`}
</button>
<button
disabled={!canSubmit}
onClick={async (evt) => {
onClick={async () => {
if (!canSubmit) {
return;
}
const [x, y] = coords;
if (!initTitle) {
console.log('Create new template');
await templateLoader.addFile(file, title, canvasId, x, y);
} else {
if (file && imageId) {
console.log('file changed for id', imageId);
await templateLoader.updateFile(imageId, file);
}
if (initTitle
&& (initTitle !== title || initX !== x
|| initY !== y || initCanvasId !== canvasId
)) {
console.log(`template ${title} changed`);
templateLoader.changeTemplate(initTitle, {
title, canvasId, x, y,
});
@ -207,6 +207,7 @@ const TemplateItemEdit = ({
}
stopEditing(initTitle);
}}
type="button"
>
{t`Save`}
</button>

View File

@ -2,6 +2,8 @@
* Settings for minimap / overlay
*/
/* eslint-disable react/no-array-index-key */
import React, { useState, useCallback, useRef } from 'react';
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
import fileDownload from 'js-file-download';
@ -37,7 +39,7 @@ const TemplateSettings = () => {
const dispatch = useDispatch();
const toggleEditing = useCallback((title) => {
const index = list.findIndex((t) => t.title === title);
const index = list.findIndex((z) => z.title === title);
const ind = editingIndices.indexOf(index);
setEditingIndices((ind === -1)
? [...editingIndices, index]
@ -45,13 +47,14 @@ const TemplateSettings = () => {
);
}, [list, editingIndices]);
console.log('list', list);
return (
<>
<h2>{t`Templates`}</h2>
<p>
{t`Tired of always spaming one single color? Want to create art instead, but you have to count pixels from some other image? Templates can help you with that! Templates can show as overlay and you can draw over them. One pixel on the template, should be one pixel on the canvas.`}
{
// eslint-disable-next-line max-len
t`Tired of always spaming one single color? Want to create art instead, but you have to count pixels from some other image? Templates can help you with that! Templates can show as overlay and you can draw over them. One pixel on the template, should be one pixel on the canvas.`
}
</p>
<SettingsItem
title={t`Enable Overlay`}
@ -64,10 +67,12 @@ const TemplateSettings = () => {
<SettingsItem
title={t`Small Pixels Overlay`}
value={oSmallPxls}
deactivated={!oVEnabled}
onToggle={() => dispatch(toggleSmallPxls())}
>
{t`Show overlay as small individual pixels (will only show in high zoomlevels).`}
{
// eslint-disable-next-line max-len
t`Show overlay as small individual pixels (will only show in high zoomlevels).`
}
</SettingsItem>
<div className="setitem">
@ -144,9 +149,11 @@ const TemplateSettings = () => {
className="modallink"
onClick={async () => {
const data = await templateLoader.exportEnabledTemplates();
fileDownload(
JSON.stringify(data), 'PixelplanetTemplates.json',
);
if (data) {
fileDownload(
JSON.stringify(data), 'PixelplanetTemplates.json',
);
}
}}
>{t`Export enabled templates`}</span>
</React.Fragment>

View File

@ -50,7 +50,7 @@ const PencilButton = () => {
}
}
dispatch(selectHoldPaint(nextMode));
}, [holdPaint, dispatch]);
}, [holdPaint, easterEgg, dispatch]);
const onShortPress = useCallback(() => {
let nextMode;

View File

@ -153,7 +153,8 @@ export function createKeyDownHandler(store) {
if (event.location === KeyboardEvent.DOM_KEY_LOCATION_RIGHT) {
// right shift
store.dispatch(selectHoldPaint(
(store.getState().gui.easterEgg) ? HOLD_PAINT.OVERLAY : HOLD_PAINT.HISTORY,
(store.getState().gui.easterEgg)
? HOLD_PAINT.OVERLAY : HOLD_PAINT.HISTORY,
true,
));
return;

View File

@ -111,7 +111,6 @@ export default (store) => (next) => (action) => {
case 's/REM_TEMPLATE':
case 's/UPD_TEMPLATE_IMG':
case 's/SET_O_OPACITY':
//
case 'REQ_BIG_CHUNK':
case 'PRE_LOADED_BIG_CHUNK':
case 'REC_BIG_CHUNK':

View File

@ -2,6 +2,8 @@
* class for storing templates for minimap / overlay
*/
import { t } from 'ttag';
import FileStorage from '../utils/FileStorage';
import {
removeTemplate,
@ -11,6 +13,7 @@ import {
templatesReady,
receivedTemplate,
} from '../store/actions/templates';
import { pAlert } from '../store/actions';
import { bufferToBase64, base64ToBuffer } from '../core/utils';
import Template from './Template';
@ -32,6 +35,7 @@ class TemplateLoader {
this.#store.dispatch(templatesReady());
await this.syncDB();
} catch (err) {
// eslint-disable-next-line no-console
console.warn(`Couldn't initialize Templates: ${err.message}`);
}
}
@ -59,7 +63,6 @@ class TemplateLoader {
if (template) {
return template.image;
}
// TODO some store action when available
this.loadExistingTemplate(id);
return null;
}
@ -72,7 +75,6 @@ class TemplateLoader {
if (template) {
return template.imageSmall;
}
// TODO some store action when available
this.loadExistingTemplate(id);
return null;
}
@ -119,10 +121,10 @@ class TemplateLoader {
async syncDB() {
try {
const { list } = this.#store.getState().templates;
const ids = list.map((t) => t.imageId);
const ids = list.map((z) => z.imageId);
const deadIds = await this.#fileStorage.sync(ids);
list.filter((t) => deadIds.includes(t.imageId)).forEach((t) => {
this.#store.dispatch(removeTemplate(t.title));
list.filter((z) => deadIds.includes(z.imageId)).forEach((z) => {
this.#store.dispatch(removeTemplate(z.title));
});
} catch (err) {
// eslint-disable-next-line no-console
@ -137,7 +139,7 @@ class TemplateLoader {
*/
async loadAllMissing() {
const { templates } = this.#store.getState();
const ids = templates.list.map((t) => t.imageId);
const ids = templates.list.map((z) => z.imageId);
const toLoad = ids.filter((i) => !this.#templates.has(i));
for (const id of toLoad) {
// eslint-disable-next-line no-await-in-loop
@ -156,7 +158,6 @@ class TemplateLoader {
throw new Error('File does not exist in indexedDB');
}
const { mimetype, buffer } = fileData;
console.log('mime', mimetype, 'buffer', buffer);
const template = new Template(imageId);
await template.fromBuffer(buffer, mimetype);
this.#templates.set(imageId, template);
@ -222,13 +223,20 @@ class TemplateLoader {
async exportEnabledTemplates() {
const { list } = this.#store.getState().templates;
const tDataList = list.filter((z) => z.enabled);
if (!tDataList.length || tDataList.length > 20) {
this.#store.dispatch(pAlert(
t`Error :(`,
t`Can not export more than 20 or no template!`,
'error',
));
return null;
}
const temps = await this.#fileStorage.loadFile(
tDataList.map((z) => z.imageId),
);
const serilizableObj = [];
for (let i = 0; i < tDataList.length; i += 1) {
const { buffer, mimetype } = temps[i];
console.log('mimetype', mimetype);
serilizableObj.push({
...tDataList[i],
// eslint-disable-next-line no-await-in-loop
@ -241,13 +249,20 @@ class TemplateLoader {
async importTemplates(file) {
const tDataList = JSON.parse(await file.text());
if (!tDataList.length || tDataList.length > 20) {
this.#store.dispatch(pAlert(
t`Error :(`,
t`Can not import more than 20 or no template!`,
'error',
));
return;
}
const bufferList = await Promise.all(
tDataList.map((z) => base64ToBuffer(z.buffer)),
);
const fileList = [];
for (let i = 0; i < tDataList.length; i += 1) {
const { mimetype } = tDataList[i];
console.log('mimetype', mimetype, 'buffer', bufferList[i]);
fileList.push(new Blob([bufferList[i]], { type: mimetype }));
}
const { list } = this.#store.getState().templates;

View File

@ -5,8 +5,6 @@
const CURRENT_VERSION = 1;
const DB_NAME = 'ppfun_files';
// TODO make sure we get sane errors on reject()
class FileStorage {
type;
static db;
@ -28,10 +26,10 @@ class FileStorage {
const request = window.indexedDB.open(DB_NAME, CURRENT_VERSION);
request.onsuccess = (event) => {
console.log('Successfully opened indexedDB');
const db = event.target.result;
db.onerror = (evt) => {
// eslint-disable-next-line no-console
console.error('indexedDB error:', evt.target.error);
};
@ -48,6 +46,7 @@ class FileStorage {
};
request.onerror = () => {
// eslint-disable-next-line no-console
console.error('Error on opening indexedDB:', request.error);
reject(request.error);
};
@ -61,25 +60,21 @@ class FileStorage {
}
const fileArray = Array.isArray(files) ? files : [files];
const buffers = await Promise.all(fileArray.map((f) => f.arrayBuffer()));
console.log('buffers', buffers);
return new Promise((resolve, reject) => {
const result = [];
const transaction = db.transaction('files', 'readwrite');
transaction.oncomplete = () => {
console.log('Success on saving files to indexedDB', result);
resolve(Array.isArray(files) ? result : result[0]);
};
transaction.onabort = (event) => {
event.stopPropagation();
console.log('Saving files to indexedDB aborted:', event, result);
reject(event.target.error);
};
const os = transaction.objectStore('files');
fileArray.forEach((file, index) => {
console.log('type', this.type, 'mime', file.type, 'buffer', buffers[index], 'file', file);
result.push(null);
os.add({
type: this.type,
@ -103,7 +98,6 @@ class FileStorage {
transaction.onabort = (event) => {
event.stopPropagation();
console.log('Saving files to indexedDB aborted:', event);
reject(event.target.error);
};
@ -128,12 +122,10 @@ class FileStorage {
const transaction = db.transaction('files', 'readonly');
transaction.oncomplete = () => {
console.log('Success on loading file', result);
resolve(Array.isArray(ids) ? result : result[0]);
};
transaction.onabort = (event) => {
event.stopPropagation();
console.log('Loading file from indexedDB aborted:', event.target.error);
reject(event.target.error);
};
@ -147,6 +139,7 @@ class FileStorage {
});
}
// eslint-disable-next-line class-methods-use-this
deleteFile(ids) {
const { db } = FileStorage;
if (!db) {
@ -157,15 +150,11 @@ class FileStorage {
return new Promise((resolve, reject) => {
const transaction = db.transaction('files', 'readwrite');
transaction.oncomplete = (event) => {
console.log(
`Successfully deleted ${indicesArray.length} files from indexedDB`,
);
transaction.oncomplete = () => {
resolve();
};
transaction.onabort = (event) => {
event.stopPropagation();
console.log('Saving files to indexedDB aborted:', event);
reject(event.target.error);
};
@ -188,12 +177,10 @@ class FileStorage {
.getAllKeys(this.type);
request.onsuccess = (event) => {
console.log('got all keys', event.target.result);
resolve(event.target.result);
};
transaction.onabort = (event) => {
event.stopPropagation();
console.log('GetAllKeys aborted:', event.target);
reject(event.target.error);
};
});
@ -208,11 +195,13 @@ class FileStorage {
const allKeys = await this.getAllKeys();
const toDelete = allKeys.filter((i) => !ids.includes(i));
if (toDelete.length) {
// eslint-disable-next-line no-console
console.log('Templaes: Keys in db but not in store', toDelete);
await this.deleteFile(toDelete);
}
const deadIds = ids.filter((i) => !allKeys.includes(i));
if (deadIds.length) {
// eslint-disable-next-line no-console
console.log('Templates: Keys in store but not in db', deadIds);
}
return deadIds;