Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Register RTC content provider #345

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 20 additions & 199 deletions packages/docprovider-extension/src/filebrowser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,104 +3,57 @@
* Distributed under the terms of the Modified BSD License.
*/

import { Drive } from '@jupyterlab/services';
import {
ILabShell,
IRouter,
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '@jupyterlab/application';
import { Dialog, showDialog } from '@jupyterlab/apputils';
import { DocumentWidget, IDocumentWidget } from '@jupyterlab/docregistry';
import { Widget } from '@lumino/widgets';
import {
FileBrowser,
IDefaultFileBrowser,
IFileBrowserFactory
} from '@jupyterlab/filebrowser';
import { IStatusBar } from '@jupyterlab/statusbar';

import { IEditorTracker } from '@jupyterlab/fileeditor';
import { ILogger, ILoggerRegistry } from '@jupyterlab/logconsole';
import { INotebookTracker } from '@jupyterlab/notebook';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { ITranslator, nullTranslator } from '@jupyterlab/translation';

import { CommandRegistry } from '@lumino/commands';

import { YFile, YNotebook } from '@jupyter/ydoc';

import {
ICollaborativeDrive,
IForkProvider,
IGlobalAwareness,
TimelineWidget,
YDrive
RtcContentProvider
} from '@jupyter/docprovider';
import { Awareness } from 'y-protocols/awareness';
import { URLExt } from '@jupyterlab/coreutils';

/**
* The command IDs used by the file browser plugin.
* The RTC content provider.
*/
namespace CommandIDs {
export const openPath = 'filebrowser:open-path';
}
const DOCUMENT_TIMELINE_URL = 'api/collaboration/timeline';

/**
* The default collaborative drive provider.
*/
export const drive: JupyterFrontEndPlugin<ICollaborativeDrive> = {
id: '@jupyter/docprovider-extension:drive',
description: 'The default collaborative drive provider',
provides: ICollaborativeDrive,
export const rtcContentProvider: JupyterFrontEndPlugin<void> = {
id: '@jupyter/docprovider-extension:content',
description: 'The RTC content provider',
autoStart: true,
requires: [ITranslator],
optional: [IGlobalAwareness],
optional: [IGlobalAwareness, ISettingRegistry],
activate: (
app: JupyterFrontEnd,
translator: ITranslator,
globalAwareness: Awareness | null
): ICollaborativeDrive => {
globalAwareness: Awareness | null,
settingRegistry: ISettingRegistry | null
): void => {
const trans = translator.load('jupyter_collaboration');
const drive = new YDrive(app.serviceManager.user, trans, globalAwareness);
app.serviceManager.contents.addDrive(drive);
return drive;
}
};
const rtcContentProvider = new RtcContentProvider(app.serviceManager.user, trans, globalAwareness);

/**
* Plugin to register the shared model factory for the content type 'file'.
*/
export const yfile: JupyterFrontEndPlugin<void> = {
id: '@jupyter/docprovider-extension:yfile',
description:
"Plugin to register the shared model factory for the content type 'file'",
autoStart: true,
requires: [ICollaborativeDrive],
optional: [],
activate: (app: JupyterFrontEnd, drive: ICollaborativeDrive): void => {
const yFileFactory = () => {
return new YFile();
};
drive.sharedModelFactory.registerDocumentFactory('file', yFileFactory);
}
};
rtcContentProvider.sharedModelFactory.registerDocumentFactory('file', yFileFactory);

/**
* Plugin to register the shared model factory for the content type 'notebook'.
*/
export const ynotebook: JupyterFrontEndPlugin<void> = {
id: '@jupyter/docprovider-extension:ynotebook',
description:
"Plugin to register the shared model factory for the content type 'notebook'",
autoStart: true,
requires: [ICollaborativeDrive],
optional: [ISettingRegistry],
activate: (
app: JupyterFrontEnd,
drive: YDrive,
settingRegistry: ISettingRegistry | null
): void => {
let disableDocumentWideUndoRedo = true;

// Fetch settings if possible.
Expand Down Expand Up @@ -128,63 +81,31 @@ export const ynotebook: JupyterFrontEndPlugin<void> = {
disableDocumentWideUndoRedo
});
};
drive.sharedModelFactory.registerDocumentFactory(
rtcContentProvider.sharedModelFactory.registerDocumentFactory(
'notebook',
yNotebookFactory
);

Drive.getContentProviderRegistry().register(rtcContentProvider);
}
};

/**
* A plugin to add a timeline slider status item to the status bar.
*/
export const statusBarTimeline: JupyterFrontEndPlugin<void> = {
id: '@jupyter/docprovider-extension:statusBarTimeline',
description: 'Plugin to add a timeline slider to the status bar',
autoStart: true,
requires: [IStatusBar, ICollaborativeDrive],
requires: [IStatusBar],
activate: async (
app: JupyterFrontEnd,
statusBar: IStatusBar,
drive: ICollaborativeDrive
statusBar: IStatusBar
): Promise<void> => {
function isYDrive(drive: YDrive | ICollaborativeDrive): drive is YDrive {
return 'getProviderForPath' in drive;
}
try {
let sliderItem: Widget | null = null;
let timelineWidget: TimelineWidget | null = null;

const updateTimelineForDocument = async (documentPath: string) => {
if (drive && isYDrive(drive)) {
// Dispose of the previous timelineWidget if it exists
if (timelineWidget) {
timelineWidget.dispose();
timelineWidget = null;
}

const provider = (await drive.getProviderForPath(
documentPath
)) as IForkProvider;
const fullPath = URLExt.join(
app.serviceManager.serverSettings.baseUrl,
DOCUMENT_TIMELINE_URL,
documentPath.split(':')[1]
);

timelineWidget = new TimelineWidget(
fullPath,
provider,
provider.contentType,
provider.format
);

const elt = document.getElementById('jp-slider-status-bar');
if (elt && !timelineWidget.isAttached) {
Widget.attach(timelineWidget, elt);
}
}
};

if (app.shell.currentChanged) {
app.shell.currentChanged.connect(async (_, args) => {
const currentWidget = args.newValue as DocumentWidget;
Expand All @@ -194,7 +115,7 @@ export const statusBarTimeline: JupyterFrontEndPlugin<void> = {
timelineWidget = null;
}
if (currentWidget && 'context' in currentWidget) {
await updateTimelineForDocument(currentWidget.context.path);
// FIXME
}
});
}
Expand Down Expand Up @@ -222,50 +143,6 @@ export const statusBarTimeline: JupyterFrontEndPlugin<void> = {
}
};

/**
* The default file browser factory provider.
*/
export const defaultFileBrowser: JupyterFrontEndPlugin<IDefaultFileBrowser> = {
id: '@jupyter/docprovider-extension:defaultFileBrowser',
description: 'The default file browser factory provider',
provides: IDefaultFileBrowser,
requires: [ICollaborativeDrive, IFileBrowserFactory],
optional: [
IRouter,
JupyterFrontEnd.ITreeResolver,
ILabShell,
ISettingRegistry
],
activate: async (
app: JupyterFrontEnd,
drive: YDrive,
fileBrowserFactory: IFileBrowserFactory,
router: IRouter | null,
tree: JupyterFrontEnd.ITreeResolver | null,
labShell: ILabShell | null
): Promise<IDefaultFileBrowser> => {
const { commands } = app;

app.serviceManager.contents.addDrive(drive);

// Manually restore and load the default file browser.
const defaultBrowser = fileBrowserFactory.createFileBrowser('filebrowser', {
auto: false,
restore: false,
driveName: drive.name
});
void Private.restoreBrowser(
defaultBrowser,
commands,
router,
tree,
labShell
);

return defaultBrowser;
}
};

/**
* The default collaborative drive provider.
*/
Expand Down Expand Up @@ -359,59 +236,3 @@ export const logger: JupyterFrontEndPlugin<void> = {
})();
}
};

