Skip to content

Commit 9fcc413

Browse files
authored
fix: notebook collaborative (#347)
* fix: notebook state * chore: remove console log * feat: datalayer collaboration * chore: token for collaborative notebook
1 parent a035f88 commit 9fcc413

File tree

9 files changed

+70
-39
lines changed

9 files changed

+70
-39
lines changed

packages/react/public/index-local.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
{
1414
"jupyterServerUrl": "http://localhost:8686/api/jupyter-server",
1515
"jupyterServerToken": "60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6",
16-
"runUrl": "https://oss.datalayer.run",
16+
"runUrl": "https://prod1.datalayer.run",
1717
"token": "",
1818
"cpuEnvironment": "python-cpu-env",
19-
"gpuEnvironment": "pytorch-cuda-env",
19+
"gpuEnvironment": "pytorch-gpu-env",
2020
"credits": 1
2121
}
2222
</script>

packages/react/public/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
<title>Jupyter React Example</title>
1212
<script id="datalayer-config-data" type="application/json">
1313
{
14-
"jupyterServerUrl": "https://oss.datalayer.run/api/jupyter-server",
14+
"jupyterServerUrl": "https://prod1.datalayer.run/api/jupyter-server",
1515
"jupyterServerToken": "60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6",
1616
"runUrl": "https://prod1.datalayer.run",
1717
"token": "",
1818
"cpuEnvironment": "python-cpu-env",
19-
"gpuEnvironment": "pytorch-cuda-env",
19+
"gpuEnvironment": "pytorch-gpu-env",
2020
"credits": 1
2121
}
2222
</script>

packages/react/src/components/notebook/Notebook.tsx

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ import { IRenderMime } from '@jupyterlab/rendermime-interfaces';
1616
import { INotebookContent } from '@jupyterlab/nbformat';
1717
import { ServiceManager, Kernel as JupyterKernel } from '@jupyterlab/services';
1818
import { WebsocketProvider as YWebsocketProvider } from 'y-websocket';
19-
import { useJupyter, Lite, Kernel, requestDocSession, COLLABORATION_ROOM_URL_PATH } from './../../jupyter';
19+
import { useJupyter, Lite, Kernel, requestDocSession, ICollaborative, COLLABORATION_ROOM_URL_PATH } from './../../jupyter';
2020
import { asObservable, Lumino } from '../lumino';
2121
import { newUuid } from '../../utils';
22-
import { OnSessionConnection, KernelTransfer } from '../../state';
22+
import { OnSessionConnection, KernelTransfer, jupyterReactStore } from '../../state';
2323
import { CellMetadataEditor } from './cell/metadata';
2424
import { ICellSidebarProps } from './cell/sidebar';
2525
import { INotebookToolbarProps } from './toolbar/NotebookToolbar';
@@ -52,7 +52,7 @@ export type INotebookProps = {
5252
Toolbar?: (props: INotebookToolbarProps) => JSX.Element;
5353
cellMetadataPanel: boolean;
5454
cellSidebarMargin: number;
55-
collaborative?: boolean;
55+
collaborative?: ICollaborative;
5656
extensions: DatalayerNotebookExtension[]
5757
height?: string;
5858
id: string;
@@ -118,7 +118,7 @@ export const Notebook = (props: INotebookProps) => {
118118
const notebookStore = useNotebookStore();
119119
const portals = notebookStore.selectNotebookPortals(id);
120120
// Bootstrap the Notebook Adapter.
121-
const bootstrapAdapter = async (collaborative: boolean, serviceManager?: ServiceManager.IManager, kernel?: Kernel) => {
121+
const bootstrapAdapter = async (collaborative: ICollaborative, serviceManager?: ServiceManager.IManager, kernel?: Kernel) => {
122122
const adapter = new NotebookAdapter({
123123
...props,
124124
id,
@@ -136,28 +136,48 @@ export const Notebook = (props: INotebookProps) => {
136136
extension.createNew(adapter.notebookPanel!, adapter.context!);
137137
setExtensionComponents(extensionComponents.concat(extension.component ?? <></>));
138138
});
139-
if (collaborative) {
140-
// Setup Collaboration.
139+
// Setup Collaboration.
140+
if (collaborative == 'jupyter') {
141141
const ydoc = (adapter.notebookPanel?.model?.sharedModel as any).ydoc;
142+
const token = jupyterReactStore.getState().jupyterConfig?.jupyterServerToken;
142143
const session = await requestDocSession("json", "notebook", path!);
143-
const yWebsocketProvider = new YWebsocketProvider(
144-
URLExt.join(serviceManager?.serverSettings.wsUrl!, COLLABORATION_ROOM_URL_PATH),
145-
`${session.format}:${session.type}:${session.fileId}`,
146-
ydoc,
144+
const roomURL = URLExt.join(serviceManager?.serverSettings.wsUrl!, COLLABORATION_ROOM_URL_PATH);
145+
const roomName = `${session.format}:${session.type}:${session.fileId}`;
146+
const yWebsocketProvider = new YWebsocketProvider(roomURL, roomName, ydoc,
147147
{
148148
disableBc: true,
149-
params: { sessionId: session.sessionId },
150-
// awareness: this._awareness
149+
params: {
150+
sessionId: session.sessionId,
151+
token: token!,
152+
},
153+
// awareness: this._awareness
151154
}
152155
);
153156
console.log('Collaboration is setup with websocket provider.', yWebsocketProvider);
154-
// Update the notebook state with the adapter.
155-
notebookStore.update({ id, state: { adapter } });
156-
// Update the notebook state further to events.
157-
adapter.notebookPanel?.model?.contentChanged.connect((notebookModel, _) => {
158-
notebookStore.changeModel({ id, notebookModel });
159-
});
160157
}
158+
else if (collaborative == 'datalayer') {
159+
const ydoc = (adapter.notebookPanel?.model?.sharedModel as any).ydoc;
160+
const token = jupyterReactStore.getState().datalayerConfig?.token;
161+
const runURL = jupyterReactStore.getState().datalayerConfig?.runUrl;
162+
const roomName = id;
163+
const roomURL = URLExt.join(runURL!.replace('http', 'ws'), `/api/spacer/v1/rooms`);
164+
const yWebsocketProvider = new YWebsocketProvider(roomURL, roomName, ydoc,
165+
{
166+
disableBc: true,
167+
params: {
168+
token: token!,
169+
}
170+
// awareness: this._awareness
171+
}
172+
);
173+
console.log('Collaboration is setup with websocket provider.', yWebsocketProvider);
174+
}
175+
// Update the notebook state with the adapter.
176+
notebookStore.update({ id, state: { adapter } });
177+
// Update the notebook state further to events.
178+
adapter.notebookPanel?.model?.contentChanged.connect((notebookModel, _) => {
179+
notebookStore.changeModel({ id, notebookModel });
180+
});
161181
/*
162182
adapter.notebookPanel?.model?.sharedModel.changed.connect((_, notebookChange) => {
163183
notebookStore.changeNotebook({ id, notebookChange });
@@ -206,7 +226,7 @@ export const Notebook = (props: INotebookProps) => {
206226
});
207227
}
208228
//
209-
const createAdapter = (collaborative: boolean, serviceManager?: ServiceManager.IManager, kernel?: Kernel) => {
229+
const createAdapter = (collaborative: ICollaborative, serviceManager?: ServiceManager.IManager, kernel?: Kernel) => {
210230
if (!kernel) {
211231
bootstrapAdapter(collaborative, serviceManager, kernel);
212232
} else {
@@ -227,10 +247,10 @@ export const Notebook = (props: INotebookProps) => {
227247
// Mutation Effects.
228248
useEffect(() => {
229249
if (serviceManager && serverless) {
230-
createAdapter(collaborative ?? false, serviceManager, kernel);
250+
createAdapter(collaborative, serviceManager, kernel);
231251
}
232252
else if (serviceManager && kernel) {
233-
createAdapter(collaborative ?? false, serviceManager, kernel);
253+
createAdapter(collaborative, serviceManager, kernel);
234254
}
235255
}, [collaborative, serviceManager, kernel]);
236256
useEffect(() => {
@@ -256,13 +276,13 @@ export const Notebook = (props: INotebookProps) => {
256276
useEffect(() => {
257277
if (adapter && path && adapter.path !== path) {
258278
disposeAdapter();
259-
createAdapter(collaborative ?? false, serviceManager);
279+
createAdapter(collaborative, serviceManager);
260280
}
261281
}, [path]);
262282
useEffect(() => {
263283
if (adapter && url && adapter.url !== url) {
264284
disposeAdapter();
265-
createAdapter(collaborative ?? false, serviceManager);
285+
createAdapter(collaborative, serviceManager);
266286
}
267287
}, [collaborative, url]);
268288
// Dispose Effects.
@@ -360,7 +380,7 @@ export const Notebook = (props: INotebookProps) => {
360380
Notebook.defaultProps = {
361381
cellMetadataPanel: false,
362382
cellSidebarMargin: 120,
363-
collaborative: false,
383+
collaborative: undefined,
364384
extensions: [],
365385
height: '100vh',
366386
kernelClients: [],

packages/react/src/components/notebook/NotebookState.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,11 @@ export const notebookStore = createStore<NotebookState>((set, get) => ({
231231
set((state: NotebookState) => ({ notebooks }));
232232
}
233233
},
234-
addPortals: (portalsId: ReactPortalsMutation) => {
234+
addPortals: (portalsMutation: ReactPortalsMutation) => {
235235
const notebooks = get().notebooks;
236-
const notebook = notebooks.get(portalsId.id);
236+
const notebook = notebooks.get(portalsMutation.id);
237237
if (notebook) {
238-
notebook.portals = notebook.portals.concat(portalsId.portals);
238+
notebook.portals = notebook.portals.concat(portalsMutation.portals);
239239
set((state: NotebookState) => ({ notebooks }));
240240
}
241241
},
@@ -248,19 +248,19 @@ export const notebookStore = createStore<NotebookState>((set, get) => ({
248248
}
249249
set((state: NotebookState) => ({ notebooks }));
250250
},
251-
setPortals: (portalsId: ReactPortalsMutation) => {
251+
setPortals: (portalsMutation: ReactPortalsMutation) => {
252252
const notebooks = get().notebooks;
253-
const notebook = notebooks.get(portalsId.id);
253+
const notebook = notebooks.get(portalsMutation.id);
254254
if (notebook) {
255-
notebook.portals = portalsId.portals;
255+
notebook.portals = portalsMutation.portals;
256256
set((state: NotebookState) => ({ notebooks }));
257257
}
258258
},
259-
setPortalDisplay: (portalDisplayId: PortalDisplayMutation) => {
259+
setPortalDisplay: (portalDisplayMutation: PortalDisplayMutation) => {
260260
const notebooks = get().notebooks;
261-
const notebook = notebooks.get(portalDisplayId.id);
261+
const notebook = notebooks.get(portalDisplayMutation.id);
262262
if (notebook) {
263-
notebook.portalDisplay = portalDisplayId.portalDisplay;
263+
notebook.portalDisplay = portalDisplayMutation.portalDisplay;
264264
set((state: NotebookState) => ({ notebooks }));
265265
}
266266
},

packages/react/src/examples/NotebookCollaborative.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const NotebookCollaborative = () => {
1414
return (
1515
<JupyterReactTheme>
1616
<Notebook
17-
collaborative
17+
collaborative="datalayer"
1818
path="collaboration.ipynb"
1919
id="notebook-collaboration-id"
2020
startDefaultKernel={true}

packages/react/src/jupyter/JupyterConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ function loadDatalayerConfig(name?: string): any {
9696
else {
9797
console.log('No Datalayer config data found in page');
9898
}
99-
}
99+
}
100100
// @ts-expect-error IJupyterConfig does not have index signature
101101
return name ? config[name] : config;
102102
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
* Copyright (c) 2021-2023 Datalayer, Inc.
3+
*
4+
* MIT License
5+
*/
6+
7+
export type ICollaborative = 'jupyter' | 'datalayer' | undefined;
8+
9+
export default ICollaborative;

packages/react/src/jupyter/collaboration/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
*/
66

77
export * from './DatalayerCollaboration';
8+
export * from './ICollaborative';
89
export * from './JupyterCollaboration';

packages/react/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"forceConsistentCasingInFileNames": true,
77
"baseUrl": "/",
88
"rootDir": "./src",
9+
"allowJs": false,
910
"allowSyntheticDefaultImports": true,
1011
"composite": true,
1112
"declaration": true,

0 commit comments

Comments
 (0)