Skip to content

Commit

Permalink
Add Root-Only Filter Feature in History Tab (#872)
Browse files Browse the repository at this point in the history
This commit adds a Root-Only Filter in the History tab, allowing users
to focus exclusively on changes to the Root of the Document.
Previously, the History tab logs both content changes and Presence events
(such as cursor movements), which can result in a high volume of events
that may not always be relevant for debugging content changes.
  • Loading branch information
gwbaik9717 authored Jul 25, 2024
1 parent 8314097 commit 7edae23
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 46 deletions.
85 changes: 78 additions & 7 deletions tools/devtools/src/devtools/contexts/YorkieSource.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,30 @@
* limitations under the License.
*/

import type { ReactNode } from 'react';
import type { Dispatch, ReactNode, SetStateAction } from 'react';
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react';

import type { SDKToPanelMessage, TransactionEvent } from 'yorkie-js-sdk';
import {
DocEventType,
type SDKToPanelMessage,
type TransactionEvent,
} from 'yorkie-js-sdk';
import { connectPort, sendToSDK } from '../../port';

const DocKeyContext = createContext<string>(null);
const YorkieDocContext = createContext(null);
const TransactionEventsContext = createContext<Array<TransactionEvent>>(null);
const TransactionEventsContext = createContext<{
events: Array<TransactionEvent>;
hidePresenceEvents: boolean;
setHidePresenceEvents: Dispatch<SetStateAction<boolean>>;
}>(null);

type Props = {
children?: ReactNode;
Expand All @@ -41,6 +50,10 @@ export function YorkieSourceProvider({ children }: Props) {
Array<TransactionEvent>
>([]);

// filter out presence events
const [hideTransactionPresenceEvents, setHideTransactionPresenceEvents] =
useState(false);

const resetDocument = () => {
setCurrentDocKey('');
setTransactionEvents([]);
Expand Down Expand Up @@ -94,7 +107,13 @@ export function YorkieSourceProvider({ children }: Props) {

return (
<DocKeyContext.Provider value={currentDocKey}>
<TransactionEventsContext.Provider value={transactionEvents}>
<TransactionEventsContext.Provider
value={{
events: transactionEvents,
hidePresenceEvents: hideTransactionPresenceEvents,
setHidePresenceEvents: setHideTransactionPresenceEvents,
}}
>
<YorkieDocContext.Provider value={[doc, setDoc]}>
{children}
</YorkieDocContext.Provider>
Expand All @@ -121,12 +140,64 @@ export function useYorkieDoc() {
return value;
}

export enum TransactionEventType {
Document = 'document',
Presence = 'presence',
}

export const getTransactionEventType = (
event: TransactionEvent,
): TransactionEventType => {
for (const docEvent of event) {
if (
docEvent.type === DocEventType.StatusChanged ||
docEvent.type === DocEventType.Snapshot ||
docEvent.type === DocEventType.LocalChange ||
docEvent.type === DocEventType.RemoteChange
) {
return TransactionEventType.Document;
}
}

return TransactionEventType.Presence;
};

export function useTransactionEvents() {
const value = useContext(TransactionEventsContext);
if (value === undefined) {
const { events, hidePresenceEvents, setHidePresenceEvents } = useContext(
TransactionEventsContext,
);

if (events === undefined) {
throw new Error(
'useTransactionEvents should be used within YorkieSourceProvider',
);
}
return value;

// create an enhanced events with metadata
const enhancedEvents = useMemo(() => {
return events.map((event) => {
const transactionEventType = getTransactionEventType(event);

return {
event,
transactionEventType,
isFiltered:
hidePresenceEvents &&
transactionEventType === TransactionEventType.Presence,
};
});
}, [hidePresenceEvents, events]);

// filter out presence events from the original events
const presenceFilteredEvents = useMemo(() => {
if (!hidePresenceEvents) return enhancedEvents;
return enhancedEvents.filter((e) => !e.isFiltered);
}, [enhancedEvents]);

return {
originalEvents: enhancedEvents,
presenceFilteredEvents,
hidePresenceEvents,
setHidePresenceEvents,
};
}
56 changes: 43 additions & 13 deletions tools/devtools/src/devtools/panel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { createRoot } from 'react-dom/client';
import { useEffect, useState } from 'react';
import yorkie from 'yorkie-js-sdk';
import { useResizable } from 'react-resizable-layout';

import { SelectedNodeProvider } from '../contexts/SelectedNode';
import { SelectedPresenceProvider } from '../contexts/SelectedPresence';
import {
Expand All @@ -34,7 +33,8 @@ import { Separator } from '../components/ResizableSeparator';

const Panel = () => {
const currentDocKey = useCurrentDocKey();
const events = useTransactionEvents();
const { originalEvents, presenceFilteredEvents, hidePresenceEvents } =
useTransactionEvents();
const [, setDoc] = useYorkieDoc();
const [selectedEventIndexInfo, setSelectedEventIndexInfo] = useState({
index: null,
Expand All @@ -57,6 +57,8 @@ const Panel = () => {
axis: 'x',
initial: 300,
});
const [hidePresenceTab, setHidePresenceTab] = useState(false);
const events = hidePresenceEvents ? presenceFilteredEvents : originalEvents;

useEffect(() => {
if (events.length === 0) {
Expand All @@ -78,13 +80,23 @@ const Panel = () => {

useEffect(() => {
if (selectedEventIndexInfo.index === null) return;

const doc = new yorkie.Document(currentDocKey);
for (let i = 0; i <= selectedEventIndexInfo.index; i++) {
doc.applyTransactionEvent(events[i]);

let eventIndex = 0;
let filteredEventIndex = 0;

while (filteredEventIndex <= selectedEventIndexInfo.index) {
if (!originalEvents[eventIndex].isFiltered) {
filteredEventIndex++;
}

doc.applyTransactionEvent(originalEvents[eventIndex].event);
eventIndex++;
}

setDoc(doc);
setSelectedEvent(events[selectedEventIndexInfo.index]);
setSelectedEvent(events[selectedEventIndexInfo.index].event);
}, [selectedEventIndexInfo]);

if (!currentDocKey) {
Expand Down Expand Up @@ -117,22 +129,40 @@ const Panel = () => {
selectedEventIndexInfo={selectedEventIndexInfo}
setSelectedEventIndexInfo={setSelectedEventIndexInfo}
/>

<Separator
dir={'horizontal'}
isDragging={isHistoryDragging}
{...historySeparatorProps}
/>

<div className="devtools-data">
<SelectedNodeProvider>
<Document style={{ width: documentW }} />
<Document
style={{
width: hidePresenceTab ? '100%' : documentW,
maxWidth: hidePresenceTab ? '100%' : '90%',
borderRight: hidePresenceTab
? 'none'
: '1px solid var(--gray-300)',
}}
hidePresenceTab={hidePresenceTab}
setHidePresenceTab={setHidePresenceTab}
/>
</SelectedNodeProvider>
<Separator
isDragging={isDocumentDragging}
{...documentSeparatorProps}
/>
<SelectedPresenceProvider>
<Presence />
</SelectedPresenceProvider>

{!hidePresenceTab && (
<>
<Separator
isDragging={isDocumentDragging}
{...documentSeparatorProps}
/>

<SelectedPresenceProvider>
<Presence />
</SelectedPresenceProvider>
</>
)}
</div>
</div>
);
Expand Down
4 changes: 4 additions & 0 deletions tools/devtools/src/devtools/panel/slider.css
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,7 @@
.rc-slider-mark-text-active .mark-remote {
color: var(--blue-0);
}

.history-slider-wrap[data-length='1'] .rc-slider-rail {
display: none;
}
9 changes: 3 additions & 6 deletions tools/devtools/src/devtools/panel/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,13 @@
width: 100%;
}

.devtools-history-toolbar {
.devtools-tab-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
}

.toggle-history-btn {
.toggle-tab-btn {
margin-left: 4px;
padding: 2px 6px;
border: 1px solid var(--gray-300);
Expand All @@ -100,7 +100,7 @@
font-size: 10px;
}

.toggle-history-btn:hover {
.toggle-tab-btn:hover {
background: var(--gray-200);
}

Expand Down Expand Up @@ -152,9 +152,6 @@

.yorkie-root {
min-width: 10%;
max-width: 90%;
width: 60%;
border-right: 1px solid var(--gray-300);
}

.yorkie-presence {
Expand Down
15 changes: 13 additions & 2 deletions tools/devtools/src/devtools/tabs/Document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { useSelectedNode } from '../contexts/SelectedNode';
import { useCurrentDocKey, useYorkieDoc } from '../contexts/YorkieSource';
import { CloseIcon } from '../icons';

export function Document({ style }) {
export function Document({ style, hidePresenceTab, setHidePresenceTab }) {
const currentDocKey = useCurrentDocKey();
const [doc] = useYorkieDoc();
const [selectedNode, setSelectedNode] = useSelectedNode();
Expand Down Expand Up @@ -60,7 +60,18 @@ export function Document({ style }) {

return (
<div className="yorkie-root content-wrap" style={{ ...style }}>
<div className="title">{currentDocKey || 'Document'}</div>
<div className="devtools-tab-toolbar">
<span className="title">{currentDocKey || 'Document'}</span>
<button
className="toggle-tab-btn"
onClick={() => {
setHidePresenceTab((v: boolean) => !v);
}}
>
{hidePresenceTab ? '◂' : '▸'}
</button>
</div>

<div className="content">
<RootTree root={root} />
{selectedNode && (
Expand Down
Loading

0 comments on commit 7edae23

Please sign in to comment.