Skip to content

Commit

Permalink
History Editor fixes
Browse files Browse the repository at this point in the history
* Added richer logging
  • Loading branch information
GuilhermeF03 committed May 17, 2024
1 parent f7fafd7 commit 12af28c
Show file tree
Hide file tree
Showing 20 changed files with 203 additions and 90 deletions.
98 changes: 52 additions & 46 deletions code/client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import { CommunicationProvider } from '@ui/contexts/communication/CommunicationContext.tsx';
import Document from '@ui/pages/document/Document';
import Header from '@ui/components/header/Header';
import Workspace from '@ui/pages/workspace/Workspace';
Expand All @@ -8,53 +7,60 @@ import './App.scss';
import { ErrorProvider } from '@ui/contexts/error/ErrorContext';
import Sidebar from '@ui/components/sidebar/Sidebar';
import { WorkspaceProvider } from '@ui/contexts/workspace/WorkspaceContext';
import Home from '@ui/pages/home/Home.tsx';
import Home from "@ui/pages/home/Home";
import {ClientLogCaller} from "@/utils/logging";
import getLogger from '@notespace/shared/src/utils/logging';
import {CommunicationProvider} from "@ui/contexts/communication/CommunicationContext";


const logger = getLogger(ClientLogCaller.React);

function App() {
return (
<div className="app">
<ErrorProvider>
<CommunicationProvider>
<Router>
<Header />
<div className="content">
<Routes>
<Route path="/" element={<Home />} />
<Route
path="/workspaces/:wid/*"
element={
<WorkspaceProvider>
<Routes>
<Route
path="/"
element={
<>
<Sidebar />
<Workspace />
</>
}
/>
<Route
path="/:id"
element={
<>
<Sidebar />
<Document />
</>
}
/>
</Routes>
</WorkspaceProvider>
}
/>
<Route path="*" element={<NotFound />} />
</Routes>
</div>
</Router>
</CommunicationProvider>
</ErrorProvider>
</div>
);
logger.logSuccess('App started');
return (
<div className="app">
<ErrorProvider>
<CommunicationProvider>
<Router>
<Header />
<div className="content">
<Routes>
<Route path="/" element={<Home />} />
<Route
path="/workspaces/:wid/*"
element={
<WorkspaceProvider>
<Routes>
<Route
path="/"
element={
<>
<Sidebar />
<Workspace />
</>
}
/>
<Route
path="/:id"
element={
<>
<Sidebar />
<Document />
</>
}
/>
</Routes>
</WorkspaceProvider>
}
/>
<Route path="*" element={<NotFound />} />
</Routes>
</div>
</Router>
</CommunicationProvider>
</ErrorProvider>
</div>
);
}

