Skip to content

Commit a63043a

Browse files
committed
Allow undoing changes when connecting, add handler for deleting fork
1 parent ea5303e commit a63043a

File tree

6 files changed

+114
-9
lines changed

6 files changed

+114
-9
lines changed

jupyter_collaboration/app.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from pycrdt_websocket.ystore import BaseYStore
99
from traitlets import Bool, Float, Type
1010

11-
from .handlers import DocForkHandler, DocMergeHandler, DocSessionHandler, YDocWebSocketHandler
11+
from .handlers import DocForkHandler, DocDeleteHandler, DocMergeHandler, DocSessionHandler, YDocWebSocketHandler
1212
from .loaders import FileLoaderMapping
1313
from .stores import SQLiteYStore
1414
from .utils import AWARENESS_EVENTS_SCHEMA_PATH, EVENTS_SCHEMA_PATH
@@ -123,6 +123,13 @@ def initialize_handlers(self):
123123
"ywebsocket_server": self.ywebsocket_server,
124124
}
125125
),
126+
(
127+
r"/api/collaboration/delete_room",
128+
DocDeleteHandler,
129+
{
130+
"ywebsocket_server": self.ywebsocket_server,
131+
}
132+
),
126133
]
127134
)
128135

jupyter_collaboration/handlers.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,13 +471,53 @@ async def put(self):
471471
if idx in root_state:
472472
del root_state[idx]
473473
else:
474+
self.set_status(404)
474475
raise RuntimeError(f"Could not find root document fork with ID: {fork_roomid}")
475476
fork_room = await self._websocket_server.get_room(fork_roomid)
476477
fork_ydoc = fork_room.ydoc
477-
update = fork_ydoc.get_update()
478-
root_ydoc.apply_update(update)
478+
fork_update = fork_ydoc.get_update()
479+
root_ydoc.apply_update(fork_update)
479480
root_room.fork_ydocs.remove(fork_ydoc)
480481
fork_state = fork_ydoc.get("state", type=Map)
481482
fork_state["merge"] = fork_roomid
482483
#self._websocket_server.delete_room(name=fork_roomid)
483484
self.set_status(200)
485+
486+
487+
class DocDeleteHandler(APIHandler):
488+
"""
489+
Jupyter Server's handler to delete a document.
490+
"""
491+
492+
auth_resource = "contents"
493+
494+
def initialize(
495+
self,
496+
ywebsocket_server: JupyterWebsocketServer,
497+
) -> None:
498+
self._websocket_server = ywebsocket_server
499+
500+
@web.authenticated
501+
@authorized
502+
async def delete(self):
503+
"""
504+
Deletes a forked document.
505+
"""
506+
model = self.get_json_body()
507+
fork_roomid = model["fork_roomid"]
508+
root_room = await self._websocket_server.get_room(model["root_roomid"])
509+
root_ydoc = root_room.ydoc
510+
idx = f"fork_{fork_roomid}"
511+
root_state = root_ydoc.get("state", type=Map)
512+
if idx in root_state:
513+
del root_state[idx]
514+
else:
515+
self.set_status(404)
516+
raise RuntimeError(f"Could not find root document fork with ID: {fork_roomid}")
517+
fork_room = await self._websocket_server.get_room(fork_roomid)
518+
fork_ydoc = fork_room.ydoc
519+
root_room.fork_ydocs.remove(fork_ydoc)
520+
fork_state = fork_ydoc.get("state", type=Map)
521+
fork_state["delete"] = fork_roomid
522+
#self._websocket_server.delete_room(name=fork_roomid)
523+
self.set_status(200)

