2455c859b4
Create the necessary tooling to listen to metadata events and plug it to the front components. Now we have a hot reload like experience when we edit a component in an app. ## Backend - Split `EventWithQueryIds` into `ObjectRecordEventWithQueryIds` and `MetadataEventWithQueryIds` - Publish metadata event batches to active SSE streams in `MetadataEventsToDbListener` ## Frontend - Create a metadata event dispatching pipeline: SSE metadata events are grouped by metadata name, transformed into `MetadataOperationBrowserEventDetail` objects, and dispatched as browser `CustomEvents` - Add `useListenToMetadataOperationBrowserEvent` hook for consuming metadata operation events filtered by metadata name and operation type - Rename `useListenToObjectRecordEventsForQuery` to `useListenToEventsForQuery`, now accepting both `RecordGqlOperationSignature` and `MetadataGqlOperationSignature` - Implement `useOnFrontComponentUpdated` which subscribes to front component metadata events and updates the Apollo cache when the component is modified - Add `builtComponentChecksum` to the front component query and appends it to the component URL for browser cache invalidation
55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
import { dispatchMetadataOperationBrowserEvent } from '@/browser-event/utils/dispatchMetadataOperationBrowserEvent';
|
|
import { turnSseMetadataEventsToMetadataOperationBrowserEvents } from '@/sse-db-event/utils/turnSseMetadataEventsToMetadataOperationBrowserEvents';
|
|
import { useCallback } from 'react';
|
|
import {
|
|
type AllMetadataName,
|
|
type MetadataEvent,
|
|
type MetadataEventWithQueryIds,
|
|
} from '~/generated-metadata/graphql';
|
|
|
|
const groupSseMetadataEventsByMetadataName = (
|
|
sseMetadataEvents: MetadataEvent[],
|
|
): Map<AllMetadataName, MetadataEvent[]> => {
|
|
const eventsByMetadataName = new Map<AllMetadataName, MetadataEvent[]>();
|
|
|
|
for (const event of sseMetadataEvents) {
|
|
const metadataName = event.metadataName as AllMetadataName;
|
|
|
|
const existing = eventsByMetadataName.get(metadataName) ?? [];
|
|
|
|
eventsByMetadataName.set(metadataName, [...existing, event]);
|
|
}
|
|
|
|
return eventsByMetadataName;
|
|
};
|
|
|
|
export const useDispatchMetadataEventsFromSseToBrowserEvents = <
|
|
T extends Record<string, unknown>,
|
|
>() => {
|
|
const dispatchMetadataEventsFromSseToBrowserEvents = useCallback(
|
|
(metadataEventsWithQueryIds: MetadataEventWithQueryIds[]) => {
|
|
const sseMetadataEvents = metadataEventsWithQueryIds.map(
|
|
(item) => item.metadataEvent,
|
|
);
|
|
|
|
const eventsByMetadataName =
|
|
groupSseMetadataEventsByMetadataName(sseMetadataEvents);
|
|
|
|
for (const [metadataName, events] of eventsByMetadataName) {
|
|
const metadataOperationBrowserEvents =
|
|
turnSseMetadataEventsToMetadataOperationBrowserEvents<T>({
|
|
metadataName,
|
|
sseMetadataEvents: events,
|
|
});
|
|
|
|
for (const browserEvent of metadataOperationBrowserEvents) {
|
|
dispatchMetadataOperationBrowserEvent(browserEvent);
|
|
}
|
|
}
|
|
},
|
|
[],
|
|
);
|
|
|
|
return { dispatchMetadataEventsFromSseToBrowserEvents };
|
|
};
|