浏览代码

Remove 'resolve-workspace` query parameter, redirect workspace dialog, and make resolution automatic.

Afshin T. Darian 6 年之前
父节点
当前提交
555d84b417
共有 3 个文件被更改,包括 27 次插入199 次删除
  1. 23 87
      packages/apputils-extension/src/index.ts
  2. 0 110
      packages/apputils-extension/src/redirect.ts
  3. 4 2
      packages/coreutils/src/url.ts

+ 23 - 87
packages/apputils-extension/src/index.ts

@@ -40,8 +40,6 @@ import { Menu } from '@phosphor/widgets';
 
 import { Palette } from './palette';
 
-import { createRedirectForm } from './redirect';
-
 import '../style/index.css';
 
 /**
@@ -51,12 +49,6 @@ import '../style/index.css';
  */
 const WORKSPACE_SAVE_DEBOUNCE_INTERVAL = 750;
 
-/**
- * The query string parameter indicating that a workspace name should be
- * automatically generated if the current request collides with an open session.
- */
-const WORKSPACE_RESOLVE = 'resolve-workspace';
-
 /**
  * The interval in milliseconds before recover options appear during splash.
  */
@@ -259,47 +251,32 @@ const resolver: JupyterFrontEndPlugin<IWindowResolver> = {
     const match = path.match(new RegExp(`^${paths.urls.workspaces}([^?\/]+)`));
     const workspace = (match && decodeURIComponent(match[1])) || '';
     const candidate = Private.candidate(paths, workspace);
+    const rest = workspace
+      ? path.replace(new RegExp(`^${paths.urls.workspaces}${workspace}`), '')
+      : path.replace(new RegExp(`^${paths.urls.page}`), '');
 
     try {
       await solver.resolve(candidate);
+      return solver;
     } catch (error) {
       // Window resolution has failed so the URL must change. Return a promise
       // that never resolves to prevent the application from loading plugins
       // that rely on `IWindowResolver`.
       return new Promise<IWindowResolver>(() => {
-        // If the user has requested workspace resolution create a new one.
-        if (WORKSPACE_RESOLVE in query) {
-          const { base, workspaces } = paths.urls;
-          const pool =
-            'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
-          const random = pool[Math.floor(Math.random() * pool.length)];
-          const path = URLExt.join(base, workspaces, `auto-${random}`);
-
-          // Clone the originally requested workspace after redirecting.
-          query['clone'] = workspace;
-
-          // Change the URL and trigger a hard reload to re-route.
-          const url = path + URLExt.objectToQueryString(query) + (hash || '');
-          router.navigate(url, { hard: true, silent: true });
-          return;
-        }
-
-        // Launch a dialog to ask the user for a new workspace name.
-        console.warn('Window resolution failed:', error);
-        return Private.redirect(router, paths, workspace);
+        const { base, workspaces } = paths.urls;
+        const pool =
+          'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+        const random = pool[Math.floor(Math.random() * pool.length)];
+        const path = URLExt.join(base, workspaces, `auto-${random}`) + rest;
+
+        // Clone the originally requested workspace after redirecting.
+        query['clone'] = workspace;
+
+        // Change the URL and trigger a hard reload to re-route.
+        const url = path + URLExt.objectToQueryString(query) + (hash || '');
+        router.navigate(url, { hard: true, silent: true });
       });
     }
-
-    // If the user has requested workspace resolution remove the query param.
-    if (WORKSPACE_RESOLVE in query) {
-      delete query[WORKSPACE_RESOLVE];
-
-      // Silently scrub the URL.
-      const url = path + URLExt.objectToQueryString(query) + (hash || '');
-      router.navigate(url, { silent: true });
-    }
-
-    return solver;
   }
 };
 
@@ -399,18 +376,17 @@ const state: JupyterFrontEndPlugin<IStateDB> = {
         }
 
         debouncer = window.setTimeout(async () => {
-          // Prevent a race condition between the timeout and saving.
-          if (!conflated) {
-            return;
-          }
-
           const data = await db.toJSON();
 
           try {
             await workspaces.save(id, { data, metadata });
-            conflated.resolve(undefined);
+            if (conflated) {
+              conflated.resolve(undefined);
+            }
           } catch (error) {
-            conflated.reject(error);
+            if (conflated) {
+              conflated.reject(error);
+            }
           }
           conflated = null;
         }, timeout);
@@ -463,9 +439,7 @@ const state: JupyterFrontEndPlugin<IStateDB> = {
         }
 
         // Any time the local state database changes, save the workspace.
-        if (workspace) {
-          db.changed.connect(listener, db);
-        }
+        db.changed.connect(listener, db);
 
         const immediate = true;
 
@@ -695,44 +669,6 @@ namespace Private {
       });
   }
 
