-
Notifications
You must be signed in to change notification settings - Fork 37
/
environment.ts
146 lines (121 loc) · 3.7 KB
/
environment.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import type { ClientOptions } from '@logux/client'
import { MemoryStore } from '@logux/core'
import type { TranslationLoader } from '@nanostores/i18n'
import {
type PersistentEvents,
type PersistentStore,
setPersistentEngine
} from '@nanostores/persistent'
import { atom, type ReadableAtom, type StoreValue } from 'nanostores'
import type { BaseRoute, BaseRouter, Route, Routes } from './router.ts'
interface LogStoreCreator {
(): ClientOptions['store']
}
export type NetworkType = 'free' | 'paid' | 'unknown' | undefined
export interface NetworkTypeDetector {
(): {
saveData: boolean | undefined
type: NetworkType
}
}
type NormalizeParams<Params> = {
[K in keyof Params]: Params[K] extends number ? Params[K] : string
}
type RouterRoutes<Router extends BaseRouter> = {
[R in Exclude<StoreValue<Router>, undefined> as R['route']]: R['params']
}
type ExactType<Good, A, B> = A extends B ? (B extends A ? Good : never) : never
type ValidateRouter<Router extends BaseRouter> = ExactType<
Router,
NormalizeParams<RouterRoutes<Router>>,
NormalizeParams<Routes>
>
interface EnvironmentListener {
(env: Environment): (() => void)[] | (() => void) | void
}
interface ErrorEvents {
addEventListener(
event: 'unhandledrejection',
listener: (event: { reason: unknown }) => void
): void
}
export interface Environment {
baseRouter: BaseRouter
errorEvents: ErrorEvents
locale: ReadableAtom<string>
logStoreCreator: LogStoreCreator
networkType: NetworkTypeDetector
openRoute(page: Route): void
restartApp: () => void
translationLoader: TranslationLoader
}
export type EnvironmentAndStore = {
persistentEvents: PersistentEvents
persistentStore: PersistentStore
} & Environment
let currentEnvironment: Environment | undefined
let listeners: EnvironmentListener[] = []
let unbinds: ((() => void) | void)[] = []
function runEnvListener(listener: EnvironmentListener): void {
let unbind = listener(currentEnvironment!)
if (Array.isArray(unbind)) {
unbinds.push(...unbind)
} else {
unbinds.push(unbind)
}
}
export function onEnvironment(cb: EnvironmentListener): void {
if (currentEnvironment) {
runEnvListener(cb)
}
listeners.push(cb)
}
export function setupEnvironment<Router extends BaseRouter>(
env: {
baseRouter: ValidateRouter<Router>
} & EnvironmentAndStore
): void {
for (let unbind of unbinds) unbind?.()
setPersistentEngine(env.persistentStore, env.persistentEvents)
currentEnvironment = {
baseRouter: env.baseRouter,
errorEvents: env.errorEvents,
locale: env.locale,
logStoreCreator: env.logStoreCreator,
networkType: env.networkType,
openRoute: env.openRoute,
restartApp: env.restartApp,
translationLoader: env.translationLoader
}
for (let listener of listeners) {
runEnvListener(listener)
}
}
export function getEnvironment(): Environment {
if (!currentEnvironment) {
throw new Error('No Slow Reader environment')
}
return currentEnvironment
}
export const isMobile = atom<boolean | undefined>()
export function setIsMobile(isSmallScreen: boolean): void {
isMobile.set(isSmallScreen)
}
const testRouter = atom<BaseRoute | undefined>()
export function setBaseTestRoute(route: BaseRoute | undefined): void {
testRouter.set(route)
}
export function getTestEnvironment(): EnvironmentAndStore {
return {
baseRouter: testRouter,
errorEvents: { addEventListener() {} },
locale: atom('en'),
logStoreCreator: () => new MemoryStore(),
networkType: () => ({ saveData: undefined, type: undefined }),
openRoute: setBaseTestRoute,
persistentEvents: { addEventListener() {}, removeEventListener() {} },
persistentStore: {},
restartApp: () => {},
translationLoader: async () => ({})
}
}