Skip to content

Commit

Permalink
feat: app broadcastChannel replication
Browse files Browse the repository at this point in the history
  • Loading branch information
dineug committed Dec 30, 2023
1 parent 1b64dae commit f00c23e
Show file tree
Hide file tree
Showing 11 changed files with 327 additions and 76 deletions.
35 changes: 0 additions & 35 deletions .prettierignore

This file was deleted.

2 changes: 2 additions & 0 deletions packages/erd-editor-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"@dineug/erd-editor": "workspace:*",
"@dineug/erd-editor-shiki-worker": "workspace:*",
"@dineug/shared": "workspace:*",
"@emotion/react": "^11.11.3",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/themes": "^2.0.3",
Expand All @@ -25,6 +26,7 @@
"nanoid": "^5.0.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"socket.io-client": "^4.7.2",
"workbox-core": "^7.0.0",
"workbox-precaching": "^7.0.0",
"workbox-window": "^7.0.0"
Expand Down
15 changes: 1 addition & 14 deletions packages/erd-editor-app/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,6 @@
</head>
<body>
<div id="app"></div>
<!-- Google tag (gtag.js) -->
<script
async
src="https://www.googletagmanager.com/gtag/js?id=G-3VBWD4V1JX"
></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());

gtag('config', 'G-3VBWD4V1JX');
</script>
<%= gtag %>
</body>
</html>
24 changes: 24 additions & 0 deletions packages/erd-editor-app/src/atoms/modules/sidebar/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { atom, useAtomValue, useSetAtom } from 'jotai';
import { loadable } from 'jotai/utils';

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

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

Expand Down Expand Up @@ -40,6 +41,29 @@ const updateSchemaEntityValueAtom = atom(
}
);

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

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

export const useSchemaEntity = () => useAtomValue(schemaEntityAtom);
export const useUpdateSchemaEntityValue = () =>
useSetAtom(updateSchemaEntityValueAtom);
export const useReplicationSchemaEntity = () =>
useSetAtom(replicationSchemaEntityAtom);
38 changes: 27 additions & 11 deletions packages/erd-editor-app/src/components/viewer/editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import {
import { useAtom } from 'jotai';
import { useLayoutEffect, useRef } from 'react';

import { useUpdateSchemaEntityValue } from '@/atoms/modules/sidebar';
import { useReplicationSchemaEntity } from '@/atoms/modules/sidebar';
import { themeAtom } from '@/atoms/modules/theme';
import { SchemaEntity } from '@/services/indexeddb/modules/schema';
import { bridge } from '@/utils/broadcastChannel';

import * as styles from './Editor.styles';

Expand All @@ -23,23 +24,38 @@ const Editor: React.FC<EditorProps> = props => {
const viewerRef = useRef<HTMLDivElement>(null);
const editorRef = useRef<ErdEditorElement | null>(null);
const [theme, setTheme] = useAtom(themeAtom);
const updateSchemaEntityValue = useUpdateSchemaEntityValue();
const replicationSchemaEntity = useReplicationSchemaEntity();

useLayoutEffect(() => {
const $viewer = viewerRef.current;
if (!$viewer) return;

const unsubscribeSet = new Set<() => void>();
const editor = document.createElement('erd-editor');
editorRef.current = editor;
editor.enableThemeBuilder = true;
editor.setInitialValue(props.entity.value);

const handleChange = () => {
updateSchemaEntityValue({
id: props.entity.id,
value: editor.value,
});
};
const sharedStore = editor.getSharedStore({ mouseTracker: false });

unsubscribeSet
.add(
sharedStore.subscribe(actions => {
replicationSchemaEntity({
id: props.entity.id,
actions,
});
})
)
.add(
bridge.on({
replicationValue: ({ payload: { id, actions } }) => {
if (id === props.entity.id) {
sharedStore.dispatch(actions);
}
},
})
);

const handleChangePresetTheme = (event: Event) => {
const e = event as CustomEvent;
Expand All @@ -51,18 +67,18 @@ const Editor: React.FC<EditorProps> = props => {
});
};

editor.addEventListener('change', handleChange);
editor.addEventListener('changePresetTheme', handleChangePresetTheme);
$viewer.appendChild(editor);

return () => {
$viewer.removeChild(editor);
editor.removeEventListener('change', handleChange);
editor.removeEventListener('changePresetTheme', handleChangePresetTheme);
Array.from(unsubscribeSet).forEach(unsubscribe => unsubscribe());
unsubscribeSet.clear();
editor.destroy();
editorRef.current = null;
};
}, [setTheme, updateSchemaEntityValue, props.entity.id, props.entity.value]);
}, [setTheme, replicationSchemaEntity, props.entity.id, props.entity.value]);

