import { pluck, distinctUntilChanged } from "rxjs/operators" import has from "lodash/has" import { Observable } from "rxjs" import { Ref } from "@nuxtjs/composition-api" import DispatchingStore, { defineDispatchers } from "./DispatchingStore" import type { KeysMatching } from "~/types/ts-utils" import { useStream } from "~/helpers/utils/composables" export const HoppBgColors = ["system", "light", "dark", "black"] as const export type HoppBgColor = typeof HoppBgColors[number] export const HoppAccentColors = [ "green", "teal", "blue", "indigo", "purple", "yellow", "orange", "red", "pink", ] as const export type HoppAccentColor = typeof HoppAccentColors[number] export const HoppFontSizes = ["small", "medium", "large"] as const export type HoppFontSize = typeof HoppFontSizes[number] export type SettingsType = { syncCollections: boolean syncHistory: boolean syncEnvironments: boolean PROXY_ENABLED: boolean PROXY_URL: string EXTENSIONS_ENABLED: boolean URL_EXCLUDES: { auth: boolean httpUser: boolean httpPassword: boolean bearerToken: boolean oauth2Token: boolean } THEME_COLOR: HoppAccentColor BG_COLOR: HoppBgColor TELEMETRY_ENABLED: boolean EXPAND_NAVIGATION: boolean SIDEBAR: boolean SIDEBAR_ON_LEFT: boolean ZEN_MODE: boolean FONT_SIZE: HoppFontSize COLUMN_LAYOUT: boolean } export const defaultSettings: SettingsType = { syncCollections: true, syncHistory: true, syncEnvironments: true, PROXY_ENABLED: false, PROXY_URL: "https://proxy.hoppscotch.io/", EXTENSIONS_ENABLED: true, URL_EXCLUDES: { auth: true, httpUser: true, httpPassword: true, bearerToken: true, oauth2Token: true, }, THEME_COLOR: "indigo", BG_COLOR: "system", TELEMETRY_ENABLED: true, EXPAND_NAVIGATION: true, SIDEBAR: true, SIDEBAR_ON_LEFT: false, ZEN_MODE: false, FONT_SIZE: "small", COLUMN_LAYOUT: true, } const validKeys = Object.keys(defaultSettings) const dispatchers = defineDispatchers({ bulkApplySettings( _currentState: SettingsType, payload: Partial ) { return payload }, toggleSetting( currentState: SettingsType, { settingKey }: { settingKey: KeysMatching } ) { if (!has(currentState, settingKey)) { // console.log( // `Toggling of a non-existent setting key '${settingKey}' ignored` // ) return {} } const result: Partial = {} result[settingKey] = !currentState[settingKey] return result }, applySetting( _currentState: SettingsType, { settingKey, value }: { settingKey: K; value: SettingsType[K] } ) { if (!validKeys.includes(settingKey)) { // console.log( // `Ignoring non-existent setting key '${settingKey}' assignment` // ) return {} } const result: Partial = {} result[settingKey] = value return result }, }) export const settingsStore = new DispatchingStore(defaultSettings, dispatchers) /** * An observable value to make avail all the state information at once */ export const settings$ = settingsStore.subject$.asObservable() export function getSettingSubject( settingKey: K ): Observable { return settingsStore.subject$.pipe(pluck(settingKey), distinctUntilChanged()) } export function bulkApplySettings(settingsObj: Partial) { settingsStore.dispatch({ dispatcher: "bulkApplySettings", payload: settingsObj, }) } export function toggleSetting(settingKey: KeysMatching) { settingsStore.dispatch({ dispatcher: "toggleSetting", payload: { settingKey, }, }) } export function applySetting( settingKey: K, value: SettingsType[K] ) { settingsStore.dispatch({ dispatcher: "applySetting", payload: { settingKey, value, }, }) } export function useSetting( settingKey: K ): Ref { return useStream( settingsStore.subject$.pipe(pluck(settingKey), distinctUntilChanged()), settingsStore.value[settingKey], (value: SettingsType[K]) => { settingsStore.dispatch({ dispatcher: "applySetting", payload: { settingKey, value, }, }) } ) }