-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from 0x04/refactor-store
Refactor store
- Loading branch information
Showing
25 changed files
with
656 additions
and
403 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,14 @@ | ||
import './style.css' | ||
import { Paint } from './src/components/paint.mjs' | ||
import * as stores from './src/stores/stores.mjs' | ||
|
||
import { PersistentStore } from './src/classes/persistent-store.mjs' | ||
|
||
window.PersistentStore = PersistentStore | ||
|
||
const paint = new Paint( | ||
document.getElementById('emojiPaint'), | ||
stores | ||
) | ||
|
||
const paint = new Paint(document.getElementById('emojiPaint')) | ||
paint.setup() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
export class Collection extends Array { | ||
constructor(identifier, entries = []) { | ||
super(...entries) | ||
this.identifier = identifier | ||
} | ||
|
||
has(identifierValue) { | ||
return (this.findIndex( | ||
value => value[this.identifier] === identifierValue | ||
) > -1) | ||
} | ||
|
||
get(identifierValue) { | ||
return this.find(value => value[this.identifier] === identifierValue) | ||
} | ||
|
||
set(identifierValue, entry) { | ||
const index = this.findIndex( | ||
value => value[this.identifier] === identifierValue | ||
) | ||
|
||
if (index === -1) { | ||
this.push({ [this.identifier]: identifierValue, ...entry }) | ||
return | ||
} | ||
|
||
this[index] = { ...this[index], ...entry } | ||
} | ||
|
||
merge(entries) { | ||
for (const entry of entries) { | ||
this.set(entry[this.identifier], entry) | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import { Store } from './store.mjs' | ||
import { Collection } from './collection.js' | ||
|
||
export class PersistentStore extends Store { | ||
/** | ||
* @type {string} | ||
*/ | ||
storageKey = null | ||
/** | ||
* @type {Set<string>} | ||
*/ | ||
stateChanges = new Set() | ||
|
||
constructor(initialState, storageKey) { | ||
super(initialState) | ||
this.storageKey = storageKey | ||
} | ||
|
||
setState(newState) { | ||
super.setState(newState) | ||
|
||
this.stateChanges = new Set([ | ||
...this.stateChanges, | ||
...Object.keys(newState) | ||
]) | ||
} | ||
|
||
read() { | ||
const [ storageKey, stateKey ] = this.storageKey.split('.') | ||
const storage = JSON.parse(localStorage.getItem(storageKey)) | ||
|
||
if (!storage || !storage[stateKey]) { | ||
return | ||
} | ||
|
||
this.setState(this.merge(storage[stateKey])) | ||
} | ||
|
||
write(newStoredState = structuredClone(this.state)) { | ||
const [ storageKey, stateKey ] = this.storageKey.split('.') | ||
const storageData = JSON.parse(localStorage.getItem(storageKey)) ?? {} | ||
|
||
if (Object.keys(newStoredState).length === 0 && !this.stateChanges.size) { | ||
return | ||
} | ||
|
||
for (const statePropertyKey of this.stateChanges) { | ||
newStoredState[statePropertyKey] = this.state[statePropertyKey] | ||
} | ||
|
||
storageData[stateKey] = newStoredState | ||
|
||
localStorage.setItem(storageKey, JSON.stringify(storageData)) | ||
|
||
this.stateChanges = new Set() | ||
} | ||
|
||
merge(storageState) { | ||
const mergedState = this.clone(this.state) | ||
|
||
for (const key in mergedState) { | ||
if (!(key in storageState)) { | ||
continue | ||
} | ||
|
||
switch (Object.getPrototypeOf(mergedState[key]).constructor) { | ||
case Object: | ||
mergedState[key] = { ...mergedState[key], ...storageState[key] } | ||
break | ||
|
||
case Collection: | ||
mergedState[key].merge(storageState[key]) | ||
break | ||
|
||
default: | ||
mergedState[key] = storageState[key] | ||
break | ||
} | ||
} | ||
|
||
return mergedState | ||
} | ||
|
||
clone() { | ||
const clonedState = structuredClone(this.state) | ||
const initialState = this.constructor.initialState | ||
|
||
// Restore right data type after `structuredClone(…)` | ||
for (const key in clonedState) { | ||
if (key in initialState && initialState[key]) { | ||
const initialEntry = initialState[key] | ||
const initialEntryClass = Object.getPrototypeOf(initialEntry).constructor | ||
|
||
switch (initialEntryClass) { | ||
case Collection: | ||
clonedState[key] = new Collection( | ||
initialEntry.identifier, | ||
clonedState[key] | ||
) | ||
break | ||
} | ||
} | ||
} | ||
|
||
return clonedState | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,11 @@ | ||
export class Point { | ||
/** | ||
* @type {number} | ||
*/ | ||
x = 0 | ||
/** | ||
* @type {number} | ||
*/ | ||
y = 0 | ||
|
||
constructor(x = 0, y = 0) { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
export class Store { | ||
/** | ||
* @type {Object} | ||
*/ | ||
state = null | ||
/** | ||
* @type {Array} | ||
*/ | ||
listeners = null | ||
|
||
constructor(initialState = {}) { | ||
this.state = structuredClone(initialState) | ||
this.listeners = [] | ||
} | ||
|
||
getState() { | ||
return this.state | ||
} | ||
|
||
setState(newState) { | ||
this.state = { ...this.state, ...newState } | ||
this.emit() | ||
} | ||
|
||
subscribe(listener) { | ||
this.listeners.push(listener) | ||
listener(this.getState(), this) | ||
} | ||
|
||
emit() { | ||
this.listeners.forEach( | ||
(listener) => listener(this.state, this) | ||
) | ||
} | ||
} |
Oops, something went wrong.