feat: add project plugin center
This commit is contained in:
124
src/stores/coding-plugins.ts
Normal file
124
src/stores/coding-plugins.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { useStore } from 'zustand';
|
||||
import { createStore, type StoreApi } from 'zustand/vanilla';
|
||||
import {
|
||||
configureDataService,
|
||||
getCodingPlugins,
|
||||
inspectDataService,
|
||||
removeDataServiceCollection,
|
||||
removeDataServiceProject,
|
||||
resetDataService,
|
||||
setCodingPluginEnabled,
|
||||
type CodingPluginProject,
|
||||
} from '@/lib/coding-plugins';
|
||||
import type { DataServiceInstanceState } from '../../shared/data-service';
|
||||
|
||||
type Dependencies = {
|
||||
list: typeof getCodingPlugins;
|
||||
setEnabled: typeof setCodingPluginEnabled;
|
||||
inspectDataService: typeof inspectDataService;
|
||||
configureDataService: typeof configureDataService;
|
||||
resetDataService: typeof resetDataService;
|
||||
removeCollection: typeof removeDataServiceCollection;
|
||||
removeProject: typeof removeDataServiceProject;
|
||||
};
|
||||
|
||||
export type CodingPluginsState = {
|
||||
projectId: string | null;
|
||||
projection: CodingPluginProject | null;
|
||||
dataService: DataServiceInstanceState | null;
|
||||
loadState: 'idle' | 'loading' | 'ready' | 'error';
|
||||
error: string | null;
|
||||
pending: Record<string, true>;
|
||||
load(projectId: string): Promise<void>;
|
||||
setEnabled(projectId: string, pluginId: string, enabled: boolean): Promise<void>;
|
||||
configure(collections: string[]): Promise<void>;
|
||||
reset(): Promise<void>;
|
||||
removeCollection(collection: string): Promise<void>;
|
||||
removeProject(): Promise<void>;
|
||||
};
|
||||
|
||||
export function createCodingPluginsStore(overrides: Partial<Dependencies> = {}): StoreApi<CodingPluginsState> {
|
||||
const deps: Dependencies = {
|
||||
list: getCodingPlugins, setEnabled: setCodingPluginEnabled, inspectDataService,
|
||||
configureDataService, resetDataService, removeCollection: removeDataServiceCollection,
|
||||
removeProject: removeDataServiceProject, ...overrides,
|
||||
};
|
||||
const flights = new Map<string, Promise<void>>();
|
||||
const operation = (key: string, run: () => Promise<void>): Promise<void> => {
|
||||
const existing = flights.get(key); if (existing) return existing;
|
||||
let flight: Promise<void>;
|
||||
flight = run().finally(() => {
|
||||
flights.delete(key);
|
||||
const pending = { ...store.getState().pending }; delete pending[key]; store.setState({ pending });
|
||||
});
|
||||
flights.set(key, flight); store.setState((state) => ({ pending: { ...state.pending, [key]: true } }));
|
||||
return flight;
|
||||
};
|
||||
const store = createStore<CodingPluginsState>((set, get) => ({
|
||||
projectId: null, projection: null, dataService: null, loadState: 'idle', error: null, pending: {},
|
||||
load(projectId) {
|
||||
return operation(`load:${projectId}`, async () => {
|
||||
set({ loadState: 'loading', error: null });
|
||||
try {
|
||||
const projection = await deps.list(projectId);
|
||||
let dataService = get().projectId === projectId ? get().dataService : null;
|
||||
const item = projection.items.find(({ settingsSurface }) => settingsSurface === 'data-service');
|
||||
if (item?.enabled && item.backend.status === 'ready') {
|
||||
const result = await deps.inspectDataService();
|
||||
if (result.success && result.data) dataService = result.data;
|
||||
}
|
||||
set({ projectId, projection, dataService, loadState: 'ready' });
|
||||
} catch (error) {
|
||||
set({ loadState: 'error', error: error instanceof Error ? error.message : String(error) });
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
},
|
||||
setEnabled(projectId, pluginId, enabled) {
|
||||
return operation(`enabled:${projectId}:${pluginId}`, async () => {
|
||||
const projection = await deps.setEnabled(projectId, pluginId, enabled);
|
||||
set({
|
||||
projectId, projection, error: null,
|
||||
...(!enabled || get().projectId !== projectId ? { dataService: null } : {}),
|
||||
});
|
||||
});
|
||||
},
|
||||
configure(collections) {
|
||||
return operation('data-service:configure', async () => {
|
||||
const result = await deps.configureDataService(collections);
|
||||
if (!result.success || !result.data) throw new Error(result.error || '开发数据空间创建失败');
|
||||
set({ dataService: result.data, error: null });
|
||||
if (get().projectId) await get().load(get().projectId as string);
|
||||
});
|
||||
},
|
||||
reset() {
|
||||
return operation('data-service:reset', async () => {
|
||||
const result = await deps.resetDataService();
|
||||
if (!result.success || !result.data) throw new Error(result.error || '开发数据重置失败');
|
||||
set({ dataService: result.data });
|
||||
});
|
||||
},
|
||||
removeCollection(collection) {
|
||||
return operation(`data-service:collection:${collection}`, async () => {
|
||||
const result = await deps.removeCollection(collection);
|
||||
if (!result.success) throw new Error(result.error || '移除 collection 失败');
|
||||
const inspected = await deps.inspectDataService();
|
||||
if (inspected.success && inspected.data) set({ dataService: inspected.data });
|
||||
});
|
||||
},
|
||||
removeProject() {
|
||||
return operation('data-service:remove-project', async () => {
|
||||
const result = await deps.removeProject();
|
||||
if (!result.success) throw new Error(result.error || '删除开发数据空间失败');
|
||||
set({ dataService: null });
|
||||
if (get().projectId) await get().load(get().projectId as string);
|
||||
});
|
||||
},
|
||||
}));
|
||||
return store;
|
||||
}
|
||||
|
||||
export const codingPluginsStore = createCodingPluginsStore();
|
||||
export function useCodingPluginsStore<T>(selector: (state: CodingPluginsState) => T): T {
|
||||
return useStore(codingPluginsStore, selector);
|
||||
}
|
||||
Reference in New Issue
Block a user