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

View File

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

View File

@ -2,6 +2,8 @@
* Settings for minimap / overlay * Settings for minimap / overlay
*/ */
/* eslint-disable react/no-array-index-key */
import React, { useState, useCallback, useRef } from 'react'; import React, { useState, useCallback, useRef } from 'react';
import { useSelector, useDispatch, shallowEqual } from 'react-redux'; import { useSelector, useDispatch, shallowEqual } from 'react-redux';
import fileDownload from 'js-file-download'; import fileDownload from 'js-file-download';
@ -37,7 +39,7 @@ const TemplateSettings = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const toggleEditing = useCallback((title) => { 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); const ind = editingIndices.indexOf(index);
setEditingIndices((ind === -1) setEditingIndices((ind === -1)
? [...editingIndices, index] ? [...editingIndices, index]
@ -45,13 +47,14 @@ const TemplateSettings = () => {
); );
}, [list, editingIndices]); }, [list, editingIndices]);
console.log('list', list);
return ( return (
<> <>
<h2>{t`Templates`}</h2> <h2>{t`Templates`}</h2>
<p> <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> </p>
<SettingsItem <SettingsItem
title={t`Enable Overlay`} title={t`Enable Overlay`}
@ -64,10 +67,12 @@ const TemplateSettings = () => {
<SettingsItem <SettingsItem
title={t`Small Pixels Overlay`} title={t`Small Pixels Overlay`}
value={oSmallPxls} value={oSmallPxls}
deactivated={!oVEnabled}
onToggle={() => dispatch(toggleSmallPxls())} 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> </SettingsItem>
<div className="setitem"> <div className="setitem">
@ -144,9 +149,11 @@ const TemplateSettings = () => {
className="modallink" className="modallink"
onClick={async () => { onClick={async () => {
const data = await templateLoader.exportEnabledTemplates(); const data = await templateLoader.exportEnabledTemplates();
fileDownload( if (data) {
JSON.stringify(data), 'PixelplanetTemplates.json', fileDownload(
); JSON.stringify(data), 'PixelplanetTemplates.json',
);
}
}} }}
>{t`Export enabled templates`}</span> >{t`Export enabled templates`}</span>
</React.Fragment> </React.Fragment>

View File

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

View File

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

View File

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

View File

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

View File

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