about summary refs log tree commit diff
path: root/src/state
diff options
context:
space:
mode:
Diffstat (limited to 'src/state')
-rw-r--r--src/state/env.ts27
-rw-r--r--src/state/index.ts30
-rw-r--r--src/state/models/root-store.ts16
-rw-r--r--src/state/storage.ts52
4 files changed, 125 insertions, 0 deletions
diff --git a/src/state/env.ts b/src/state/env.ts
new file mode 100644
index 000000000..90a2cab5e
--- /dev/null
+++ b/src/state/env.ts
@@ -0,0 +1,27 @@
+/**
+ * The environment is a place where services and shared dependencies between
+ * models live. They are made available to every model via dependency injection.
+ */
+
+import {getEnv, IStateTreeNode} from 'mobx-state-tree'
+
+export class Environment {
+  constructor() {}
+
+  async setup() {}
+}
+
+/**
+ * Extension to the MST models that adds the environment property.
+ * Usage:
+ *
+ *   .extend(withEnvironment)
+ *
+ */
+export const withEnvironment = (self: IStateTreeNode) => ({
+  views: {
+    get environment() {
+      return getEnv<Environment>(self)
+    },
+  },
+})
diff --git a/src/state/index.ts b/src/state/index.ts
new file mode 100644
index 000000000..7c97ce294
--- /dev/null
+++ b/src/state/index.ts
@@ -0,0 +1,30 @@
+import {onSnapshot} from 'mobx-state-tree'
+import {RootStoreModel, RootStore} from './models/root-store'
+import {Environment} from './env'
+import * as storage from './storage'
+
+const ROOT_STATE_STORAGE_KEY = 'root'
+
+export async function setupState() {
+  let rootStore: RootStore
+  let data: any
+
+  const env = new Environment()
+  try {
+    data = (await storage.load(ROOT_STATE_STORAGE_KEY)) || {}
+    rootStore = RootStoreModel.create(data, env)
+  } catch (e) {
+    console.error('Failed to load state from storage', e)
+    rootStore = RootStoreModel.create({}, env)
+  }
+
+  // track changes & save to storage
+  onSnapshot(rootStore, snapshot =>
+    storage.save(ROOT_STATE_STORAGE_KEY, snapshot),
+  )
+
+  return rootStore
+}
+
+export {useStores, RootStoreModel, RootStoreProvider} from './models/root-store'
+export type {RootStore} from './models/root-store'
diff --git a/src/state/models/root-store.ts b/src/state/models/root-store.ts
new file mode 100644
index 000000000..164dfcced
--- /dev/null
+++ b/src/state/models/root-store.ts
@@ -0,0 +1,16 @@
+/**
+ * The root store is the base of all modeled state.
+ */
+
+import {Instance, SnapshotOut, types} from 'mobx-state-tree'
+import {createContext, useContext} from 'react'
+
+export const RootStoreModel = types.model('RootStore').props({})
+
+export interface RootStore extends Instance<typeof RootStoreModel> {}
+export interface RootStoreSnapshot extends SnapshotOut<typeof RootStoreModel> {}
+
+// react context & hook utilities
+const RootStoreContext = createContext<RootStore>({} as RootStore)
+export const RootStoreProvider = RootStoreContext.Provider
+export const useStores = () => useContext(RootStoreContext)
diff --git a/src/state/storage.ts b/src/state/storage.ts
new file mode 100644
index 000000000..dc5fb620f
--- /dev/null
+++ b/src/state/storage.ts
@@ -0,0 +1,52 @@
+import AsyncStorage from '@react-native-async-storage/async-storage'
+
+export async function loadString(key: string): Promise<string | null> {
+  try {
+    return await AsyncStorage.getItem(key)
+  } catch {
+    // not sure why this would fail... even reading the RN docs I'm unclear
+    return null
+  }
+}
+
+export async function saveString(key: string, value: string): Promise<boolean> {
+  try {
+    await AsyncStorage.setItem(key, value)
+    return true
+  } catch {
+    return false
+  }
+}
+
+export async function load(key: string): Promise<any | null> {
+  try {
+    const str = await AsyncStorage.getItem(key)
+    if (typeof str !== 'string') {
+      return null
+    }
+    return JSON.parse(str)
+  } catch {
+    return null
+  }
+}
+
+export async function save(key: string, value: any): Promise<boolean> {
+  try {
+    await AsyncStorage.setItem(key, JSON.stringify(value))
+    return true
+  } catch {
+    return false
+  }
+}
+
+export async function remove(key: string): Promise<void> {
+  try {
+    await AsyncStorage.removeItem(key)
+  } catch {}
+}
+
+export async function clear(): Promise<void> {
+  try {
+    await AsyncStorage.clear()
+  } catch {}
+}