-  /**
-   * Allows the user to clear state if splash screen takes too long.
-   */
-  export async function redirect(
-    router: IRouter,
-    paths: JupyterFrontEnd.IPaths,
-    workspace: string,
-    warn = false
-  ): Promise<void> {
-    const form = createRedirectForm(warn);
-    const dialog = new Dialog({
-      title: 'Please use a different workspace.',
-      body: form,
-      focusNodeSelector: 'input',
-      buttons: [Dialog.okButton({ label: 'Switch Workspace' })]
-    });
-    const result = await dialog.launch();
-
-    dialog.dispose();
-    if (!result.value) {
-      return redirect(router, paths, workspace, true);
-    }
-
-    // Navigate to a new workspace URL and abandon this session altogether.
-    const page = paths.urls.page;
-    const workspaces = paths.urls.workspaces;
-    const prefix = (workspace ? workspaces : page).length + workspace.length;
-    const rest = router.current.request.substring(prefix);
-    const url = URLExt.join(workspaces, result.value, rest);
-
-    router.navigate(url, { hard: true, silent: true });
-
-    // This promise will never resolve because the application navigates
-    // away to a new location. It only exists to satisfy the return type
-    // of the `redirect` function.
-    return new Promise<void>(() => undefined);
-  }
-
   /**
    * The splash element.
    */

+ 0 - 110
packages/apputils-extension/src/redirect.ts

@@ -1,110 +0,0 @@
-/*-----------------------------------------------------------------------------
-| Copyright (c) Jupyter Development Team.
-| Distributed under the terms of the Modified BSD License.
-|----------------------------------------------------------------------------*/
-
-import { Widget } from '@phosphor/widgets';
-
-/**
- * The form label.
- */
-const LABEL = `This workspace is already in use in another JupyterLab window.
-  Please enter another workspace name.`;
-
-/**
- * The form input field placeholder.
- */
-const PLACEHOLDER = 'url-friendly-workspace-name';
-
-/**
- * The form warning message if an empty value was submitted.
- */
-const WARNING = 'Please enter a value.';
-
-/**
- * The UI for the recovery option to redirect to a different workspace.
- */
-export class RedirectForm extends Widget {
-  /**
-   * Create a redirect form.
-   */
-  constructor() {
-    super({ node: Private.createNode() });
-  }
-
-  /**
-   * The text label of the form.
-   */
-  get label(): string {
-    return this.node.querySelector('label span').textContent;
-  }
-  set label(label: string) {
-    this.node.querySelector('label span').textContent = label;
-  }
-
-  /**
-   * The input placeholder.
-   */
-  get placeholder(): string {
-    return this.node.querySelector('input').placeholder;
-  }
-  set placeholder(placeholder: string) {
-    this.node.querySelector('input').placeholder = placeholder;
-  }
-
-  /**
-   * The warning message.
-   */
-  get warning(): string {
-    return this.node.querySelector('.jp-RedirectForm-warning').textContent;
-  }
-  set warning(warning: string) {
-    this.node.querySelector('.jp-RedirectForm-warning').textContent = warning;
-  }
-
-  /**
-   * Returns the input value.
-   */
-  getValue(): string {
-    return encodeURIComponent(this.node.querySelector('input').value);
-  }
-}
-
-/**
- * Return a new redirect form, populated with default language.
- */
-export function createRedirectForm(warn = false): RedirectForm {
-  const form = new RedirectForm();
-
-  form.label = LABEL;
-  form.placeholder = PLACEHOLDER;
-  form.warning = warn ? WARNING : '';
-
-  return form;
-}
-
-/**
- * A namespace for private module data.
- */
-namespace Private {
-  /**
-   * Create the redirect form's content.
-   */
-  export function createNode(): HTMLElement {
-    const node = document.createElement('div');
-    const label = document.createElement('label');
-    const input = document.createElement('input');
-    const text = document.createElement('span');
-    const warning = document.createElement('div');
-
-    node.className = 'jp-RedirectForm';
-    warning.className = 'jp-RedirectForm-warning';
-
-    label.appendChild(text);
-    label.appendChild(input);
-    node.appendChild(label);
-    node.appendChild(warning);
-
-    return node;
-  }
-}

+ 4 - 2
packages/coreutils/src/url.ts

@@ -98,7 +98,7 @@ export namespace URLExt {
    * Modified version of [stackoverflow](http://stackoverflow.com/a/30707423).
    */
   export function objectToQueryString(value: JSONObject): string {
-    const keys = Object.keys(value);
+    const keys = Object.keys(value).filter(key => key.length > 0);
 
     if (!keys.length) {
       return '';
@@ -129,7 +129,9 @@ export namespace URLExt {
         (acc, val) => {
           const [key, value] = val.split('=');
 
-          acc[key] = decodeURIComponent(value || '');
+          if (key.length > 0) {
+            acc[key] = decodeURIComponent(value || '');
+          }
 
           return acc;
         },