packages/collaboration-extension/src/collaboration.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
EditorExtensionRegistry,
2929
IEditorExtensionRegistry
3030
} from '@jupyterlab/codemirror';
31-
import { requestDocMerge, WebSocketAwarenessProvider } from '@jupyter/docprovider';
31+
import { requestDocDelete, requestDocMerge, WebSocketAwarenessProvider } from '@jupyter/docprovider';
3232
import {
3333
SidePanel,
3434
usersIcon,
@@ -310,6 +310,7 @@ export class EditingModeExtension implements DocumentRegistry.IWidgetExtension<N
310310
reviewCommands.addCommand('discard', {
311311
label: 'Discard',
312312
execute: () => {
313+
requestDocDelete(sharedModel.currentRoomId, sharedModel.rootRoomId);
313314
}
314315
});
315316

@@ -322,7 +323,7 @@ export class EditingModeExtension implements DocumentRegistry.IWidgetExtension<N
322323
if (changes.stateChange) {
323324
changes.stateChange.forEach(value => {
324325
const forkPrefix = 'fork_';
325-
if (value.name === 'merge') {
326+
if (value.name === 'merge' || value.name === 'delete') {
326327
// FIXME: a client who is not connected to the fork should not see this update
327328
if (sharedModel.currentRoomId === value.newValue) {
328329
editingMenu.title.label = 'Editing';
@@ -331,7 +332,8 @@ export class EditingModeExtension implements DocumentRegistry.IWidgetExtension<N
331332
delete suggestions[value.newValue];
332333
suggestionMenu.removeItem(item);
333334
reviewMenu.clearItems();
334-
sharedModel.provider.connect(sharedModel.rootRoomId);
335+
const merge = value.name === 'merge';
336+
sharedModel.provider.connect(sharedModel.rootRoomId, merge);
335337
open_dialog('Editing', this._trans);
336338
}
337339
}

packages/docprovider/src/requests.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ServerConnection, Contents } from '@jupyterlab/services';
1313
const DOC_SESSION_URL = 'api/collaboration/session';
1414
const DOC_FORK_URL = 'api/collaboration/fork_room';
1515
const DOC_MERGE_URL = 'api/collaboration/merge_room';
16+
const DOC_DELETE_URL = 'api/collaboration/delete_room';
1617

1718
/**
1819
* Document session model
@@ -150,3 +151,42 @@ export async function requestDocMerge(
150151

151152
return data;
152153
}
154+
155+
156+
export async function requestDocDelete(
157+
forkRoomid: string,
158+
rootRoomid: string,
159+
): Promise<any> {
160+
const settings = ServerConnection.makeSettings();
161+
const url = URLExt.join(
162+
settings.baseUrl,
163+
DOC_DELETE_URL,
164+
);
165+
const body = {
166+
method: 'DELETE',
167+
body: JSON.stringify({ fork_roomid: forkRoomid, root_roomid: rootRoomid })
168+
};
169+
170+
let response: Response;
171+
try {
172+
response = await ServerConnection.makeRequest(url, body, settings);
173+
} catch (error) {
174+
throw new ServerConnection.NetworkError(error as Error);
175+
}
176+
177+
let data: any = await response.text();
178+
179+
if (data.length > 0) {
180+
try {
181+
data = JSON.parse(data);
182+
} catch (error) {
183+
console.log('Not a JSON response body.', response);
184+
}
185+
}
186+
187+
if (!response.ok) {
188+
throw new ServerConnection.ResponseError(response, data.message || data);
189+
}
190+
191+
return data;
192+
}

packages/docprovider/src/yprovider.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,25 @@ export class WebSocketProvider implements IDocumentProvider {
9696
return forkId;
9797
}
9898

99-
connect(roomId: string) {
99+
connect(roomId: string, merge?: boolean) {
100100
this._sharedModel.currentRoomId = roomId;
101101
this._yWebsocketProvider?.disconnect();
102+
if (roomId === this._sharedModel.rootRoomId) {
103+
// connecting to the root
104+
// don't bring our changes there if not merging
105+
if (merge !== true) {
106+
while (this._sharedModel.undoManager.canUndo()) {
107+
this._sharedModel.undoManager.undo();
108+
}
109+
}
110+
this._sharedModel.undoManager.clear();
111+
}
112+
else {
113+
// connecting to a fork
114+
// keep track of changes so that we can undo them when connecting back to root
115+
this._sharedModel.undoManager.clear();
116+
}
117+
102118
this._yWebsocketProvider = new YWebsocketProvider(
103119
this._serverUrl,
104120
roomId,

yarn.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2156,7 +2156,7 @@ __metadata:
21562156

21572157
"@jupyter/ydoc@file:.yalc/@jupyter/ydoc::locator=%40jupyter%2Freal-time-collaboration%40workspace%3A.":
21582158
version: 2.0.1
2159-
resolution: "@jupyter/ydoc@file:.yalc/@jupyter/ydoc#.yalc/@jupyter/ydoc::hash=e50509&locator=%40jupyter%2Freal-time-collaboration%40workspace%3A."
2159+
resolution: "@jupyter/ydoc@file:.yalc/@jupyter/ydoc#.yalc/@jupyter/ydoc::hash=045bce&locator=%40jupyter%2Freal-time-collaboration%40workspace%3A."
21602160
dependencies:
21612161
"@jupyterlab/application": ^4.0.0
21622162
"@jupyterlab/nbformat": ^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0
@@ -2165,7 +2165,7 @@ __metadata:
21652165
"@lumino/signaling": ^1.10.0 || ^2.0.0
21662166
y-protocols: ^1.0.5
21672167
yjs: ^13.5.40
2168-
checksum: c54e335aebc1f0b28241fe0031d5f47513a7a90621b5cca10e6aec3a965adea96390bd8e2c51191e448a9c276ca284ff563eb4870844233798e79a03560b1648
2168+
checksum: aed2b93d1f9d447e7ad1be8699dc3a31968f4cfd4772a1ed1330308eb08ba8d14973df24bae4e49bd93f416f646b84edb44c567487403e7d0bdb665e6ca0e29f
21692169
languageName: node
21702170
linkType: hard
21712171

0 commit comments

Comments
 (0)