Add storage change propagation for same-window States
The browser's default storage event only triggers when a change happens within another window, meaning that different StorageStates sharing the same storage object would not be informed of each other's updates.
This commit is contained in:
parent
80b88ec647
commit
24f7cffa82
2 changed files with 29 additions and 14 deletions
|
@ -99,11 +99,9 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This comes with a series of limitations:
|
This comes with the disadvantage that all values must be stored in a
|
||||||
<ul>
|
serialised form, which won't work for all data and will break identity.
|
||||||
<li>Two <code>StorageState</code>s backed by the same <code>Storage</code> object won't be notified of each other's changes.</li>
|
Specifically, all values are converted to JSON strings for storage.
|
||||||
<li>As only strings can be stored, the library converts all values to and from JSON.</li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
35
state.js
35
state.js
|
@ -87,6 +87,16 @@ export class State extends EventTarget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class StorageChangeEvent extends Event {
|
||||||
|
constructor(storage, key, value, targetState) {
|
||||||
|
super("storagechange")
|
||||||
|
this.storageArea = storage
|
||||||
|
this.key = key
|
||||||
|
this.newValue = value
|
||||||
|
this.targetState = targetState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class StoredState extends State {
|
export class StoredState extends State {
|
||||||
#storage
|
#storage
|
||||||
#valueKey
|
#valueKey
|
||||||
|
@ -97,9 +107,10 @@ export class StoredState extends State {
|
||||||
this.#valueKey = options.key ?? 'value'
|
this.#valueKey = options.key ?? 'value'
|
||||||
|
|
||||||
// Initialise storage from defaults
|
// Initialise storage from defaults
|
||||||
for (const [key, value] of Object.entries(init)) {
|
for (let [prop, value] of Object.entries(init)) {
|
||||||
if (this.#storage[key] == undefined)
|
if (prop === this.#valueKey) prop = 'value'
|
||||||
this.set(key, value)
|
if (this.#storage[prop] == undefined)
|
||||||
|
this.set(prop, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit change events for any changed keys
|
// Emit change events for any changed keys
|
||||||
|
@ -111,16 +122,22 @@ export class StoredState extends State {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen for changes from other windows
|
// Listen for changes from other windows
|
||||||
addEventListener("storage", event => {
|
const handler = event => {
|
||||||
let prop = event.key
|
if (event.targetState !== this && event.storageArea == this.#storage) {
|
||||||
if (prop === this.#valueKey) prop = 'value'
|
let prop = event.key
|
||||||
this.emit(prop, JSON.parse(event.newValue))
|
if (prop === this.#valueKey) prop = 'value'
|
||||||
})
|
this.emit(prop, JSON.parse(event.newValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addEventListener("storage", handler)
|
||||||
|
addEventListener("storagechange", handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
set(prop, value) {
|
set(prop, value) {
|
||||||
if (prop == "value") prop = this.#valueKey
|
if (prop == "value") prop = this.#valueKey
|
||||||
this.#storage[prop] = JSON.stringify(value)
|
const json = JSON.stringify(value)
|
||||||
|
dispatchEvent(new StorageChangeEvent(this.#storage, prop, json, this))
|
||||||
|
this.#storage[prop] = json
|
||||||
}
|
}
|
||||||
|
|
||||||
get(prop) {
|
get(prop) {
|
||||||
|
|
Loading…
Reference in a new issue