refactor: improve type checking for DispatchingStore dispatch payloads
This commit is contained in:
@@ -1,15 +1,15 @@
|
|||||||
import { Ref } from "vue"
|
import { Ref } from "vue"
|
||||||
import { settingsStore, SettingsType } from "~/newstore/settings"
|
import { settingsStore, SettingsDef } from "~/newstore/settings"
|
||||||
import { pluck, distinctUntilChanged } from "rxjs/operators"
|
import { pluck, distinctUntilChanged } from "rxjs/operators"
|
||||||
import { useStream, useStreamStatic } from "./stream"
|
import { useStream, useStreamStatic } from "./stream"
|
||||||
|
|
||||||
export function useSetting<K extends keyof SettingsType>(
|
export function useSetting<K extends keyof SettingsDef>(
|
||||||
settingKey: K
|
settingKey: K
|
||||||
): Ref<SettingsType[K]> {
|
): Ref<SettingsDef[K]> {
|
||||||
return useStream(
|
return useStream(
|
||||||
settingsStore.subject$.pipe(pluck(settingKey), distinctUntilChanged()),
|
settingsStore.subject$.pipe(pluck(settingKey), distinctUntilChanged()),
|
||||||
settingsStore.value[settingKey],
|
settingsStore.value[settingKey],
|
||||||
(value: SettingsType[K]) => {
|
(value: SettingsDef[K]) => {
|
||||||
settingsStore.dispatch({
|
settingsStore.dispatch({
|
||||||
dispatcher: "applySetting",
|
dispatcher: "applySetting",
|
||||||
payload: {
|
payload: {
|
||||||
@@ -25,13 +25,13 @@ export function useSetting<K extends keyof SettingsType>(
|
|||||||
* A static version (does not require component setup)
|
* A static version (does not require component setup)
|
||||||
* of `useSetting`
|
* of `useSetting`
|
||||||
*/
|
*/
|
||||||
export function useSettingStatic<K extends keyof SettingsType>(
|
export function useSettingStatic<K extends keyof SettingsDef>(
|
||||||
settingKey: K
|
settingKey: K
|
||||||
): [Ref<SettingsType[K]>, () => void] {
|
): [Ref<SettingsDef[K]>, () => void] {
|
||||||
return useStreamStatic(
|
return useStreamStatic(
|
||||||
settingsStore.subject$.pipe(pluck(settingKey), distinctUntilChanged()),
|
settingsStore.subject$.pipe(pluck(settingKey), distinctUntilChanged()),
|
||||||
settingsStore.value[settingKey],
|
settingsStore.value[settingKey],
|
||||||
(value: SettingsType[K]) => {
|
(value: SettingsDef[K]) => {
|
||||||
settingsStore.dispatch({
|
settingsStore.dispatch({
|
||||||
dispatcher: "applySetting",
|
dispatcher: "applySetting",
|
||||||
payload: {
|
payload: {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
setDoc,
|
setDoc,
|
||||||
} from "firebase/firestore"
|
} from "firebase/firestore"
|
||||||
import { platform } from "~/platform"
|
import { platform } from "~/platform"
|
||||||
import { applySetting, settingsStore, SettingsType } from "~/newstore/settings"
|
import { applySetting, settingsStore, SettingsDef } from "~/newstore/settings"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used locally to prevent infinite loop when settings sync update
|
* Used locally to prevent infinite loop when settings sync update
|
||||||
@@ -59,7 +59,7 @@ export function initSettings() {
|
|||||||
} else {
|
} else {
|
||||||
writeSettings(
|
writeSettings(
|
||||||
dispatch.payload.settingKey,
|
dispatch.payload.settingKey,
|
||||||
settingsStore.value[dispatch.payload.settingKey as keyof SettingsType]
|
settingsStore.value[dispatch.payload.settingKey as keyof SettingsDef]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import { Subject, BehaviorSubject } from "rxjs"
|
|||||||
import { map } from "rxjs/operators"
|
import { map } from "rxjs/operators"
|
||||||
import { assign, clone } from "lodash-es"
|
import { assign, clone } from "lodash-es"
|
||||||
|
|
||||||
type dispatcherFunc<StoreType> = (
|
type DispatcherFunc<StoreType, PayloadType> = (
|
||||||
currentVal: StoreType,
|
currentVal: StoreType,
|
||||||
payload: any
|
payload: PayloadType
|
||||||
) => Partial<StoreType>
|
) => Partial<StoreType>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,26 +13,32 @@ type dispatcherFunc<StoreType> = (
|
|||||||
* This function exists to provide better typing for dispatch function.
|
* This function exists to provide better typing for dispatch function.
|
||||||
* As you can see, its pretty much an identity function.
|
* As you can see, its pretty much an identity function.
|
||||||
*/
|
*/
|
||||||
export const defineDispatchers = <StoreType, T>(
|
export const defineDispatchers = <
|
||||||
|
StoreType,
|
||||||
|
T extends { [x: string]: DispatcherFunc<StoreType, any> }
|
||||||
|
>(
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
dispatchers: { [_ in keyof T]: dispatcherFunc<StoreType> }
|
dispatchers: T
|
||||||
) => dispatchers
|
) => dispatchers
|
||||||
|
|
||||||
type Dispatch<
|
type Dispatch<
|
||||||
StoreType,
|
StoreType,
|
||||||
DispatchersType extends Record<string, dispatcherFunc<StoreType>>
|
DispatchersType extends { [x: string]: DispatcherFunc<StoreType, any> },
|
||||||
|
Dispatcher extends keyof DispatchersType
|
||||||
> = {
|
> = {
|
||||||
dispatcher: keyof DispatchersType
|
dispatcher: Dispatcher
|
||||||
payload: any
|
payload: Parameters<DispatchersType[Dispatcher]>[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class DispatchingStore<
|
export default class DispatchingStore<
|
||||||
StoreType,
|
StoreType,
|
||||||
DispatchersType extends Record<string, dispatcherFunc<StoreType>>
|
DispatchersType extends { [x: string]: DispatcherFunc<StoreType, any> }
|
||||||
> {
|
> {
|
||||||
#state$: BehaviorSubject<StoreType>
|
#state$: BehaviorSubject<StoreType>
|
||||||
#dispatchers: DispatchersType
|
#dispatchers: DispatchersType
|
||||||
#dispatches$: Subject<Dispatch<StoreType, DispatchersType>> = new Subject()
|
#dispatches$: Subject<
|
||||||
|
Dispatch<StoreType, DispatchersType, keyof DispatchersType>
|
||||||
|
> = new Subject()
|
||||||
|
|
||||||
constructor(initialValue: StoreType, dispatchers: DispatchersType) {
|
constructor(initialValue: StoreType, dispatchers: DispatchersType) {
|
||||||
this.#state$ = new BehaviorSubject(initialValue)
|
this.#state$ = new BehaviorSubject(initialValue)
|
||||||
@@ -64,9 +70,12 @@ export default class DispatchingStore<
|
|||||||
return this.#dispatches$
|
return this.#dispatches$
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch({ dispatcher, payload }: Dispatch<StoreType, DispatchersType>) {
|
dispatch<Dispatcher extends keyof DispatchersType>({
|
||||||
|
dispatcher,
|
||||||
|
payload,
|
||||||
|
}: Dispatch<StoreType, DispatchersType, Dispatcher>) {
|
||||||
if (!this.#dispatchers[dispatcher])
|
if (!this.#dispatchers[dispatcher])
|
||||||
throw new Error(`Undefined dispatch type '${dispatcher}'`)
|
throw new Error(`Undefined dispatch type '${String(dispatcher)}'`)
|
||||||
|
|
||||||
this.#dispatches$.next({ dispatcher, payload })
|
this.#dispatches$.next({ dispatcher, payload })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ const dispatchers = defineDispatchers({
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deleteAllParams(curr: RESTSession) {
|
deleteAllParams(curr: RESTSession, {}) {
|
||||||
return {
|
return {
|
||||||
request: {
|
request: {
|
||||||
...curr.request,
|
...curr.request,
|
||||||
@@ -168,7 +168,7 @@ const dispatchers = defineDispatchers({
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deleteAllHeaders(curr: RESTSession) {
|
deleteAllHeaders(curr: RESTSession, {}) {
|
||||||
return {
|
return {
|
||||||
request: {
|
request: {
|
||||||
...curr.request,
|
...curr.request,
|
||||||
@@ -257,7 +257,7 @@ const dispatchers = defineDispatchers({
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deleteAllFormDataEntries(curr: RESTSession) {
|
deleteAllFormDataEntries(curr: RESTSession, {}) {
|
||||||
// Only perform update if the current content-type is formdata
|
// Only perform update if the current content-type is formdata
|
||||||
if (curr.request.body.contentType !== "multipart/form-data") return {}
|
if (curr.request.body.contentType !== "multipart/form-data") return {}
|
||||||
|
|
||||||
@@ -288,7 +288,7 @@ const dispatchers = defineDispatchers({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
clearResponse(_curr: RESTSession) {
|
clearResponse(_curr: RESTSession, {}) {
|
||||||
return {
|
return {
|
||||||
response: null,
|
response: null,
|
||||||
}
|
}
|
||||||
@@ -490,7 +490,7 @@ export function setRESTTestScript(newScript: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setRESTReqBody(newBody: HoppRESTReqBody | null) {
|
export function setRESTReqBody(newBody: HoppRESTReqBody) {
|
||||||
restSessionStore.dispatch({
|
restSessionStore.dispatch({
|
||||||
dispatcher: "setRequestBody",
|
dispatcher: "setRequestBody",
|
||||||
payload: {
|
payload: {
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export function setSIOEndpoint(newEndpoint: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setSIOVersion(newVersion: string) {
|
export function setSIOVersion(newVersion: SIOClientVersion) {
|
||||||
SIOSessionStore.dispatch({
|
SIOSessionStore.dispatch({
|
||||||
dispatcher: "setVersion",
|
dispatcher: "setVersion",
|
||||||
payload: {
|
payload: {
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ const dispatchers = defineDispatchers({
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deleteAllProtocols(curr: HoppWSSession) {
|
deleteAllProtocols(curr: HoppWSSession, {}) {
|
||||||
return {
|
return {
|
||||||
request: {
|
request: {
|
||||||
endpoint: curr.request.endpoint,
|
endpoint: curr.request.endpoint,
|
||||||
|
|||||||
@@ -127,7 +127,13 @@ const restCollectionDispatchers = defineDispatchers({
|
|||||||
|
|
||||||
editFolder(
|
editFolder(
|
||||||
{ state }: RESTCollectionStoreType,
|
{ state }: RESTCollectionStoreType,
|
||||||
{ path, folder }: { path: string; folder: string }
|
{
|
||||||
|
path,
|
||||||
|
folder,
|
||||||
|
}: {
|
||||||
|
path: string
|
||||||
|
folder: HoppCollection<HoppRESTRequest>
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
const newState = state
|
const newState = state
|
||||||
|
|
||||||
@@ -393,7 +399,7 @@ const gqlCollectionDispatchers = defineDispatchers({
|
|||||||
|
|
||||||
editFolder(
|
editFolder(
|
||||||
{ state }: GraphqlCollectionStoreType,
|
{ state }: GraphqlCollectionStoreType,
|
||||||
{ path, folder }: { path: string; folder: string }
|
{ path, folder }: { path: string; folder: HoppCollection<HoppGQLRequest> }
|
||||||
) {
|
) {
|
||||||
const newState = state
|
const newState = state
|
||||||
|
|
||||||
|
|||||||
@@ -236,7 +236,8 @@ const dispatchers = defineDispatchers({
|
|||||||
globals: entries,
|
globals: entries,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearGlobalVariables() {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
clearGlobalVariables(_store, {}) {
|
||||||
return {
|
return {
|
||||||
globals: [],
|
globals: [],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,7 +141,8 @@ const RESTHistoryDispatchers = defineDispatchers({
|
|||||||
state: currentVal.state.filter((e) => !isEqual(e, entry)),
|
state: currentVal.state.filter((e) => !isEqual(e, entry)),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearHistory() {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
clearHistory(_, {}) {
|
||||||
return {
|
return {
|
||||||
state: [],
|
state: [],
|
||||||
}
|
}
|
||||||
@@ -189,7 +190,8 @@ const GQLHistoryDispatchers = defineDispatchers({
|
|||||||
state: currentVal.state.filter((e) => !isEqual(e, entry)),
|
state: currentVal.state.filter((e) => !isEqual(e, entry)),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearHistory() {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
clearHistory(_, {}) {
|
||||||
return {
|
return {
|
||||||
state: [],
|
state: [],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,18 @@ const defaultLocalState: LocalState = {
|
|||||||
REMEMBERED_TEAM_ID: undefined,
|
REMEMBERED_TEAM_ID: undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ApplyLocalState = {
|
||||||
|
[K in keyof LocalState]: {
|
||||||
|
key: K
|
||||||
|
value: LocalState[K]
|
||||||
|
}
|
||||||
|
}[keyof LocalState]
|
||||||
|
|
||||||
const dispatchers = defineDispatchers({
|
const dispatchers = defineDispatchers({
|
||||||
bulkApplyState(_currentState: LocalState, payload: Partial<LocalState>) {
|
bulkApplyState(_currentState: LocalState, payload: Partial<LocalState>) {
|
||||||
return payload
|
return payload
|
||||||
},
|
},
|
||||||
applyState<K extends keyof LocalState>(
|
applyState(_currentState: LocalState, { key, value }: ApplyLocalState) {
|
||||||
_currentState: LocalState,
|
|
||||||
{ key, value }: { key: K; value: LocalState[K] }
|
|
||||||
) {
|
|
||||||
const result: Partial<LocalState> = {
|
const result: Partial<LocalState> = {
|
||||||
[key]: value,
|
[key]: value,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const HoppFontSizes = ["small", "medium", "large"] as const
|
|||||||
|
|
||||||
export type HoppFontSize = (typeof HoppFontSizes)[number]
|
export type HoppFontSize = (typeof HoppFontSizes)[number]
|
||||||
|
|
||||||
export type SettingsType = {
|
export type SettingsDef = {
|
||||||
syncCollections: boolean
|
syncCollections: boolean
|
||||||
syncHistory: boolean
|
syncHistory: boolean
|
||||||
syncEnvironments: boolean
|
syncEnvironments: boolean
|
||||||
@@ -53,7 +53,7 @@ export type SettingsType = {
|
|||||||
COLUMN_LAYOUT: boolean
|
COLUMN_LAYOUT: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultSettings: SettingsType = {
|
export const defaultSettings: SettingsDef = {
|
||||||
syncCollections: true,
|
syncCollections: true,
|
||||||
syncHistory: true,
|
syncHistory: true,
|
||||||
syncEnvironments: true,
|
syncEnvironments: true,
|
||||||
@@ -79,18 +79,22 @@ export const defaultSettings: SettingsType = {
|
|||||||
COLUMN_LAYOUT: true,
|
COLUMN_LAYOUT: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ApplySettingPayload = {
|
||||||
|
[K in keyof SettingsDef]: {
|
||||||
|
settingKey: K
|
||||||
|
value: SettingsDef[K]
|
||||||
|
}
|
||||||
|
}[keyof SettingsDef]
|
||||||
|
|
||||||
const validKeys = Object.keys(defaultSettings)
|
const validKeys = Object.keys(defaultSettings)
|
||||||
|
|
||||||
const dispatchers = defineDispatchers({
|
const dispatchers = defineDispatchers({
|
||||||
bulkApplySettings(
|
bulkApplySettings(_currentState: SettingsDef, payload: Partial<SettingsDef>) {
|
||||||
_currentState: SettingsType,
|
|
||||||
payload: Partial<SettingsType>
|
|
||||||
) {
|
|
||||||
return payload
|
return payload
|
||||||
},
|
},
|
||||||
toggleSetting(
|
toggleSetting(
|
||||||
currentState: SettingsType,
|
currentState: SettingsDef,
|
||||||
{ settingKey }: { settingKey: KeysMatching<SettingsType, boolean> }
|
{ settingKey }: { settingKey: KeysMatching<SettingsDef, boolean> }
|
||||||
) {
|
) {
|
||||||
if (!has(currentState, settingKey)) {
|
if (!has(currentState, settingKey)) {
|
||||||
// console.log(
|
// console.log(
|
||||||
@@ -99,14 +103,14 @@ const dispatchers = defineDispatchers({
|
|||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: Partial<SettingsType> = {}
|
const result: Partial<SettingsDef> = {}
|
||||||
result[settingKey] = !currentState[settingKey]
|
result[settingKey] = !currentState[settingKey]
|
||||||
|
|
||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
applySetting<K extends keyof SettingsType>(
|
applySetting(
|
||||||
_currentState: SettingsType,
|
_currentState: SettingsDef,
|
||||||
{ settingKey, value }: { settingKey: K; value: SettingsType[K] }
|
{ settingKey, value }: ApplySettingPayload
|
||||||
) {
|
) {
|
||||||
if (!validKeys.includes(settingKey)) {
|
if (!validKeys.includes(settingKey)) {
|
||||||
// console.log(
|
// console.log(
|
||||||
@@ -115,8 +119,9 @@ const dispatchers = defineDispatchers({
|
|||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: Partial<SettingsType> = {}
|
const result: Partial<SettingsDef> = {
|
||||||
result[settingKey] = value
|
[settingKey]: value,
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
@@ -129,20 +134,20 @@ export const settingsStore = new DispatchingStore(defaultSettings, dispatchers)
|
|||||||
*/
|
*/
|
||||||
export const settings$ = settingsStore.subject$.asObservable()
|
export const settings$ = settingsStore.subject$.asObservable()
|
||||||
|
|
||||||
export function getSettingSubject<K extends keyof SettingsType>(
|
export function getSettingSubject<K extends keyof SettingsDef>(
|
||||||
settingKey: K
|
settingKey: K
|
||||||
): Observable<SettingsType[K]> {
|
): Observable<SettingsDef[K]> {
|
||||||
return settingsStore.subject$.pipe(pluck(settingKey), distinctUntilChanged())
|
return settingsStore.subject$.pipe(pluck(settingKey), distinctUntilChanged())
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bulkApplySettings(settingsObj: Partial<SettingsType>) {
|
export function bulkApplySettings(settingsObj: Partial<SettingsDef>) {
|
||||||
settingsStore.dispatch({
|
settingsStore.dispatch({
|
||||||
dispatcher: "bulkApplySettings",
|
dispatcher: "bulkApplySettings",
|
||||||
payload: settingsObj,
|
payload: settingsObj,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toggleSetting(settingKey: KeysMatching<SettingsType, boolean>) {
|
export function toggleSetting(settingKey: KeysMatching<SettingsDef, boolean>) {
|
||||||
settingsStore.dispatch({
|
settingsStore.dispatch({
|
||||||
dispatcher: "toggleSetting",
|
dispatcher: "toggleSetting",
|
||||||
payload: {
|
payload: {
|
||||||
@@ -151,12 +156,13 @@ export function toggleSetting(settingKey: KeysMatching<SettingsType, boolean>) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applySetting<K extends keyof SettingsType>(
|
export function applySetting<K extends ApplySettingPayload>(
|
||||||
settingKey: K,
|
settingKey: K["settingKey"],
|
||||||
value: SettingsType[K]
|
value: K["value"]
|
||||||
) {
|
) {
|
||||||
settingsStore.dispatch({
|
settingsStore.dispatch({
|
||||||
dispatcher: "applySetting",
|
dispatcher: "applySetting",
|
||||||
|
// @ts-expect-error TS is not able to understand the type semantics here
|
||||||
payload: {
|
payload: {
|
||||||
settingKey,
|
settingKey,
|
||||||
value,
|
value,
|
||||||
|
|||||||
Reference in New Issue
Block a user