import { create } from 'zustand' import { useI18nStore } from './i18n.store' import { useRedisStateStore } from './redis-state.store' import { useSettingsStore } from './settings.store' import { parseRedisInfo } from './redis-parser' // --- Event bus for tree commands --- type VoidCallback = () => void const treeListeners: Record> = {} export function onTreeEvent(event: string, cb: VoidCallback): VoidCallback { if (!treeListeners[event]) treeListeners[event] = new Set() treeListeners[event].add(cb) return () => { treeListeners[event].delete(cb) } } export function emitTreeEvent(event: string) { treeListeners[event]?.forEach(cb => cb()) } // --- Dialog state --- interface ConfirmOptions { title?: string message: string disableCancel?: boolean onOk?: () => void onCancel?: () => void } interface PromptOptions { title: string placeholder: string initialValue?: string okLabel: string cancelLabel: string onOk?: (value: string) => void onCancel?: () => void } interface CommonState { // Toast toastMessage: string toastOpen: boolean toast: (message: string, hideDelay?: number) => void closeToast: () => void // Confirm dialog confirmOpen: boolean confirmOptions: ConfirmOptions | null confirm: (options: ConfirmOptions) => Promise resolveConfirm: ((ok: boolean) => void) | null // Prompt dialog promptOpen: boolean promptOptions: PromptOptions | null prompt: (options: PromptOptions) => Promise resolvePrompt: ((value: string | null) => void) | null // Error handling generalHandleError: (dataOrError: any) => boolean // Redis info loading loadRedisInfoResponse: (options: { response?: any }) => void } let lastResponse: any = null export const useCommonStore = create((set, get) => ({ toastMessage: '', toastOpen: false, toast: (message: string, _hideDelay?: number) => { set({ toastMessage: message, toastOpen: true }) }, closeToast: () => set({ toastOpen: false }), confirmOpen: false, confirmOptions: null, resolveConfirm: null, confirm: (options: ConfirmOptions) => { return new Promise((resolve, reject) => { const strings = useI18nStore.getState().strings const isAlert = options.disableCancel === true set({ confirmOpen: true, confirmOptions: { title: options.title || (isAlert ? strings?.confirm?.info : strings?.confirm?.title), message: options.message, disableCancel: isAlert, }, resolveConfirm: (ok: boolean) => { set({ confirmOpen: false, confirmOptions: null, resolveConfirm: null }) ok ? resolve() : reject() }, }) }) }, promptOpen: false, promptOptions: null, resolvePrompt: null, prompt: (options: PromptOptions) => { return new Promise((resolve, reject) => { set({ promptOpen: true, promptOptions: options, resolvePrompt: (value: string | null) => { set({ promptOpen: false, promptOptions: null, resolvePrompt: null }) value !== null ? resolve(value) : reject() }, }) }) }, generalHandleError: (dataOrError: any): boolean => { if (dataOrError === undefined) return true if (!(dataOrError instanceof Error || dataOrError instanceof Object)) { dataOrError = new Error(String(dataOrError)) } if (dataOrError instanceof Error || dataOrError.status === 'error') { let error: any = dataOrError instanceof Error ? dataOrError : dataOrError.error console.warn('generalHandleError') console.error(error) const strings = useI18nStore.getState().strings const codes = strings?.code || {} if (typeof error === 'string' && codes.hasOwnProperty(error)) { error = new Error(codes[error]) } else if (error?.code && codes.hasOwnProperty(error.code)) { error.message = codes[error.code] } else if (error?.message && codes.hasOwnProperty(error.message)) { error.message = codes[error.message] } if (error?.message === 'Connection is closed.') { useRedisStateStore.setState({ connection: undefined }) } // Show as alert get().confirm({ title: strings?.title?.error, message: error?.message || String(error), disableCancel: true, }).catch(() => {}) return false } return true }, loadRedisInfoResponse: (options: { response?: any } = {}) => { let response = options.response || lastResponse lastResponse = response if (!response) return const settings = useSettingsStore.getState() const info = parseRedisInfo(response.info) const keys = settings.keysSort && response.keys.length <= settings.maxLightKeysCount ? [...response.keys].sort() : response.keys useRedisStateStore.setState({ info, keysRaw: keys, keysInfo: response.keysInfo, keysInfoFetchedAt: Date.now(), }) }, }))