useLayoutEffect(() => {
const editor = editorRef.current;
Expand Down
18 changes: 18 additions & 0 deletions packages/erd-editor-app/src/internal-types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,21 @@ export type EntityType<T> = T & {
updateAt: number;
createAt: number;
};

export type ValuesType<T extends Record<string, string>> = T[keyof T];

type Action<K extends keyof M, M> = {
type: K;
payload: M[K];
};

export type AnyAction<P = any> = {
type: string;
payload: P;
};

type Reducer<K extends keyof M, M> = (action: Action<K, M>) => void;

export type ReducerRecord<K extends keyof M, M> = {
[P in K]: Reducer<P, M>;
};
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import Dexie, { Table } from 'dexie';

import {
addSchemaEntity,
deleteSchemaEntity,
getSchemaEntities,
getSchemaEntity,
SchemaEntity,
updateSchemaEntity,
} from '@/services/indexeddb/modules/schema';
import { SchemaEntity } from '@/services/indexeddb/modules/schema';
import { SchemaService } from '@/services/indexeddb/modules/schema/service';

export class AppDatabase extends Dexie {
schemas!: Table<SchemaEntity, string>;
Expand All @@ -31,30 +25,33 @@ function getDB() {
}

export class AppDatabaseService {
private get db() {
return getDB();
}
private db = getDB();
private schemaService = new SchemaService(this.db);

async addSchemaEntity(entityValue: Pick<SchemaEntity, 'name'>) {
return await addSchemaEntity(this.db, entityValue);
return await this.schemaService.add(entityValue);
}

async updateSchemaEntity(
id: string,
entityValue: Partial<Pick<SchemaEntity, 'value' | 'name'>>
) {
return await updateSchemaEntity(this.db, id, entityValue);
return await this.schemaService.update(id, entityValue);
}

async deleteSchemaEntity(id: string) {
return await deleteSchemaEntity(this.db, id);
return await this.schemaService.delete(id);
}

async getSchemaEntity(id: string) {
return await getSchemaEntity(this.db, id);
return await this.schemaService.get(id);
}

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

async replicationSchemaEntity(id: string, actions: any) {
await this.schemaService.replication(id, actions);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {
createReplicationStore,
ReplicationStore,
} from '@dineug/erd-editor/engine.js';
import { omit } from 'lodash-es';

import { type AppDatabase } from '@/services/indexeddb/appDatabaseService';
import {
addSchemaEntity,
deleteSchemaEntity,
getSchemaEntities,
getSchemaEntity,
SchemaEntity,
updateSchemaEntity,
} from '@/services/indexeddb/modules/schema';

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);

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

return result;
}

async update(
id: string,
entityValue: Partial<Pick<SchemaEntity, 'value' | 'name'>>
) {
const result = await updateSchemaEntity(this.db, id, entityValue);

const prev = this.cache.get(id);
if (prev && result) {
this.cache.set(id, { ...prev, ...entityValue });
}

return result;
}

async delete(id: string) {
const result = await deleteSchemaEntity(this.db, id);
this.cache.delete(id);
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 });
}

return result;
}

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

async replication(id: string, actions: any) {
let prev = this.cache.get(id);

if (!prev) {
await this.get(id);
prev = this.cache.get(id);
}

if (prev) {
prev.store.dispatch(actions as any);
}
}
}

function toWidth(value: string) {
return value.length * 11;
}
Loading

0 comments on commit f00c23e

Please sign in to comment.