From 2004c3108c90e0c72ea2bd6e7fd8964fe7aa1b14 Mon Sep 17 00:00:00 2001 From: Yourim Cha Date: Mon, 13 Jan 2025 15:50:54 +0900 Subject: [PATCH 1/5] Add EventsForDocReplay to handle document replay events --- .../src/devtools/contexts/YorkieSource.tsx | 57 ++++++++-------- .../devtools/src/devtools/panel/index.tsx | 6 +- .../devtools/src/devtools/tabs/History.tsx | 16 ++--- packages/sdk/src/devtools/index.ts | 36 +++------- packages/sdk/src/devtools/protocol.ts | 6 +- packages/sdk/src/devtools/types.ts | 66 ++++++++++++++++++- packages/sdk/src/document/document.ts | 10 +-- 7 files changed, 120 insertions(+), 77 deletions(-) diff --git a/packages/devtools/src/devtools/contexts/YorkieSource.tsx b/packages/devtools/src/devtools/contexts/YorkieSource.tsx index 986a11d8a..8362a38b8 100644 --- a/packages/devtools/src/devtools/contexts/YorkieSource.tsx +++ b/packages/devtools/src/devtools/contexts/YorkieSource.tsx @@ -24,18 +24,14 @@ import { useState, } from 'react'; -import { - DocEventType, - type SDKToPanelMessage, - type TransactionEvent, -} from 'yorkie-js-sdk'; +import { DocEventType, Devtools, type SDKToPanelMessage } from 'yorkie-js-sdk'; import { connectPort, sendToSDK } from '../../port'; import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; const DocKeyContext = createContext(null); const YorkieDocContext = createContext(null); -const TransactionEventsContext = createContext<{ - events: Array; +const ReplayDocEventsContext = createContext<{ + events: Array; hidePresenceEvents: boolean; setHidePresenceEvents: Dispatch>; }>(null); @@ -47,17 +43,16 @@ type Props = { export function YorkieSourceProvider({ children }: Props) { const [currentDocKey, setCurrentDocKey] = useState(''); const [doc, setDoc] = useState(null); - const [transactionEvents, setTransactionEvents] = useState< - Array + const [replayDocEvents, setReplayDocEvents] = useState< + Array >([]); // filter out presence events - const [hideTransactionPresenceEvents, setHideTransactionPresenceEvents] = - useState(false); + const [hidePresenceEvents, setHidePresenceEvents] = useState(false); const resetDocument = () => { setCurrentDocKey(''); - setTransactionEvents([]); + setReplayDocEvents([]); setDoc(null); }; @@ -77,11 +72,11 @@ export function YorkieSourceProvider({ children }: Props) { case 'doc::sync::full': // TODO(chacha912): Notify the user that they need to use the latest version of Yorkie-JS-SDK. if (message.events === undefined) break; - setTransactionEvents(message.events); + setReplayDocEvents(message.events); break; case 'doc::sync::partial': if (message.event === undefined) break; - setTransactionEvents((events) => [...events, message.event]); + setReplayDocEvents((events) => [...events, message.event]); break; } }, []); @@ -108,17 +103,17 @@ export function YorkieSourceProvider({ children }: Props) { return ( - {children} - + ); } @@ -145,14 +140,14 @@ export function useYorkieDoc() { return value; } -export enum TransactionEventType { +export enum ReplayDocEventType { Document = 'document', Presence = 'presence', } -export const getTransactionEventType = ( - event: TransactionEvent, -): TransactionEventType => { +export const getReplayDocEventType = ( + event: Devtools.EventsForDocReplay, +): ReplayDocEventType => { for (const docEvent of event) { if ( docEvent.type === DocEventType.StatusChanged || @@ -160,36 +155,36 @@ export const getTransactionEventType = ( docEvent.type === DocEventType.LocalChange || docEvent.type === DocEventType.RemoteChange ) { - return TransactionEventType.Document; + return ReplayDocEventType.Document; } } - return TransactionEventType.Presence; + return ReplayDocEventType.Presence; }; -export function useTransactionEvents() { +export function useReplayDocEvents() { const { events, hidePresenceEvents, setHidePresenceEvents } = useContext( - TransactionEventsContext, + ReplayDocEventsContext, ); if (events === undefined) { throw new YorkieError( Code.ErrContextNotProvided, - 'useTransactionEvents should be used within YorkieSourceProvider', + 'useReplayDocEvents should be used within YorkieSourceProvider', ); } // create an enhanced events with metadata const enhancedEvents = useMemo(() => { return events.map((event) => { - const transactionEventType = getTransactionEventType(event); + const replayDocEventType = getReplayDocEventType(event); return { event, - transactionEventType, + replayDocEventType: replayDocEventType, isFiltered: hidePresenceEvents && - transactionEventType === TransactionEventType.Presence, + replayDocEventType === ReplayDocEventType.Presence, }; }); }, [hidePresenceEvents, events]); diff --git a/packages/devtools/src/devtools/panel/index.tsx b/packages/devtools/src/devtools/panel/index.tsx index 4ced813a3..48681e323 100644 --- a/packages/devtools/src/devtools/panel/index.tsx +++ b/packages/devtools/src/devtools/panel/index.tsx @@ -23,7 +23,7 @@ import { SelectedPresenceProvider } from '../contexts/SelectedPresence'; import { YorkieSourceProvider, useCurrentDocKey, - useTransactionEvents, + useReplayDocEvents, useYorkieDoc, } from '../contexts/YorkieSource'; import { Document } from '../tabs/Document'; @@ -34,7 +34,7 @@ import { Separator } from '../components/ResizableSeparator'; const Panel = () => { const currentDocKey = useCurrentDocKey(); const { originalEvents, presenceFilteredEvents, hidePresenceEvents } = - useTransactionEvents(); + useReplayDocEvents(); const [, setDoc] = useYorkieDoc(); const [selectedEventIndexInfo, setSelectedEventIndexInfo] = useState({ index: null, @@ -91,7 +91,7 @@ const Panel = () => { filteredEventIndex++; } - doc.applyTransactionEvent(originalEvents[eventIndex].event); + doc.applyEventsForDocReplay(originalEvents[eventIndex].event); eventIndex++; } diff --git a/packages/devtools/src/devtools/tabs/History.tsx b/packages/devtools/src/devtools/tabs/History.tsx index d01af06c9..0c3677210 100644 --- a/packages/devtools/src/devtools/tabs/History.tsx +++ b/packages/devtools/src/devtools/tabs/History.tsx @@ -15,18 +15,18 @@ */ import { useEffect, useState, useRef } from 'react'; -import { DocEventType, Change, type TransactionEvent } from 'yorkie-js-sdk'; +import { DocEventType, Change, Devtools } from 'yorkie-js-sdk'; import Slider from 'rc-slider'; import { JSONView } from '../components/JsonView'; import { CursorIcon, DocumentIcon } from '../icons'; import { - TransactionEventType, - useTransactionEvents, + ReplayDocEventType, + useReplayDocEvents, } from '../contexts/YorkieSource'; const SLIDER_MARK_WIDTH = 24; -const getEventInfo = (event: TransactionEvent) => { +const getEventInfo = (event: Devtools.EventsForDocReplay) => { const info = []; for (const docEvent of event) { if ( @@ -75,7 +75,7 @@ export function History({ presenceFilteredEvents, hidePresenceEvents, setHidePresenceEvents, - } = useTransactionEvents(); + } = useReplayDocEvents(); const events = hidePresenceEvents ? presenceFilteredEvents : originalEvents; @@ -109,13 +109,13 @@ export function History({ const marks = {}; for (const [index, event] of events.entries()) { const source = event.event[0].source; - const transactionEventType = event.transactionEventType; + const replayDocEventType = event.replayDocEventType; marks[index] = ( - {transactionEventType === TransactionEventType.Presence ? ( + {replayDocEventType === ReplayDocEventType.Presence ? ( ) : ( diff --git a/packages/sdk/src/devtools/index.ts b/packages/sdk/src/devtools/index.ts index 5a30162fc..8f886e64b 100644 --- a/packages/sdk/src/devtools/index.ts +++ b/packages/sdk/src/devtools/index.ts @@ -14,33 +14,29 @@ * limitations under the License. */ -import { - Document, - Indexable, - TransactionEvent, -} from '@yorkie-js-sdk/src/yorkie'; -import { DocEventType } from '@yorkie-js-sdk/src/document/document'; +import { Document, Indexable } from '@yorkie-js-sdk/src/yorkie'; import { logger } from '@yorkie-js-sdk/src/util/logger'; import type * as DevTools from './protocol'; import { EventSourceDevPanel, EventSourceSDK } from './protocol'; +import { EventsForDocReplay, isEventsForDocReplay } from './types'; type DevtoolsStatus = 'connected' | 'disconnected' | 'synced'; let devtoolsStatus: DevtoolsStatus = 'disconnected'; const unsubsByDocKey = new Map void>>(); /** - * `transactionEventsByDocKey` stores all events in the document for replaying + * `replayEventsByDocKey` stores all events in the document for replaying * (time-traveling feature) in Devtools. Later, external storage such as * IndexedDB will be used. */ -const transactionEventsByDocKey = new Map>(); +const replayEventsByDocKey = new Map>(); declare global { interface Window { - transactionEventsByDocKey: Map>; + replayEventsByDocKey: Map>; } } if (typeof window !== 'undefined') { - window.transactionEventsByDocKey = transactionEventsByDocKey; + window.replayEventsByDocKey = replayEventsByDocKey; } /** @@ -79,25 +75,13 @@ export function setupDevtools( return; } - transactionEventsByDocKey.set(doc.getKey(), []); + replayEventsByDocKey.set(doc.getKey(), []); const unsub = doc.subscribe('all', (event) => { - if ( - event.some( - (docEvent) => - docEvent.type !== DocEventType.StatusChanged && - docEvent.type !== DocEventType.Snapshot && - docEvent.type !== DocEventType.LocalChange && - docEvent.type !== DocEventType.RemoteChange && - docEvent.type !== DocEventType.Initialized && - docEvent.type !== DocEventType.Watched && - docEvent.type !== DocEventType.Unwatched && - docEvent.type !== DocEventType.PresenceChanged, - ) - ) { + if (!isEventsForDocReplay(event)) { return; } - transactionEventsByDocKey.get(doc.getKey())!.push(event); + replayEventsByDocKey.get(doc.getKey())!.push(event); if (devtoolsStatus === 'synced') { sendToPanel({ msg: 'doc::sync::partial', @@ -148,7 +132,7 @@ export function setupDevtools( sendToPanel({ msg: 'doc::sync::full', docKey: doc.getKey(), - events: transactionEventsByDocKey.get(doc.getKey())!, + events: replayEventsByDocKey.get(doc.getKey())!, }); logger.info(`[YD] Devtools subscribed. Doc: ${doc.getKey()}`); break; diff --git a/packages/sdk/src/devtools/protocol.ts b/packages/sdk/src/devtools/protocol.ts index 2efc161be..128a05f32 100644 --- a/packages/sdk/src/devtools/protocol.ts +++ b/packages/sdk/src/devtools/protocol.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { TransactionEvent } from '@yorkie-js-sdk/src/document/document'; +import { EventsForDocReplay } from './types'; /** * `EventSourceDevPanel` is the name of the source representing messages @@ -76,7 +76,7 @@ export type SDKToPanelMessage = | { msg: 'doc::sync::full'; docKey: string; - events: Array; + events: Array; } /** * Sent whenever the document is changed. @@ -84,7 +84,7 @@ export type SDKToPanelMessage = | { msg: 'doc::sync::partial'; docKey: string; - event: TransactionEvent; + event: EventsForDocReplay; }; export type FullPanelToSDKMessage = PanelToSDKMessage & { diff --git a/packages/sdk/src/devtools/types.ts b/packages/sdk/src/devtools/types.ts index 7cf2efd89..bea73ae97 100644 --- a/packages/sdk/src/devtools/types.ts +++ b/packages/sdk/src/devtools/types.ts @@ -17,7 +17,21 @@ import type { PrimitiveValue } from '@yorkie-js-sdk/src/document/crdt/primitive'; import type { CRDTTreePosStruct } from '@yorkie-js-sdk/src/document/crdt/tree'; import { CounterValue } from '@yorkie-js-sdk/src/document/crdt/counter'; -import { Json } from '@yorkie-js-sdk/src/document/document'; +import { + Json, + DocEvent, + DocEventType, + type Indexable, + type StatusChangedEvent, + type SnapshotEvent, + type LocalChangeEvent, + type RemoteChangeEvent, + type InitializedEvent, + type WatchedEvent, + type UnwatchedEvent, + type PresenceChangedEvent, +} from '@yorkie-js-sdk/src/document/document'; +import type { OperationInfo } from '@yorkie-js-sdk/src/document/operation/operation'; /** * `Client` represents a client value in devtools. @@ -85,3 +99,53 @@ export type TreeNodeInfo = { path?: Array; pos?: CRDTTreePosStruct; }; + +/** + * `EventForDocReplay` is an event used to replay a document. + */ +export type EventForDocReplay< + P extends Indexable = Indexable, + T = OperationInfo, +> = + | StatusChangedEvent + | SnapshotEvent + | LocalChangeEvent + | RemoteChangeEvent + | InitializedEvent

+ | WatchedEvent

+ | UnwatchedEvent

+ | PresenceChangedEvent

; + +/** + * `EventsForDocReplay` is a list of events used to replay a document. + */ +export type EventsForDocReplay = Array; + +/** + * `isEventForDocReplay` checks if an event can be used to replay a document. + */ +export function isEventForDocReplay( + event: DocEvent, +): event is EventForDocReplay { + const typesForDocReplay = [ + DocEventType.StatusChanged, + DocEventType.Snapshot, + DocEventType.LocalChange, + DocEventType.RemoteChange, + DocEventType.Initialized, + DocEventType.Watched, + DocEventType.Unwatched, + DocEventType.PresenceChanged, + ]; + + return typesForDocReplay.includes(event.type); +} + +/** + * `isEventsForDocReplay` checks if a list of events can be used to replay a document. + */ +export function isEventsForDocReplay( + events: Array, +): events is EventsForDocReplay { + return events.every(isEventForDocReplay); +} diff --git a/packages/sdk/src/document/document.ts b/packages/sdk/src/document/document.ts index 92aa1a39f..19305a3d7 100644 --- a/packages/sdk/src/document/document.ts +++ b/packages/sdk/src/document/document.ts @@ -1718,9 +1718,9 @@ export class Document { } /** - * `applyDocEvent` applies the docEvent into this document. + * `applyEventForDocReplay` applies the given event into this document. */ - public applyDocEvent(event: DocEvent

) { + public applyEventForDocReplay(event: Devtools.EventForDocReplay

) { if (event.type === DocEventType.StatusChanged) { this.applyStatus(event.value.status); if (event.value.status === DocStatus.Attached) { @@ -1782,11 +1782,11 @@ export class Document { } /** - * `applyTransactionEvent` applies the given TransactionEvent into this document. + * `applyEventsForDocReplay` applies the given events into this document. */ - public applyTransactionEvent(event: TransactionEvent

) { + public applyEventsForDocReplay(event: Array>) { for (const docEvent of event) { - this.applyDocEvent(docEvent); + this.applyEventForDocReplay(docEvent); } } From 1a988d312649ea75c1e385da5330161b2301b5fc Mon Sep 17 00:00:00 2001 From: Yourim Cha Date: Mon, 13 Jan 2025 15:52:01 +0900 Subject: [PATCH 2/5] Resolve other type errors in devtools package --- packages/devtools/src/devtools/components/Detail.tsx | 10 +++++++--- packages/devtools/src/devtools/components/Tree.tsx | 4 ++-- packages/sdk/src/yorkie.ts | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/devtools/src/devtools/components/Detail.tsx b/packages/devtools/src/devtools/components/Detail.tsx index f4f49d98a..6eefe0fd8 100644 --- a/packages/devtools/src/devtools/components/Detail.tsx +++ b/packages/devtools/src/devtools/components/Detail.tsx @@ -110,9 +110,13 @@ function TreeGraph({ tree }: { tree: Devtools.TreeNodeInfo }) { [], ); - return flattenTreeWithDepth(tree).map((node) => ( - - )); + return ( + <> + {flattenTreeWithDepth(tree).map((node) => ( + + ))} + + ); } export function TreeDetail({ diff --git a/packages/devtools/src/devtools/components/Tree.tsx b/packages/devtools/src/devtools/components/Tree.tsx index 002bfbfd3..974535643 100644 --- a/packages/devtools/src/devtools/components/Tree.tsx +++ b/packages/devtools/src/devtools/components/Tree.tsx @@ -22,7 +22,7 @@ import useResizeObserver from 'use-resize-observer'; import { useSelectedNode } from '../contexts/SelectedNode'; import { useSelectedPresence } from '../contexts/SelectedPresence'; -import type { Devtools } from 'yorkie-js-sdk'; +import type { Devtools, Json } from 'yorkie-js-sdk'; import { ArrayIcon, @@ -47,7 +47,7 @@ export type UserNode = Devtools.Client & { export type PresenceJsonNode = { id: string; key: string; - value: Devtools.Json; + value: Json; isLastChild: boolean; type: 'JSON'; }; diff --git a/packages/sdk/src/yorkie.ts b/packages/sdk/src/yorkie.ts index ae1aff558..04af886ee 100644 --- a/packages/sdk/src/yorkie.ts +++ b/packages/sdk/src/yorkie.ts @@ -44,6 +44,7 @@ export { DocSyncStatus, DocStatus, type Indexable, + type Json, type DocEvent, type TransactionEvent, Document, From 841c444c503df802b244f23138881b0506586707 Mon Sep 17 00:00:00 2001 From: Yourim Cha Date: Mon, 13 Jan 2025 15:53:33 +0900 Subject: [PATCH 3/5] Update devtools-publish workflow trigger condition --- .github/workflows/devtools-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/devtools-publish.yml b/.github/workflows/devtools-publish.yml index d7ede4057..4b2752fee 100644 --- a/.github/workflows/devtools-publish.yml +++ b/.github/workflows/devtools-publish.yml @@ -5,7 +5,7 @@ on: branches: - 'main' paths: - - packages/devtools/** + - packages/devtools/package.json jobs: build-and-deploy: runs-on: ubuntu-latest From 097af478ec0b1015c4c93c8e368340bd0c3b98fe Mon Sep 17 00:00:00 2001 From: Youngteac Hong Date: Tue, 14 Jan 2025 11:18:23 +0900 Subject: [PATCH 4/5] Revise the codes --- .../src/devtools/contexts/YorkieSource.tsx | 6 ++-- .../devtools/src/devtools/panel/index.tsx | 2 +- .../devtools/src/devtools/tabs/History.tsx | 2 +- packages/sdk/src/devtools/index.ts | 8 ++--- packages/sdk/src/devtools/protocol.ts | 6 ++-- packages/sdk/src/devtools/types.ts | 22 ++++++------- packages/sdk/src/document/document.ts | 32 +++++++++---------- packages/sdk/src/yorkie.ts | 2 +- 8 files changed, 39 insertions(+), 41 deletions(-) diff --git a/packages/devtools/src/devtools/contexts/YorkieSource.tsx b/packages/devtools/src/devtools/contexts/YorkieSource.tsx index 8362a38b8..417993d4c 100644 --- a/packages/devtools/src/devtools/contexts/YorkieSource.tsx +++ b/packages/devtools/src/devtools/contexts/YorkieSource.tsx @@ -31,7 +31,7 @@ import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; const DocKeyContext = createContext(null); const YorkieDocContext = createContext(null); const ReplayDocEventsContext = createContext<{ - events: Array; + events: Array; hidePresenceEvents: boolean; setHidePresenceEvents: Dispatch>; }>(null); @@ -44,7 +44,7 @@ export function YorkieSourceProvider({ children }: Props) { const [currentDocKey, setCurrentDocKey] = useState(''); const [doc, setDoc] = useState(null); const [replayDocEvents, setReplayDocEvents] = useState< - Array + Array >([]); // filter out presence events @@ -146,7 +146,7 @@ export enum ReplayDocEventType { } export const getReplayDocEventType = ( - event: Devtools.EventsForDocReplay, + event: Devtools.DocEventsForReplay, ): ReplayDocEventType => { for (const docEvent of event) { if ( diff --git a/packages/devtools/src/devtools/panel/index.tsx b/packages/devtools/src/devtools/panel/index.tsx index 48681e323..34cf349b0 100644 --- a/packages/devtools/src/devtools/panel/index.tsx +++ b/packages/devtools/src/devtools/panel/index.tsx @@ -91,7 +91,7 @@ const Panel = () => { filteredEventIndex++; } - doc.applyEventsForDocReplay(originalEvents[eventIndex].event); + doc.applyDocEventsForReplay(originalEvents[eventIndex].event); eventIndex++; } diff --git a/packages/devtools/src/devtools/tabs/History.tsx b/packages/devtools/src/devtools/tabs/History.tsx index 0c3677210..de87d3afa 100644 --- a/packages/devtools/src/devtools/tabs/History.tsx +++ b/packages/devtools/src/devtools/tabs/History.tsx @@ -26,7 +26,7 @@ import { const SLIDER_MARK_WIDTH = 24; -const getEventInfo = (event: Devtools.EventsForDocReplay) => { +const getEventInfo = (event: Devtools.DocEventsForReplay) => { const info = []; for (const docEvent of event) { if ( diff --git a/packages/sdk/src/devtools/index.ts b/packages/sdk/src/devtools/index.ts index 8f886e64b..1036261e5 100644 --- a/packages/sdk/src/devtools/index.ts +++ b/packages/sdk/src/devtools/index.ts @@ -18,7 +18,7 @@ import { Document, Indexable } from '@yorkie-js-sdk/src/yorkie'; import { logger } from '@yorkie-js-sdk/src/util/logger'; import type * as DevTools from './protocol'; import { EventSourceDevPanel, EventSourceSDK } from './protocol'; -import { EventsForDocReplay, isEventsForDocReplay } from './types'; +import { DocEventsForReplay, isDocEventsForReplay } from './types'; type DevtoolsStatus = 'connected' | 'disconnected' | 'synced'; let devtoolsStatus: DevtoolsStatus = 'disconnected'; @@ -29,10 +29,10 @@ const unsubsByDocKey = new Map void>>(); * (time-traveling feature) in Devtools. Later, external storage such as * IndexedDB will be used. */ -const replayEventsByDocKey = new Map>(); +const replayEventsByDocKey = new Map>(); declare global { interface Window { - replayEventsByDocKey: Map>; + replayEventsByDocKey: Map>; } } if (typeof window !== 'undefined') { @@ -77,7 +77,7 @@ export function setupDevtools( replayEventsByDocKey.set(doc.getKey(), []); const unsub = doc.subscribe('all', (event) => { - if (!isEventsForDocReplay(event)) { + if (!isDocEventsForReplay(event)) { return; } diff --git a/packages/sdk/src/devtools/protocol.ts b/packages/sdk/src/devtools/protocol.ts index 128a05f32..9858f4a2d 100644 --- a/packages/sdk/src/devtools/protocol.ts +++ b/packages/sdk/src/devtools/protocol.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { EventsForDocReplay } from './types'; +import { DocEventsForReplay } from './types'; /** * `EventSourceDevPanel` is the name of the source representing messages @@ -76,7 +76,7 @@ export type SDKToPanelMessage = | { msg: 'doc::sync::full'; docKey: string; - events: Array; + events: Array; } /** * Sent whenever the document is changed. @@ -84,7 +84,7 @@ export type SDKToPanelMessage = | { msg: 'doc::sync::partial'; docKey: string; - event: EventsForDocReplay; + event: DocEventsForReplay; }; export type FullPanelToSDKMessage = PanelToSDKMessage & { diff --git a/packages/sdk/src/devtools/types.ts b/packages/sdk/src/devtools/types.ts index bea73ae97..b5e1e05fc 100644 --- a/packages/sdk/src/devtools/types.ts +++ b/packages/sdk/src/devtools/types.ts @@ -101,9 +101,9 @@ export type TreeNodeInfo = { }; /** - * `EventForDocReplay` is an event used to replay a document. + * `DocEventForReplay` is an event used to replay a document. */ -export type EventForDocReplay< +export type DocEventForReplay< P extends Indexable = Indexable, T = OperationInfo, > = @@ -117,16 +117,16 @@ export type EventForDocReplay< | PresenceChangedEvent

; /** - * `EventsForDocReplay` is a list of events used to replay a document. + * `DocEventsForReplay` is a list of events used to replay a document. */ -export type EventsForDocReplay = Array; +export type DocEventsForReplay = Array; /** - * `isEventForDocReplay` checks if an event can be used to replay a document. + * `isDocEventForReplay` checks if an event can be used to replay a document. */ -export function isEventForDocReplay( +export function isDocEventForReplay( event: DocEvent, -): event is EventForDocReplay { +): event is DocEventForReplay { const typesForDocReplay = [ DocEventType.StatusChanged, DocEventType.Snapshot, @@ -142,10 +142,10 @@ export function isEventForDocReplay( } /** - * `isEventsForDocReplay` checks if a list of events can be used to replay a document. + * `isDocEventsForReplay` checks if a list of events can be used to replay a document. */ -export function isEventsForDocReplay( +export function isDocEventsForReplay( events: Array, -): events is EventsForDocReplay { - return events.every(isEventForDocReplay); +): events is DocEventsForReplay { + return events.every(isDocEventForReplay); } diff --git a/packages/sdk/src/document/document.ts b/packages/sdk/src/document/document.ts index 19305a3d7..780d15581 100644 --- a/packages/sdk/src/document/document.ts +++ b/packages/sdk/src/document/document.ts @@ -228,12 +228,10 @@ export type DocEvent

= | AuthErrorEvent; /** - * `TransactionEvent` represents document events that occur within + * `DocEvents` represents document events that occur within * a single transaction (e.g., doc.update). */ -export type TransactionEvent

= Array< - DocEvent

->; +export type DocEvents

= Array>; /** * @internal @@ -449,7 +447,7 @@ type DocEventCallbackMap

= { broadcast: NextFn; 'local-broadcast': NextFn; 'auth-error': NextFn; - all: NextFn>; + all: NextFn>; }; export type DocEventTopic = keyof DocEventCallbackMap; export type DocEventCallback

= @@ -633,8 +631,8 @@ export class Document { presences: Map; }; - private eventStream: Observable>; - private eventStreamObserver!: Observer>; + private eventStream: Observable>; + private eventStreamObserver!: Observer>; /** * `onlineClients` is a set of client IDs that are currently online. @@ -673,7 +671,7 @@ export class Document { this.checkpoint = InitialCheckpoint; this.localChanges = []; - this.eventStream = createObservable>((observer) => { + this.eventStream = createObservable>((observer) => { this.eventStreamObserver = observer; }); @@ -771,7 +769,7 @@ export class Document { // 03. Publish the document change event. // NOTE(chacha912): Check opInfos, which represent the actually executed operations. - const event: TransactionEvent

= []; + const event: DocEvents

= []; if (opInfos.length > 0) { event.push({ type: DocEventType.LocalChange, @@ -1168,7 +1166,7 @@ export class Document { * `publish` triggers an event in this document, which can be received by * callback functions from document.subscribe(). */ - public publish(event: TransactionEvent

) { + public publish(event: DocEvents

) { if (this.eventStreamObserver) { this.eventStreamObserver.next(event); } @@ -1522,7 +1520,7 @@ export class Document { this.ensureClone(); change.execute(this.clone!.root, this.clone!.presences, source); - const event: TransactionEvent

= []; + const event: DocEvents

= []; const actorID = change.getID().getActorID(); if (change.hasPresenceChange() && this.onlineClients.has(actorID)) { const presenceChange = change.getPresenceChange()!; @@ -1718,9 +1716,9 @@ export class Document { } /** - * `applyEventForDocReplay` applies the given event into this document. + * `applyDocEventForReplay` applies the given event into this document. */ - public applyEventForDocReplay(event: Devtools.EventForDocReplay

) { + public applyDocEventForReplay(event: Devtools.DocEventForReplay

) { if (event.type === DocEventType.StatusChanged) { this.applyStatus(event.value.status); if (event.value.status === DocStatus.Attached) { @@ -1784,9 +1782,9 @@ export class Document { /** * `applyEventsForDocReplay` applies the given events into this document. */ - public applyEventsForDocReplay(event: Array>) { + public applyDocEventsForReplay(event: Array>) { for (const docEvent of event) { - this.applyEventForDocReplay(docEvent); + this.applyDocEventForReplay(docEvent); } } @@ -2034,7 +2032,7 @@ export class Document { this.localChanges.push(change); this.changeID = change.getID(); const actorID = this.changeID.getActorID(); - const event: TransactionEvent

= []; + const event: DocEvents

= []; if (opInfos.length > 0) { event.push({ type: DocEventType.LocalChange, @@ -2133,7 +2131,7 @@ export class Document { this.localChanges.push(change); this.changeID = change.getID(); const actorID = this.changeID.getActorID(); - const event: TransactionEvent

= []; + const event: DocEvents

= []; if (opInfos.length > 0) { event.push({ type: DocEventType.LocalChange, diff --git a/packages/sdk/src/yorkie.ts b/packages/sdk/src/yorkie.ts index 04af886ee..8abcb9ccc 100644 --- a/packages/sdk/src/yorkie.ts +++ b/packages/sdk/src/yorkie.ts @@ -46,7 +46,7 @@ export { type Indexable, type Json, type DocEvent, - type TransactionEvent, + type DocEvents, Document, type ChangeInfo, } from '@yorkie-js-sdk/src/document/document'; From 0a9b23daaae7bdbf87406e515d40ac4ccb2b8d25 Mon Sep 17 00:00:00 2001 From: Yourim Cha Date: Tue, 14 Jan 2025 15:43:05 +0900 Subject: [PATCH 5/5] Rename ReplayDocEventType to DocEventScope --- .../src/devtools/contexts/YorkieSource.tsx | 59 ++++++++++--------- .../devtools/src/devtools/panel/index.tsx | 4 +- .../devtools/src/devtools/tabs/History.tsx | 14 ++--- packages/sdk/src/devtools/index.ts | 14 ++--- packages/sdk/src/devtools/types.ts | 4 +- packages/sdk/src/document/document.ts | 2 +- 6 files changed, 47 insertions(+), 50 deletions(-) diff --git a/packages/devtools/src/devtools/contexts/YorkieSource.tsx b/packages/devtools/src/devtools/contexts/YorkieSource.tsx index 417993d4c..0a280b2d1 100644 --- a/packages/devtools/src/devtools/contexts/YorkieSource.tsx +++ b/packages/devtools/src/devtools/contexts/YorkieSource.tsx @@ -30,7 +30,7 @@ import { Code, YorkieError } from '@yorkie-js-sdk/src/util/error'; const DocKeyContext = createContext(null); const YorkieDocContext = createContext(null); -const ReplayDocEventsContext = createContext<{ +const DocEventsForReplayContext = createContext<{ events: Array; hidePresenceEvents: boolean; setHidePresenceEvents: Dispatch>; @@ -43,7 +43,7 @@ type Props = { export function YorkieSourceProvider({ children }: Props) { const [currentDocKey, setCurrentDocKey] = useState(''); const [doc, setDoc] = useState(null); - const [replayDocEvents, setReplayDocEvents] = useState< + const [docEventsForReplay, setDocEventsForReplay] = useState< Array >([]); @@ -52,7 +52,7 @@ export function YorkieSourceProvider({ children }: Props) { const resetDocument = () => { setCurrentDocKey(''); - setReplayDocEvents([]); + setDocEventsForReplay([]); setDoc(null); }; @@ -72,11 +72,11 @@ export function YorkieSourceProvider({ children }: Props) { case 'doc::sync::full': // TODO(chacha912): Notify the user that they need to use the latest version of Yorkie-JS-SDK. if (message.events === undefined) break; - setReplayDocEvents(message.events); + setDocEventsForReplay(message.events); break; case 'doc::sync::partial': if (message.event === undefined) break; - setReplayDocEvents((events) => [...events, message.event]); + setDocEventsForReplay((events) => [...events, message.event]); break; } }, []); @@ -103,9 +103,9 @@ export function YorkieSourceProvider({ children }: Props) { return ( - {children} - + ); } @@ -140,51 +140,54 @@ export function useYorkieDoc() { return value; } -export enum ReplayDocEventType { - Document = 'document', +/** + * `DocEventScope` represents the scope of the document event. + */ +export enum DocEventScope { + Root = 'root', Presence = 'presence', + Document = 'document', } -export const getReplayDocEventType = ( - event: Devtools.DocEventsForReplay, -): ReplayDocEventType => { - for (const docEvent of event) { +export const getDocEventsScope = ( + events: Devtools.DocEventsForReplay, +): DocEventScope => { + for (const e of events) { if ( - docEvent.type === DocEventType.StatusChanged || - docEvent.type === DocEventType.Snapshot || - docEvent.type === DocEventType.LocalChange || - docEvent.type === DocEventType.RemoteChange + e.type === DocEventType.Snapshot || + e.type === DocEventType.LocalChange || + e.type === DocEventType.RemoteChange ) { - return ReplayDocEventType.Document; + return DocEventScope.Root; + } else if (e.type === DocEventType.StatusChanged) { + return DocEventScope.Document; } } - return ReplayDocEventType.Presence; + return DocEventScope.Presence; }; -export function useReplayDocEvents() { +export function useDocEventsForReplay() { const { events, hidePresenceEvents, setHidePresenceEvents } = useContext( - ReplayDocEventsContext, + DocEventsForReplayContext, ); if (events === undefined) { throw new YorkieError( Code.ErrContextNotProvided, - 'useReplayDocEvents should be used within YorkieSourceProvider', + 'useDocEventsForReplay should be used within YorkieSourceProvider', ); } // create an enhanced events with metadata const enhancedEvents = useMemo(() => { return events.map((event) => { - const replayDocEventType = getReplayDocEventType(event); + const scope = getDocEventsScope(event); return { event, - replayDocEventType: replayDocEventType, - isFiltered: - hidePresenceEvents && - replayDocEventType === ReplayDocEventType.Presence, + scope, + isFiltered: hidePresenceEvents && scope === DocEventScope.Presence, }; }); }, [hidePresenceEvents, events]); diff --git a/packages/devtools/src/devtools/panel/index.tsx b/packages/devtools/src/devtools/panel/index.tsx index 34cf349b0..c3059be00 100644 --- a/packages/devtools/src/devtools/panel/index.tsx +++ b/packages/devtools/src/devtools/panel/index.tsx @@ -23,7 +23,7 @@ import { SelectedPresenceProvider } from '../contexts/SelectedPresence'; import { YorkieSourceProvider, useCurrentDocKey, - useReplayDocEvents, + useDocEventsForReplay, useYorkieDoc, } from '../contexts/YorkieSource'; import { Document } from '../tabs/Document'; @@ -34,7 +34,7 @@ import { Separator } from '../components/ResizableSeparator'; const Panel = () => { const currentDocKey = useCurrentDocKey(); const { originalEvents, presenceFilteredEvents, hidePresenceEvents } = - useReplayDocEvents(); + useDocEventsForReplay(); const [, setDoc] = useYorkieDoc(); const [selectedEventIndexInfo, setSelectedEventIndexInfo] = useState({ index: null, diff --git a/packages/devtools/src/devtools/tabs/History.tsx b/packages/devtools/src/devtools/tabs/History.tsx index de87d3afa..1a7518570 100644 --- a/packages/devtools/src/devtools/tabs/History.tsx +++ b/packages/devtools/src/devtools/tabs/History.tsx @@ -19,10 +19,7 @@ import { DocEventType, Change, Devtools } from 'yorkie-js-sdk'; import Slider from 'rc-slider'; import { JSONView } from '../components/JsonView'; import { CursorIcon, DocumentIcon } from '../icons'; -import { - ReplayDocEventType, - useReplayDocEvents, -} from '../contexts/YorkieSource'; +import { DocEventScope, useDocEventsForReplay } from '../contexts/YorkieSource'; const SLIDER_MARK_WIDTH = 24; @@ -75,7 +72,7 @@ export function History({ presenceFilteredEvents, hidePresenceEvents, setHidePresenceEvents, - } = useReplayDocEvents(); + } = useDocEventsForReplay(); const events = hidePresenceEvents ? presenceFilteredEvents : originalEvents; @@ -109,13 +106,10 @@ export function History({ const marks = {}; for (const [index, event] of events.entries()) { const source = event.event[0].source; - const replayDocEventType = event.replayDocEventType; marks[index] = ( - - {replayDocEventType === ReplayDocEventType.Presence ? ( + + {event.scope === DocEventScope.Presence ? ( ) : ( diff --git a/packages/sdk/src/devtools/index.ts b/packages/sdk/src/devtools/index.ts index 1036261e5..e103340cf 100644 --- a/packages/sdk/src/devtools/index.ts +++ b/packages/sdk/src/devtools/index.ts @@ -25,18 +25,18 @@ let devtoolsStatus: DevtoolsStatus = 'disconnected'; const unsubsByDocKey = new Map void>>(); /** - * `replayEventsByDocKey` stores all events in the document for replaying + * `docEventsForReplayByDocKey` stores all events in the document for replaying * (time-traveling feature) in Devtools. Later, external storage such as * IndexedDB will be used. */ -const replayEventsByDocKey = new Map>(); +const docEventsForReplayByDocKey = new Map>(); declare global { interface Window { - replayEventsByDocKey: Map>; + docEventsForReplayByDocKey: Map>; } } if (typeof window !== 'undefined') { - window.replayEventsByDocKey = replayEventsByDocKey; + window.docEventsForReplayByDocKey = docEventsForReplayByDocKey; } /** @@ -75,13 +75,13 @@ export function setupDevtools( return; } - replayEventsByDocKey.set(doc.getKey(), []); + docEventsForReplayByDocKey.set(doc.getKey(), []); const unsub = doc.subscribe('all', (event) => { if (!isDocEventsForReplay(event)) { return; } - replayEventsByDocKey.get(doc.getKey())!.push(event); + docEventsForReplayByDocKey.get(doc.getKey())!.push(event); if (devtoolsStatus === 'synced') { sendToPanel({ msg: 'doc::sync::partial', @@ -132,7 +132,7 @@ export function setupDevtools( sendToPanel({ msg: 'doc::sync::full', docKey: doc.getKey(), - events: replayEventsByDocKey.get(doc.getKey())!, + events: docEventsForReplayByDocKey.get(doc.getKey())!, }); logger.info(`[YD] Devtools subscribed. Doc: ${doc.getKey()}`); break; diff --git a/packages/sdk/src/devtools/types.ts b/packages/sdk/src/devtools/types.ts index b5e1e05fc..47c9a082b 100644 --- a/packages/sdk/src/devtools/types.ts +++ b/packages/sdk/src/devtools/types.ts @@ -127,7 +127,7 @@ export type DocEventsForReplay = Array; export function isDocEventForReplay( event: DocEvent, ): event is DocEventForReplay { - const typesForDocReplay = [ + const types = [ DocEventType.StatusChanged, DocEventType.Snapshot, DocEventType.LocalChange, @@ -138,7 +138,7 @@ export function isDocEventForReplay( DocEventType.PresenceChanged, ]; - return typesForDocReplay.includes(event.type); + return types.includes(event.type); } /** diff --git a/packages/sdk/src/document/document.ts b/packages/sdk/src/document/document.ts index 780d15581..025b9572a 100644 --- a/packages/sdk/src/document/document.ts +++ b/packages/sdk/src/document/document.ts @@ -1780,7 +1780,7 @@ export class Document { } /** - * `applyEventsForDocReplay` applies the given events into this document. + * `applyDocEventsForReplay` applies the given events into this document. */ public applyDocEventsForReplay(event: Array>) { for (const docEvent of event) {