Skip to content

Commit

Permalink
Merge pull request #1 from 0x04/refactor-store
Browse files Browse the repository at this point in the history
Refactor store
  • Loading branch information
0x04 authored Jan 17, 2025
2 parents f07095a + 0891dad commit 9b256b0
Show file tree
Hide file tree
Showing 25 changed files with 656 additions and 403 deletions.
11 changes: 10 additions & 1 deletion main.mjs
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()
35 changes: 35 additions & 0 deletions src/classes/collection.js
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)
}
}
}
43 changes: 0 additions & 43 deletions src/classes/data-storage.mjs

This file was deleted.

83 changes: 0 additions & 83 deletions src/classes/data.mjs

This file was deleted.

6 changes: 6 additions & 0 deletions src/classes/mouse-grid.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Point } from './point.mjs'

export class MouseGrid {
/**
* @type {Rectangle}
*/
gridRect = null
/**
* @type {Rectangle}
*/
cellRect = null

constructor(gridRect = null, cellRect = null) {
Expand Down
107 changes: 107 additions & 0 deletions src/classes/persistent-store.mjs
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
}
}
6 changes: 6 additions & 0 deletions src/classes/point.mjs
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) {
Expand Down
6 changes: 6 additions & 0 deletions src/classes/rectangle.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Point } from './point.mjs'

export class Rectangle extends Point {
/**
* @type {number}
*/
width = 0
/**
* @type {number}
*/
height = 0

constructor(x = 0, y = 0, width = 0, height = 0) {
Expand Down
35 changes: 35 additions & 0 deletions src/classes/store.mjs
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)
)
}
}
Loading

0 comments on commit 9b256b0

Please sign in to comment.