export default App;
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
BaseSetNodeOperation,
BaseSplitNodeOperation,
Editor,
Range,
Element,
Range,
Text,
} from 'slate';
import {
Expand All @@ -24,7 +24,7 @@ import {
SplitNodeOperation,
UnsetNodeOperation,
} from '@domain/editor/operations/history/types';
import { pointToCursor } from '@domain/editor/slate/utils/selection';
import {pointToCursor} from '@domain/editor/slate/utils/selection';

const reverseTypes: { [key: string]: HistoryOperation['type'] } = {
insert_text: 'remove_text',
Expand Down Expand Up @@ -69,9 +69,9 @@ function toHistoryOperations(editor: Editor, operations: Batch | undefined, reve
case 'remove_text':
return removeTextOperation(operation as BaseRemoveTextOperation);
case 'insert_node':
return nodeOperation(operation as BaseInsertNodeOperation, true);
return nodeOperation(operation as BaseInsertNodeOperation, selectionBefore, true);
case 'remove_node':
return nodeOperation(operation as BaseRemoveNodeOperation, false);
return nodeOperation(operation as BaseRemoveNodeOperation, selectionBefore, false);
case 'merge_node':
return handleNodeOperation(operation as BaseMergeNodeOperation, selectionBefore?.anchor.offset, true);
case 'split_node':
Expand Down Expand Up @@ -104,15 +104,15 @@ function toHistoryOperations(editor: Editor, operations: Batch | undefined, reve

if (operation.text === '') return undefined;

const cursor = pointToCursor(editor, { path: operation.path, offset: 0 });
const cursor = pointToCursor(editor, { path: operation.path, offset: operation.offset });

const start = {
line: operation.path[0],
column: cursor.column + operation.offset,
column: cursor.column + offset(cursor.line),
};
const end = {
line: start.line,
column: start.column + operation.text.length - 1 + offset(start.line),
column: start.column + operation.text.length,
};

const selection = { start, end };
Expand All @@ -122,23 +122,45 @@ function toHistoryOperations(editor: Editor, operations: Batch | undefined, reve
/**
* Handles a slate insert or remove node operation
* @param operation
* @param selectionBefore
* @param insert_mode
*/
function nodeOperation(
operation: BaseInsertNodeOperation | BaseRemoveNodeOperation,
selectionBefore: BaseRange | null,
insert_mode: boolean
): InsertNodeOperation | RemoveNodeOperation | undefined {
const lineOffset = (line: number) => (line === 0 ? 0 : 1);

// Remove whole line
if(operation.path.length === 1) {
const start = pointToCursor(editor, {path: operation.path, offset: 0});
const end = pointToCursor(editor, {path: [operation.path[0] + 1, 0], offset: 0});

const selection = { start, end };
return {
type: insert_mode ? 'insert_node' : 'remove_node',
selection,
node: operation.node,
};
}

if (!Text.isText(operation.node)) return;

if (operation.node.text === '') return undefined;

const offset = (line: number) => (line === 0 ? 0 : 1);
if(!selectionBefore) return undefined

const start = pointToCursor(editor, { path: operation.path, offset: 0 });
const cursor = pointToCursor(editor, selectionBefore.anchor);

const start = {
...cursor,
column: cursor.column + lineOffset(cursor.line),
};

const end = {
...start,
column: start.column + operation.node.text.length - 1 + offset(start.line),
column: start.column + operation.node.text.length,
};

const selection = { start, end };
Expand All @@ -161,9 +183,8 @@ function toHistoryOperations(editor: Editor, operations: Batch | undefined, reve
offset: number | undefined,
merge_mode: boolean
): MergeNodeOperation | SplitNodeOperation | undefined {
if (!Element.isElement(operation.properties)) return undefined;

if (!operation.properties.type) return undefined;
if (operation.path.length > 1) return undefined;
if (!(operation.properties as Element).type) return undefined;

return merge_mode
? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,11 @@ export const getLeafRenderer = ({ attributes, leaf, children }: RenderLeafProps)
if (!renderer) continue;
children = renderer(children);
}
if (leaf.cursor) {
if(leaf.cursor){
const { color, range, styles } = leaf.cursor;
children = Range.isCollapsed(range!) ? (
<Cursor color={color} styles={styles} children={children} />
) : (
<Selection color={color} children={children} />
);
children = Range.isCollapsed(range!)
? <Cursor color={color} styles={styles} children={children} />
: <Selection color={color} children={children} />
}
return <span {...attributes}>{children}</span>;
};
7 changes: 6 additions & 1 deletion code/client/src/domain/editor/slate/utils/selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,13 @@ const pointsToSelection = (editor: Editor, start: Point, end: Point): Selection
*/
export function pointToCursor(editor: Editor, point: Point): Cursor {
const line = point.path[0];
const children = Node.children(editor, [line]);
const children = Array.from(Node.children(editor, [line]));
const cursor: Cursor = { line, column: point.offset };

for (const entry of children) {
// If path has only one element, and it is the same as the first element of the point path - same line
if (point.path.length === 1 && point.path[0] === entry[1][0]) break;

// Else verify if the path is the same
if (Path.equals(entry[1], point.path)) break;

Expand All @@ -54,6 +56,9 @@ export function pointToCursor(editor: Editor, point: Point): Cursor {
cursor.column += text.text.length;
}
}



return cursor;
}

Expand Down
4 changes: 2 additions & 2 deletions code/client/src/ui/pages/document/Document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import Editor from '@ui/pages/document/components/editor/Editor';
import useFugue from '@domain/editor/crdt/useFugue';
import { useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useCommunication } from '@ui/contexts/communication/useCommunication.ts';
import { useCommunication } from '@ui/contexts/communication/useCommunication';
import useError from '@ui/contexts/error/useError';
import useDocumentService from '@services/resource/useResourceService';
import { DocumentResource } from '@notespace/shared/src/workspace/types/resource.ts';
import { DocumentResource } from '@notespace/shared/src/workspace/types/resource';
import './Document.scss';

function Document() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function Editor({ title, fugue, communication }: SlateEditorProps) {
return (
<div className="editor">
<div className="container">
<Slate editor={editor} initialValue={initialValue} onChange={onSelectionChange}>
<Slate editor={editor} initialValue={initialValue} onChange={() => onSelectionChange()}>
<Title title={title} placeholder="Untitled" communication={communication} />
<Toolbar onApplyMark={onFormat} />
<Editable
Expand Down
9 changes: 9 additions & 0 deletions code/client/src/utils/logging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {ColorWrap, LogColor} from '@notespace/shared/src/utils/logging';


export const ClientLogCaller = {
React: ColorWrap(LogColor.Blue, 'React'),
Services: ColorWrap(LogColor.Yellow, 'Services'),
Domain: ColorWrap(LogColor.Green, 'Domain'),
PWA: ColorWrap(LogColor.Red, 'PWA'),
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ export default function errorHandler(error: Error, req: Request, res: Response,
}
const message = response.statusCode === 500 ? 'Internal server error' : error.message;
response.send({ error: message });
console.error(error.stack);
//console.error(error.stack);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const cursorColorsMap = new Map<string, string>();

function onCursorChange() {
return (socket: Socket, range: any) => {
const documentId = rooms.document.get(socket)?.id;
const documentId = rooms.document.get(socket.id)?.id;
if (!documentId) return;
if (!range) {
deleteCursor(socket, documentId);
Expand All @@ -21,7 +21,7 @@ function onCursorChange() {
};
}

function deleteCursor(socket: Socket, documentId: string) {
export function deleteCursor(socket: Socket, documentId: string) {
cursorColorsMap.delete(socket.id);
socket.broadcast.to(documentId).emit('cursorChange', { id: socket.id });
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { Socket } from 'socket.io';
import rooms from '@controllers/ws/rooms/rooms';
import {deleteCursor} from "@controllers/ws/events/document/onCursorChange";

function onLeaveDocument() {
return function (socket: Socket) {

const documentId = rooms.document.get(socket.id)?.id;
if (!documentId) return;
deleteCursor(socket, documentId); // Done so that the cursor is removed when the user leaves the document
rooms.document.leave(socket);
};
}

export default onLeaveDocument;
export default onLeaveDocument;
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function onOperation(service: DocumentsService) {
return async (socket: Socket, operations: Operation[]) => {
if (!operations) throw new InvalidParameterError('Operations are required');

const { id, wid } = rooms.document.get(socket);
const { id, wid } = rooms.document.get(socket.id);
if (!id) throw new ForbiddenError('Client not in a room');

socket.broadcast.to(id).emit('operations', operations);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { Socket } from 'socket.io';
import rooms from '@controllers/ws/rooms/rooms';
import {deleteCursor} from "@controllers/ws/events/document/onCursorChange";

function onLeaveWorkspace() {
return function (socket: Socket) {
const documentId = rooms.document.get(socket.id)?.id;
if (!documentId) return;
deleteCursor(socket, documentId); // Done so that the cursor is removed when the user leaves the document
rooms.workspace.leave(socket);
};
}
Expand Down
Loading

0 comments on commit 12af28c

Please sign in to comment.