Skip to content

Commit

Permalink
slider set to undo stack updates, improved fork handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Meriem-BenIsmail committed Aug 28, 2024
1 parent 8e70e51 commit 0c2c367
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 141 deletions.
121 changes: 77 additions & 44 deletions packages/docprovider/src/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@

import React, { useState, useRef } from 'react';
import '../style/slider.css';
import { requestDocFork, requestDocumentTimeline } from './requests';
import {
requestUndoRedo,
requestDocSession,
requestDocumentTimeline
} from './requests';
import { historyIcon } from '@jupyterlab/ui-components';
import { Notification } from '@jupyterlab/apputils';
import { IForkProvider } from './ydrive';
Expand All @@ -23,86 +27,115 @@ export const TimelineSliderComponent: React.FC<Props> = ({
contentType,
format
}) => {
const [data, setData] = useState({ roomId: '', timestamps: [] });
const [data, setData] = useState({
roomId: '',
timestamps: [],
forkRoom: '',
sessionId: ''
});
const [currentTimestampIndex, setCurrentTimestampIndex] = useState(
data.timestamps.length - 1
);
const [session, setSession]: any = useState();
const [forkRoomID, setForkRoomID]: any = useState();
const [toggle, setToggle] = useState(false);
const [isBtn, setIsBtn] = useState(false);

const isFirstChange = useRef(true);
const isFirstSliderChange = useRef(true);
const sessionRef = useRef<any>(null);

async function fetchTimeline(notebookPath: string) {
try {
const response = await requestDocumentTimeline(
format,
contentType,
notebookPath
);
if (isFirstChange.current) {
const response = await requestDocumentTimeline(
format,
contentType,
notebookPath
);

if (!response.ok) {
if (response.status === 404) {
throw new Error('Not found');
} else if (response.status === 503) {
throw new Error('WebSocket closed');
} else {
throw new Error(`Failed to fetch data: ${response.statusText}`);
if (!response.ok) {
if (response.status === 404) {
throw new Error('Not found');
} else if (response.status === 503) {
throw new Error('WebSocket closed');
} else {
throw new Error(`Failed to fetch data: ${response.statusText}`);
}
}
}
const text = await response.text();
let data = { roomId: '', timestamps: [] };
if (text) {
data = JSON.parse(text);
setData(data);
setCurrentTimestampIndex(data.timestamps.length - 1);
}
setToggle(true);

return data;
const text = await response.text();
let data = { roomId: '', timestamps: [], forkRoom: '', sessionId: '' };
if (text) {
Notification.warning(
'Document is now in read-only mode. Changes will not be saved.',
{ autoClose: 2500 }
);

data = JSON.parse(text);
setData(data);
setCurrentTimestampIndex(data.timestamps.length - 1);
provider.connectToForkDoc(data.forkRoom, data.sessionId);

sessionRef.current = await requestDocSession(
format,
contentType,
extractFilenameFromURL(apiURL)
);
}
setToggle(true);
isFirstChange.current = false;

return data;
}
} catch (error) {
console.error('Error fetching data:', error);
}
}

const handleRestore = async () => {
const response = await requestDocFork(
`${session.format}:${session.type}:${session.fileId}`,
'undo',
if (!sessionRef.current) {
console.error('Session is not initialized');
return;
}

const response = await requestUndoRedo(
`${sessionRef.current.format}:${sessionRef.current.type}:${sessionRef.current.fileId}`,
'restore',
0
0,
data.forkRoom
);

if (response.code === 200) {
Notification.success(response.status, { autoClose: 4000 });
} else {
Notification.error(response.status, { autoClose: 4000 });
}
};

const handleSliderChange = async (
event: React.ChangeEvent<HTMLInputElement>
) => {
const currentTimestamp = parseInt(event.target.value);
const steps = Math.abs(currentTimestamp - currentTimestampIndex);

try {
const action = determineAction(currentTimestamp);
setCurrentTimestampIndex(currentTimestamp);

// create fork when first using the slider
if (isFirstChange.current) {
if (isFirstSliderChange.current) {
setIsBtn(true);
isFirstChange.current = false;
const obj = await provider.connectToFork(action, 'original', steps);
setForkRoomID(obj.forkRoomId);
setSession(obj.session);
} else if (session && forkRoomID) {
await requestDocFork(
`${session.format}:${session.type}:${session.fileId}`,
action,
'fork',
steps
);
isFirstSliderChange.current = false;
}

if (!sessionRef.current) {
console.error('Session is not initialized');
return;
}

await requestUndoRedo(
`${sessionRef.current.format}:${sessionRef.current.type}:${sessionRef.current.fileId}`,
action,
steps,
data.forkRoom
);
} catch (error: any) {
console.error('Error fetching or applying updates:', error);
}
Expand Down
12 changes: 6 additions & 6 deletions packages/docprovider/src/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ServerConnection, Contents } from '@jupyterlab/services';
* See https://github.com/jupyterlab/jupyter_collaboration
*/
const DOC_SESSION_URL = 'api/collaboration/session';
const DOC_FORK_URL = 'api/collaboration/fork_room';
const DOC_FORK_URL = 'api/collaboration/undo_redo';
const TIMELINE_URL = 'api/collaboration/timeline';

/**
Expand Down Expand Up @@ -99,11 +99,11 @@ export async function requestDocumentTimeline(
return response;
}

export async function requestDocFork(
export async function requestUndoRedo(
roomid: string,
action: 'undo' | 'redo',
mode: string,
steps: number
action: 'undo' | 'redo' | 'restore',
steps: number,
forkRoom: string
): Promise<any> {
const settings = ServerConnection.makeSettings();
let url = URLExt.join(
Expand All @@ -112,7 +112,7 @@ export async function requestDocFork(
encodeURIComponent(roomid)
);

url = url.concat(`?action=${action}&&mode=${mode}&&steps=${steps}`);
url = url.concat(`?action=${action}&&steps=${steps}&&forkRoom=${forkRoom}`);

const body = { method: 'PUT' };

Expand Down
6 changes: 1 addition & 5 deletions packages/docprovider/src/ydrive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ const DISABLE_RTC =
const DOCUMENT_PROVIDER_URL = 'api/collaboration/room';

export interface IForkProvider {
connectToFork: (
action: 'undo' | 'redo',
mode: string,
steps: number
) => Promise<any>;
connectToForkDoc: (forkRoomId: string, sessionId: string) => Promise<void>;
contentType: string;
format: string;
}
Expand Down
23 changes: 2 additions & 21 deletions packages/docprovider/src/yprovider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { DocumentChange, YDocument } from '@jupyter/ydoc';
import { Awareness } from 'y-protocols/awareness';
import { WebsocketProvider as YWebsocketProvider } from 'y-websocket';

import { requestDocFork, requestDocSession } from './requests';
import { requestDocSession } from './requests';
import { IForkProvider } from './ydrive';

/**
Expand Down Expand Up @@ -122,26 +122,8 @@ export class WebSocketProvider implements IDocumentProvider, IForkProvider {
this._yWebsocketProvider.on('connection-close', this._onConnectionClosed);
}

async connectToFork(
action: 'undo' | 'redo',
mode: string,
steps: number
): Promise<any> {
const session = await requestDocSession(
this._format,
this._contentType,
this._path
);

async connectToForkDoc(forkRoomId: string, sessionId: string): Promise<void> {
this._disconnect();
const response = await requestDocFork(
`${session.format}:${session.type}:${session.fileId}`,
action,
mode,
steps
);
const forkRoomId = response.roomId;
const sessionId = response.sessionId;
this._yWebsocketProvider = new YWebsocketProvider(
this._serverUrl,
forkRoomId,
Expand All @@ -152,7 +134,6 @@ export class WebSocketProvider implements IDocumentProvider, IForkProvider {
awareness: this._awareness
}
);
return { session, forkRoomId };
}

get wsProvider() {
Expand Down
6 changes: 3 additions & 3 deletions projects/jupyter-server-ydoc/jupyter_server_ydoc/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@

from .handlers import (
DocSessionHandler,
TimelineForkHandler,
TimelineHandler,
UndoRedoHandler,
YDocWebSocketHandler,
)
from .loaders import FileLoaderMapping
Expand Down Expand Up @@ -144,8 +144,8 @@ def initialize_handlers(self):
},
),
(
r"/api/collaboration/fork_room/(.*)",
TimelineForkHandler,
r"/api/collaboration/undo_redo/(.*)",
UndoRedoHandler,
{
"ywebsocket_server": self.ywebsocket_server,
},
Expand Down
Loading

0 comments on commit 0c2c367

Please sign in to comment.