Skip to content

Commit

Permalink
feat: app multiple tab data sync
Browse files Browse the repository at this point in the history
  • Loading branch information
dineug committed Jan 1, 2024
1 parent 4bd6c13 commit d787994
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 84 deletions.
65 changes: 46 additions & 19 deletions packages/erd-editor-app/src/atoms/modules/schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ import { atomWithImmer } from 'jotai-immer';
import { selectedSchemaIdAtom } from '@/atoms/modules/sidebar';
import { getAppDatabaseService } from '@/services/indexeddb';
import { SchemaEntity } from '@/services/indexeddb/modules/schema';
import {
addSchemaEntityAction,
deleteSchemaEntityAction,
dispatch,
updateSchemaEntityAction,
} from '@/utils/broadcastChannel';

const schemaEntitiesAtom = atomWithImmer<Array<Omit<SchemaEntity, 'value'>>>(
[]
);
export const schemaEntitiesAtom = atomWithImmer<
Array<Omit<SchemaEntity, 'value'>>
>([]);

const updateSchemaEntitiesAtom = atom(null, async (get, set) => {
const service = getAppDatabaseService();
Expand All @@ -34,6 +40,7 @@ const addSchemaEntityAtom = atom(
draft.push(result);
});
set(selectedSchemaIdAtom, result.id);
dispatch(addSchemaEntityAction({ value: result }));

return result;
}
Expand All @@ -44,43 +51,63 @@ const updateSchemaEntityAtom = atom(
async (
get,
set,
{
id,
entityValue,
}: {
payload: {
id: string;
entityValue: Partial<{ name: string; value: string }>;
entityValue: Partial<{ name: string }>;
}
) => {
const service = getAppDatabaseService();
if (!service) throw new Error('Database service is not initialized');

set(schemaEntitiesAtom, draft => {
const value = draft.find(item => item.id === id);
if (!value) return;
Object.assign(value, entityValue);
});
const { id, entityValue } = payload;
const entities = get(schemaEntitiesAtom);
const prev = entities.find(item => item.id === id);

const update = (newValue?: Partial<{ name: string }>) => {
set(schemaEntitiesAtom, draft => {
const value = draft.find(item => item.id === id);
if (!value || !newValue) return;
Object.assign(value, newValue);
});
};

update(entityValue);

try {
const result = await service.updateSchemaEntity(id, entityValue);

return await service.updateSchemaEntity(id, entityValue);
result ? dispatch(updateSchemaEntityAction(payload)) : update(prev);
return result;
} catch (error) {
update(prev);
throw error;
}
}
);

const deleteSchemaEntityAtom = atom(null, async (get, set, id: string) => {
const service = getAppDatabaseService();
if (!service) throw new Error('Database service is not initialized');

const prev = get(schemaEntitiesAtom);
const selectedSchemaId = get(selectedSchemaIdAtom);

set(schemaEntitiesAtom, draft => {
const index = draft.findIndex(item => item.id === id);
if (index === -1) return;
draft.splice(index, 1);
});

const selectedSchemaId = get(selectedSchemaIdAtom);
if (selectedSchemaId === id) {
set(selectedSchemaIdAtom, null);
try {
await service.deleteSchemaEntity(id);
if (selectedSchemaId === id) {
set(selectedSchemaIdAtom, null);
}
dispatch(deleteSchemaEntityAction({ id }));
} catch (error) {
set(schemaEntitiesAtom, prev);
throw error;
}

await service.deleteSchemaEntity(id);
});

export const useSchemaEntities = () => useAtomValue(schemaEntitiesAtom);
Expand Down
35 changes: 8 additions & 27 deletions packages/erd-editor-app/src/atoms/modules/sidebar/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ import { atom, useAtomValue, useSetAtom } from 'jotai';
import { loadable } from 'jotai/utils';

import { getAppDatabaseService } from '@/services/indexeddb';
import { dispatch, replicationValueAction } from '@/utils/broadcastChannel';
import {
dispatch,
replicationSchemaEntityAction,
} from '@/utils/broadcastChannel';

export const selectedSchemaIdAtom = atom<string | null>(null);

const asyncSchemaEntityAtom = atom(async get => {
const id = get(selectedSchemaIdAtom);
const service = getAppDatabaseService();
if (!service) throw new Error('Database service is not initialized');

const id = get(selectedSchemaIdAtom);
if (!service) throw new Error('Database service is not initialized');
if (!id) throw new Error('not found selected schema id');

const result = await service.getSchemaEntity(id);
Expand All @@ -21,29 +24,9 @@ const asyncSchemaEntityAtom = atom(async get => {

const schemaEntityAtom = loadable(asyncSchemaEntityAtom);

const updateSchemaEntityValueAtom = atom(
null,
async (
get,
set,
{
id,
value,
}: {
id: string;
value: string;
}
) => {
const service = getAppDatabaseService();
if (!service) throw new Error('Database service is not initialized');

return await service.updateSchemaEntity(id, { value });
}
);

const replicationSchemaEntityAtom = atom(
null,
(
async (
get,
set,
{
Expand All @@ -58,12 +41,10 @@ const replicationSchemaEntityAtom = atom(
if (!service) throw new Error('Database service is not initialized');

service.replicationSchemaEntity(id, actions);
dispatch(replicationValueAction({ id, actions }));
dispatch(replicationSchemaEntityAction({ id, actions }));
}
);

export const useSchemaEntity = () => useAtomValue(schemaEntityAtom);
export const useUpdateSchemaEntityValue = () =>
useSetAtom(updateSchemaEntityValueAtom);
export const useReplicationSchemaEntity = () =>
useSetAtom(replicationSchemaEntityAtom);
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const Editor: React.FC<EditorProps> = props => {
)
.add(
bridge.on({
replicationValue: ({ payload: { id, actions } }) => {
replicationSchemaEntity: ({ payload: { id, actions } }) => {
if (id === props.entity.id) {
sharedStore.dispatch(actions);
}
Expand Down
6 changes: 5 additions & 1 deletion packages/erd-editor-app/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import './styles.css';

import { Provider } from 'jotai';
import React from 'react';
import { createRoot } from 'react-dom/client';

import App from '@/components/app/App';
import { store } from '@/store';

const root = createRoot(document.getElementById('app')!);
root.render(
<React.StrictMode>
<App />
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class AppDatabaseService {
}

async getSchemaEntities() {
return await this.schemaService.getEntities();
return await this.schemaService.getAll();
}

async replicationSchemaEntity(id: string, actions: any) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,25 @@ export class SchemaService {
private cache = new Map<string, SchemaEntity & { store: ReplicationStore }>();
constructor(private db: AppDatabase) {}

async add(entityValue: Pick<SchemaEntity, 'name'>) {
const result = await addSchemaEntity(this.db, entityValue);
private createCache(entity: SchemaEntity) {
if (this.cache.has(entity.id)) return;

const store = createReplicationStore({ toWidth });
const store = createReplicationStore({
toWidth: value => value.length * 11,
});
store.setInitialValue(entity.value);
store.on({
change: () => {
this.update(result.id, { value: store.value });
this.update(entity.id, { value: store.value });
},
});
this.cache.set(result.id, { ...result, store });
this.cache.set(entity.id, { ...entity, store });
}

async add(entityValue: Pick<SchemaEntity, 'name'>) {
const result = await addSchemaEntity(this.db, entityValue);

this.createCache(result);
return result;
}

Expand All @@ -51,37 +59,26 @@ export class SchemaService {
}

async delete(id: string) {
const result = await deleteSchemaEntity(this.db, id);
await deleteSchemaEntity(this.db, id);
const prev = this.cache.get(id);

if (prev) {
this.cache.delete(id);
prev.store.destroy();
}

return result;
}

async get(id: string) {
const prev = this.cache.get(id);
if (prev) return omit(prev, 'store');

const result = await getSchemaEntity(this.db, id);
if (result) {
const store = createReplicationStore({ toWidth });
store.setInitialValue(result.value);
store.on({
change: () => {
this.update(result.id, { value: store.value });
},
});
this.cache.set(id, { ...result, store });
}
result && this.createCache(result);

return result;
}

async getEntities() {
async getAll() {
return await getSchemaEntities(this.db);
}

Expand All @@ -98,7 +95,3 @@ export class SchemaService {
}
}
}

function toWidth(value: string) {
return value.length * 11;
}
34 changes: 34 additions & 0 deletions packages/erd-editor-app/src/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { createStore } from 'jotai';

import { schemaEntitiesAtom } from '@/atoms/modules/schema';
import { selectedSchemaIdAtom } from '@/atoms/modules/sidebar';
import { bridge } from '@/utils/broadcastChannel';

export const store = createStore();

bridge.on({
addSchemaEntity: ({ payload: { value } }) => {
store.set(schemaEntitiesAtom, draft => {
draft.push(value);
});
},
updateSchemaEntity: ({ payload: { id, entityValue } }) => {
store.set(schemaEntitiesAtom, draft => {
const value = draft.find(item => item.id === id);
if (!value) return;
Object.assign(value, entityValue);
});
},
deleteSchemaEntity: ({ payload: { id } }) => {
const selectedSchemaId = store.get(selectedSchemaIdAtom);
if (selectedSchemaId === id) {
store.set(selectedSchemaIdAtom, null);
}

store.set(schemaEntitiesAtom, draft => {
const index = draft.findIndex(item => item.id === id);
if (index === -1) return;
draft.splice(index, 1);
});
},
});
36 changes: 31 additions & 5 deletions packages/erd-editor-app/src/utils/broadcastChannel.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
import { isObject, safeCallback } from '@dineug/shared';

import { AnyAction, ReducerRecord, ValuesType } from '@/internal-types';
import { SchemaEntity } from '@/services/indexeddb/modules/schema';

const BridgeActionType = {
replicationValue: 'replicationValue',
replicationSchemaEntity: 'replicationSchemaEntity',
addSchemaEntity: 'addSchemaEntity',
updateSchemaEntity: 'updateSchemaEntity',
deleteSchemaEntity: 'deleteSchemaEntity',
} as const;
type BridgeActionType = ValuesType<typeof BridgeActionType>;

type BridgeActionMap = {
[BridgeActionType.replicationValue]: {
[BridgeActionType.replicationSchemaEntity]: {
id: string;
actions: any;
};
[BridgeActionType.addSchemaEntity]: {
value: SchemaEntity;
};
[BridgeActionType.updateSchemaEntity]: {
id: string;
entityValue: Partial<{ name: string }>;
};
[BridgeActionType.deleteSchemaEntity]: {
id: string;
};
};

function createAction<P = void>(type: string) {
Expand Down Expand Up @@ -56,6 +70,18 @@ channel.addEventListener('message', event => {
bridge.emit(action);
});

export const replicationValueAction = createAction<
BridgeActionMap[typeof BridgeActionType.replicationValue]
>(BridgeActionType.replicationValue);
export const replicationSchemaEntityAction = createAction<
BridgeActionMap[typeof BridgeActionType.replicationSchemaEntity]
>(BridgeActionType.replicationSchemaEntity);

export const addSchemaEntityAction = createAction<
BridgeActionMap[typeof BridgeActionType.addSchemaEntity]
>(BridgeActionType.addSchemaEntity);

export const updateSchemaEntityAction = createAction<
BridgeActionMap[typeof BridgeActionType.updateSchemaEntity]
>(BridgeActionType.updateSchemaEntity);

export const deleteSchemaEntityAction = createAction<
BridgeActionMap[typeof BridgeActionType.deleteSchemaEntity]
>(BridgeActionType.deleteSchemaEntity);
7 changes: 0 additions & 7 deletions packages/erd-editor-app/src/utils/serialize.ts

This file was deleted.

0 comments on commit d787994

Please sign in to comment.