namespace Private {
/**
* Restores file browser state and overrides state if tree resolver resolves.
*/
export async function restoreBrowser(
browser: FileBrowser,
commands: CommandRegistry,
router: IRouter | null,
tree: JupyterFrontEnd.ITreeResolver | null,
labShell: ILabShell | null
): Promise<void> {
const restoring = 'jp-mod-restoring';

browser.addClass(restoring);

if (!router) {
await browser.model.restore(browser.id);
await browser.model.refresh();
browser.removeClass(restoring);
return;
}

const listener = async () => {
router.routed.disconnect(listener);

const paths = await tree?.paths;

if (paths?.file || paths?.browser) {
// Restore the model without populating it.
await browser.model.restore(browser.id, false);
if (paths.file) {
await commands.execute(CommandIDs.openPath, {
path: paths.file,
dontShowBrowser: true
});
}
if (paths.browser) {
await commands.execute(CommandIDs.openPath, {
path: paths.browser,
dontShowBrowser: true
});
}
} else {
await browser.model.restore(browser.id);
await browser.model.refresh();
}
browser.removeClass(restoring);

if (labShell?.isEmpty('main')) {
void commands.execute('launcher:create');
}
};
router.routed.connect(listener);
}
}
10 changes: 2 additions & 8 deletions packages/docprovider-extension/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@
import { JupyterFrontEndPlugin } from '@jupyterlab/application';

import {
drive,
yfile,
ynotebook,
defaultFileBrowser,
logger,
rtcContentProvider,
statusBarTimeline
} from './filebrowser';
import { notebookCellExecutor } from './executor';
Expand All @@ -21,10 +18,7 @@ import { notebookCellExecutor } from './executor';
* Export the plugins as default.
*/
const plugins: JupyterFrontEndPlugin<any>[] = [
drive,
yfile,
ynotebook,
defaultFileBrowser,
rtcContentProvider,
logger,
notebookCellExecutor,
statusBarTimeline
Expand Down
19 changes: 1 addition & 18 deletions packages/docprovider/src/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

import { DocumentChange, IAwareness, YDocument } from '@jupyter/ydoc';
import { IAwareness } from '@jupyter/ydoc';
import { Contents } from '@jupyterlab/services';

import { Token } from '@lumino/coreutils';
Expand All @@ -23,13 +23,6 @@ export const IGlobalAwareness = new Token<IAwareness>(
'@jupyter/collaboration:IGlobalAwareness'
);

/**
* A document factory for registering shared models
*/
export type SharedDocumentFactory = (
options: Contents.ISharedFactoryOptions
) => YDocument<DocumentChange>;

/**
* A Collaborative implementation for an `IDrive`, talking to the
* server using the Jupyter REST API and a WebSocket connection.
Expand All @@ -45,16 +38,6 @@ export interface ICollaborativeDrive extends Contents.IDrive {
* Yjs sharedModel factory for real-time collaboration.
*/
export interface ISharedModelFactory extends Contents.ISharedFactory {
/**
* Register a SharedDocumentFactory.
*
* @param type Document type
* @param factory Document factory
*/
registerDocumentFactory(
type: Contents.ContentType,
factory: SharedDocumentFactory
): void;
}

/**
Expand Down
Loading
Loading