Skip to content

Commit

Permalink
Merge pull request #46 from Timendus/refactorings
Browse files Browse the repository at this point in the history
Refactorings
  • Loading branch information
Timendus authored Feb 26, 2024
2 parents fac467e + 6026ac9 commit bb623a4
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 49 deletions.
15 changes: 5 additions & 10 deletions docs/js/app/board-renderer.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
import PlayMode from "./model/play-mode.js";

export default class BoardRenderer {
constructor(element, board) {
constructor(element) {
this._element = element;
this._board = board;
}

set board(board) {
this._board = board || this._board;
}

render() {
render(board) {
let html = "";
for (let y = 0; y < this._board.rows; y++) {
for (let y = 0; y < board.rows; y++) {
html += `<div class='row'>`;
for (let x = 0; x < this._board.cols; x++) {
let sound = this._board.getSound(x, y);
for (let x = 0; x < board.cols; x++) {
let sound = board.getSound(x, y);
let title, artist, colour, playMode;

if (sound) {
Expand Down
74 changes: 35 additions & 39 deletions docs/js/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,34 @@ import Midi from "./util/midi.js";
import Keyboard from "./util/keyboard.js";
import BoardRenderer from "./board-renderer.js";
import IndexedDB from "./util/indexedDB.js";
import files from "./util/files.js";
import "./lib/thimbleful.js";
import "./util/pwa.js";

/* Initialize all the bits and pieces */

const database = await IndexedDB.connect("soundboard");
let board = await findOrCreateBoard();
const boardRenderer = new BoardRenderer(document.getElementById("board"), board);
const boardRenderer = new BoardRenderer(document.getElementById("board"));
const clickHandler = Thimbleful.Click.instance();
const dragDrop = Thimbleful.FileTarget.instance();
const midi = new Midi();
const keyboard = new Keyboard();
let volume = 1;

const fileTypes = [
{
description: "Soundboard save file",
accept: {
"text/plain": [".soundboard"],
},
},
];

/* Render the board to the DOM */

board.resizeIfEmpty(...rowsAndCols());
boardRenderer.render();
boardRenderer.render(board);

/* Register all event handlers */

Expand All @@ -44,7 +54,7 @@ dragDrop.register(".sound:not(.loaded)", (file, data, e) => {
// Rerender the board (I think the timeout had something to do with the drag
// and drop stuff not removing the hover class otherwise? Not sure anymore.)
window.setTimeout(() => {
boardRenderer.render();
boardRenderer.render(board);
}, 10);
});

Expand Down Expand Up @@ -74,37 +84,47 @@ clickHandler.register("button#clear", {
board.allSounds().forEach((s) => s.destroy());
board = new Board();
board.resizeIfEmpty(...rowsAndCols());
boardRenderer.board = board;
boardRenderer.render();
boardRenderer.render(board);
database.removeItem("autosave");
},
});
clickHandler.register("button#load", {
click: async () => {
const newBoard = Board.fromStorageObject(JSON.parse(await upload()));
const newBoard = Board.fromStorageObject(
JSON.parse(
await files.load({
types: fileTypes,
startIn: "music",
}),
),
);
board.allSounds().forEach((s) => s.destroy());
board = newBoard;
boardRenderer.board = board;
boardRenderer.render();
boardRenderer.render(board);
document.querySelector("body").classList.remove("settings");
saveBoard("loaded save file");
},
});
clickHandler.register("button#save", {
click: () => {
download("soundboard.json", JSON.stringify(board.toStorageObject()));
files.save({
suggestedName: "Untitled.soundboard",
contents: JSON.stringify(board.toStorageObject()),
types: fileTypes,
startIn: "music",
});
},
});
clickHandler.register("button#add-row", {
click: () => {
board.addRow();
boardRenderer.render();
boardRenderer.render(board);
},
});
clickHandler.register("button#add-col", {
click: () => {
board.addColumn();
boardRenderer.render();
boardRenderer.render(board);
},
});
clickHandler.register("button#settings", {
Expand Down Expand Up @@ -150,7 +170,7 @@ clickHandler.register("button.assign-key", { click: (e) => captureKey(e) });
// Resize the soundboard when resizing the window
window.addEventListener("resize", () => {
board.resizeIfEmpty(...rowsAndCols());
boardRenderer.render();
boardRenderer.render(board);
});

/* Helper functions */
Expand Down Expand Up @@ -178,30 +198,6 @@ async function saveBoard(reason) {
console.info("💾 Saved board to IndexedDB because:", reason);
}

// Push a download to the user ("Save as")
function download(filename, contents) {
if (!filename || !contents) return;
const anchor = document.createElement("a");
anchor.download = filename;
anchor.href = "data:text/plain;charset=utf-8," + encodeURIComponent(contents);
anchor.click();
}

// Get an upload from the user ("Open file")
async function upload() {
return new Promise((resolve, reject) => {
const input = document.createElement("input");
input.type = "file";
input.addEventListener("change", (c) => {
if (c.target.files.length != 1) reject("No file selected");
const reader = new FileReader();
reader.addEventListener("load", (e) => resolve(e.target.result));
reader.readAsText(c.target.files[0]);
});
input.click();
});
}

// Where did we click "in the grid"?
function _soundFromEvent(e) {
const soundElm = e.target.closest(".sound");
Expand All @@ -214,7 +210,7 @@ function trigger(e, redraw, callback) {
const [sound] = _soundFromEvent(e);
if (!sound) return;
callback(sound);
if (redraw) boardRenderer.render();
if (redraw) boardRenderer.render(board);
}

function keyTrigger(key, callback) {
Expand All @@ -233,7 +229,7 @@ function setColour(e) {
colourValue = window.getComputedStyle(e.target).getPropertyValue("background-color");
}
sound.colour = colourValue;
boardRenderer.render();
boardRenderer.render(board);
saveBoard("colour was changed");
}

Expand All @@ -251,7 +247,7 @@ function captureKey(e) {
.finally(() => {
keyboard.cancelGetKeyPress();
midi.cancelGetKeyPress();
boardRenderer.render();
boardRenderer.render(board);
saveBoard("key binding was changed");
});
}
110 changes: 110 additions & 0 deletions docs/js/app/util/files.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* Abstract away loading and saving of text files in the browser. The given save
* and load options are "best effort". If the new File System Access API is not
* available in the browser, it will still work, but basically ignore all your
* preferences.
*
* Usage:
*
* ```javascript
* import files from "files.js";
*
* await files.save({
* suggestedName: "Untitled.extension",
* contents: "I'm in your files",
* startIn: "documents",
* types: [{
* description: 'My very special file type',
* accept: {
* 'text/plain': ['.extension'],
* },
* }],
* });
*
* await files.load({
* startIn: "documents",
* types: [{
* description: 'My very special file type',
* accept: {
* 'text/plain': ['.extension'],
* },
* }],
* });
* ```
*/

/**
* See types here:
* https://developer.mozilla.org/en-US/docs/Web/API/Window/showSaveFilePicker
* @typedef {Object} FileTypes
* @property {string} description What these files are known as
* @property {Object} accept The accepted files types and associated extensions
*/

/**
* @typedef {Object} SaveOptions
* @property {string} suggestedName The name to save the file as
* @property {string} contents What should be stored in the file
* @property {FileTypes[]} types Which file types are supported
* @property {("desktop"|"document"|"downloads"|"music"|"pictures"|"videos")}
* startIn Where the file picker opens
*/

/**
* @typedef {Object} LoadOptions
* @property {FileTypes[]} types Which file types are supported
* @property {("desktop"|"document"|"downloads"|"music"|"pictures"|"videos")}
* startIn Where the file picker opens
*/

/**
* Push a download to the user ("Save as")
* @param {SaveOptions} options What to store, and where
*/
export async function save(options) {
if (!options.suggestedName || !options.contents) return;

if ("showSaveFilePicker" in window) {
const fileHandle = await window.showSaveFilePicker(options);
const writable = await fileHandle.createWritable();
await writable.write(options.contents);
await writable.close();
return;
}

// Fallback behaviour for browser that don't support the fancy new File System
// Access API
const anchor = document.createElement("a");
anchor.download = options.suggestedName;
anchor.href = "data:text/plain;charset=utf-8," + encodeURIComponent(options.contents);
anchor.click();
}

/**
* Get an upload from the user ("Open file")
* @param {LoadOptions} options What to open, and from where
* @returns {string} The contents of the opened file
*/
export async function load(options) {
if ("showOpenFilePicker" in window) {
const [fileHandle] = await window.showOpenFilePicker(options);
const file = await fileHandle.getFile();
return await file.text();
}

// Fallback behaviour for browser that don't support the fancy new File System
// Access API
return new Promise((resolve, reject) => {
const input = document.createElement("input");
input.type = "file";
input.addEventListener("change", (c) => {
if (c.target.files.length != 1) reject("No file selected");
const reader = new FileReader();
reader.addEventListener("load", (e) => resolve(e.target.result));
reader.readAsText(c.target.files[0]);
});
input.click();
});
}

export default { save, load };
1 change: 1 addition & 0 deletions docs/js/service-worker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const filesToCache = [
"js/app/model/mp3file.js",
"js/app/model/play-mode.js",
"js/app/model/sound.js",
"js/app/util/files.js",
"js/app/util/indexedDB.js",
"js/app/util/keyboard.js",
"js/app/util/midi.js",
Expand Down

0 comments on commit bb623a4

Please sign in to comment.