-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add persistent state provider (#1830)
* Add persistent state provider * Catch write error * Handle read errors, update error msgs * Fix lint * Don't provide initial state to loader * Remove colorMode from shell state * Idea: hook into persisted context from other files * Migrate settings to new hook * Rework persisted state to split individual contexts * Tweak persisted schema and validation --------- Co-authored-by: Paul Frazee <[email protected]>
- Loading branch information
1 parent
bfe196b
commit 96d8faf
Showing
13 changed files
with
465 additions
and
74 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
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
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,6 @@ | ||
export default class BroadcastChannel { | ||
constructor(public name: string) {} | ||
postMessage(_data: any) {} | ||
close() {} | ||
onmessage: (event: MessageEvent) => void = () => {} | ||
} |
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 @@ | ||
export default BroadcastChannel |
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,91 @@ | ||
import EventEmitter from 'eventemitter3' | ||
import {logger} from '#/logger' | ||
import {defaults, Schema} from '#/state/persisted/schema' | ||
import {migrate} from '#/state/persisted/legacy' | ||
import * as store from '#/state/persisted/store' | ||
import BroadcastChannel from '#/state/persisted/broadcast' | ||
|
||
export type {Schema} from '#/state/persisted/schema' | ||
export {defaults as schema} from '#/state/persisted/schema' | ||
|
||
const broadcast = new BroadcastChannel('BSKY_BROADCAST_CHANNEL') | ||
const UPDATE_EVENT = 'BSKY_UPDATE' | ||
|
||
let _state: Schema = defaults | ||
const _emitter = new EventEmitter() | ||
|
||
/** | ||
* Initializes and returns persisted data state, so that it can be passed to | ||
* the Provider. | ||
*/ | ||
export async function init() { | ||
logger.debug('persisted state: initializing') | ||
|
||
broadcast.onmessage = onBroadcastMessage | ||
|
||
try { | ||
await migrate() // migrate old store | ||
const stored = await store.read() // check for new store | ||
if (!stored) await store.write(defaults) // opt: init new store | ||
_state = stored || defaults // return new store | ||
} catch (e) { | ||
logger.error('persisted state: failed to load root state from storage', { | ||
error: e, | ||
}) | ||
// AsyncStorage failured, but we can still continue in memory | ||
return defaults | ||
} | ||
} | ||
|
||
export function get<K extends keyof Schema>(key: K): Schema[K] { | ||
return _state[key] | ||
} | ||
|
||
export async function write<K extends keyof Schema>( | ||
key: K, | ||
value: Schema[K], | ||
): Promise<void> { | ||
try { | ||
_state[key] = value | ||
await store.write(_state) | ||
// must happen on next tick, otherwise the tab will read stale storage data | ||
setTimeout(() => broadcast.postMessage({event: UPDATE_EVENT}), 0) | ||
logger.debug(`persisted state: wrote root state to storage`) | ||
} catch (e) { | ||
logger.error(`persisted state: failed writing root state to storage`, { | ||
error: e, | ||
}) | ||
} | ||
} | ||
|
||
export function onUpdate(cb: () => void): () => void { | ||
_emitter.addListener('update', cb) | ||
return () => _emitter.removeListener('update', cb) | ||
} | ||
|
||
async function onBroadcastMessage({data}: MessageEvent) { | ||
// validate event | ||
if (typeof data === 'object' && data.event === UPDATE_EVENT) { | ||
try { | ||
// read next state, possibly updated by another tab | ||
const next = await store.read() | ||
|
||
if (next) { | ||
logger.debug(`persisted state: handling update from broadcast channel`) | ||
_state = next | ||
_emitter.emit('update') | ||
} else { | ||
logger.error( | ||
`persisted state: handled update update from broadcast channel, but found no data`, | ||
) | ||
} | ||
} catch (e) { | ||
logger.error( | ||
`persisted state: failed handling update from broadcast channel`, | ||
{ | ||
error: e, | ||
}, | ||
) | ||
} | ||
} | ||
} |
Oops, something went wrong.