From 20e57b499d309052d4a12ef2d6a2288eec8bf04f Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Wed, 3 Jul 2019 19:05:41 -0400
Subject: Couple of fixes

---
 src/client/views/collections/CollectionDockingView.tsx | 3 +++
 1 file changed, 3 insertions(+)

(limited to 'src/client/views/collections/CollectionDockingView.tsx')

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 8724216f5..8b6f5b6a6 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -455,6 +455,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     }
 
     render() {
+        if (this.props.renderDepth > 0) {
+            return <div style={{ width: "100%", height: "100%" }}>Nested workspaces can't be rendered</div>;
+        }
         return (
             <Measure offset onResize={this.onResize}>
                 {({ measureRef }) =>
-- 
cgit v1.2.3-70-g09d2


From 575ee72cd6ddd65d6c02c6f50a3266f632b1b858 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 4 Jul 2019 00:17:15 -0400
Subject: highlights current workspace in treeview

---
 src/client/views/collections/CollectionDockingView.tsx    | 2 ++
 src/client/views/collections/CollectionTreeView.tsx       | 3 ++-
 src/client/views/nodes/DocumentView.tsx                   | 4 ++--
 src/client/views/presentationview/PresentationElement.tsx | 2 +-
 4 files changed, 7 insertions(+), 4 deletions(-)

(limited to 'src/client/views/collections/CollectionDockingView.tsx')

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 8b6f5b6a6..c5f8fb728 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -240,6 +240,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                         // Because this is in a set timeout, if this component unmounts right after mounting,
                         // we will leak a GoldenLayout, because we try to destroy it before we ever create it
                         setTimeout(() => this.setupGoldenLayout(), 1);
+                        this.props.Document.workspaceBrush = true;
                     }
                     this._ignoreStateChange = "";
                 }, { fireImmediately: true });
@@ -249,6 +250,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
     }
     componentWillUnmount: () => void = () => {
         try {
+            this.props.Document.workspaceBrush = false;
             this._goldenLayout.unbind('itemDropped', this.itemDropped);
             this._goldenLayout.unbind('tabCreated', this.tabCreated);
             this._goldenLayout.unbind('stackCreated', this.stackCreated);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 622ba37d1..3db786043 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -217,7 +217,8 @@ class TreeView extends React.Component<TreeViewProps> {
         return <>
             <div className="docContainer" id={`docContainer-${this.props.parentKey}`} ref={reference} onPointerDown={onItemDown}
                 style={{
-                    background: BoolCast(this.props.document.protoBrush, false) ? "#06123232" : BoolCast(this.props.document.libraryBrush, false) ? "#06121212" : "0",
+                    background: BoolCast(this.props.document.libraryBrush, false) ? "#06121212" : "0",
+                    outline: BoolCast(this.props.document.workspaceBrush, false) ? "dashed 1px #06123232" : undefined,
                     pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? "all" : "none"
                 }}
             >
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 69dc31fec..f751a45fe 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -589,9 +589,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 style={{
                     outlineColor: "maroon",
                     outlineStyle: "dashed",
-                    outlineWidth: (BoolCast(this.props.Document.libraryBrush) || BoolCast(this.props.Document.protoBrush)) && !NumCast(this.props.Document.borderRounding) ?
+                    outlineWidth: BoolCast(this.props.Document.libraryBrush) && !NumCast(this.props.Document.borderRounding) ?
                         `${this.props.ScreenToLocalTransform().Scale}px` : "0px",
-                    border: (BoolCast(this.props.Document.libraryBrush) || BoolCast(this.props.Document.protoBrush)) && NumCast(this.props.Document.borderRounding) ?
+                    border: BoolCast(this.props.Document.libraryBrush) && NumCast(this.props.Document.borderRounding) ?
                         `dashed maroon ${this.props.ScreenToLocalTransform().Scale}px` : undefined,
                     borderRadius: "inherit",
                     background: backgroundColor,
diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx
index d63c0b066..6896ee452 100644
--- a/src/client/views/presentationview/PresentationElement.tsx
+++ b/src/client/views/presentationview/PresentationElement.tsx
@@ -377,7 +377,7 @@ export default class PresentationElement extends React.Component<PresentationEle
                 style={{
                     outlineColor: "maroon",
                     outlineStyle: "dashed",
-                    outlineWidth: BoolCast(p.document.libraryBrush, false) || BoolCast(p.document.protoBrush, false) ? `1px` : "0px",
+                    outlineWidth: BoolCast(p.document.libraryBrush, false) ? `1px` : "0px",
                 }}
                 onClick={e => { p.gotoDocument(p.index, NumCast(this.props.mainDocument.selectedDoc)); e.stopPropagation(); }}>
                 <strong className="presentationView-name">
-- 
cgit v1.2.3-70-g09d2


From 8b9fa667816378cc6cae2977f1c2d80c204b28d0 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 5 Jul 2019 14:45:15 -0400
Subject: added more options to sidebar

---
 src/client/views/Main.scss                         | 18 ++++++++++++++
 src/client/views/MainView.tsx                      | 28 ++++++++++++++++++----
 .../views/collections/CollectionDockingView.tsx    |  4 ++++
 .../views/collections/CollectionTreeView.tsx       |  7 ++++--
 src/new_fields/Doc.ts                              | 11 +++++----
 .../authentication/models/current_user_utils.ts    | 10 ++++++++
 6 files changed, 67 insertions(+), 11 deletions(-)

(limited to 'src/client/views/collections/CollectionDockingView.tsx')

diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index b85a8040a..f52e3b658 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -230,6 +230,8 @@ ul#add-options-list {
 .mainView-libraryFlyout {
     height: 100%;
     position: absolute;
+    display: flex;
+    flex-direction:column;
 }
 
 .mainView-libraryHandle {
@@ -241,4 +243,20 @@ ul#add-options-list {
     position: absolute;
     z-index: 1;
     background: gray;
+}
+
+.mainView-workspace {
+    height:200px;
+    position:relative;
+    display:flex;
+}
+.mainView-library {
+    height:75%;
+    position:relative;
+    display:flex;
+}
+.mainView-recentlyClosed {
+    height:25%;
+    position:relative;
+    display:flex;
 }
\ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 61ccf4c1d..92468e3fd 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -8,12 +8,12 @@ import * as React from 'react';
 import { SketchPicker } from 'react-color';
 import Measure from 'react-measure';
 import * as request from 'request';
-import { Doc, DocListCast, Opt } from '../../new_fields/Doc';
+import { Doc, DocListCast, Opt, HeightSym } from '../../new_fields/Doc';
 import { Id } from '../../new_fields/FieldSymbols';
 import { InkTool } from '../../new_fields/InkField';
 import { List } from '../../new_fields/List';
 import { listSpec } from '../../new_fields/Schema';
-import { Cast, FieldValue } from '../../new_fields/Types';
+import { Cast, FieldValue, NumCast } from '../../new_fields/Types';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { RouteStore } from '../../server/RouteStore';
 import { emptyFunction, returnOne, returnTrue } from '../../Utils';
@@ -163,7 +163,9 @@ export class MainView extends React.Component {
 
     @action
     createNewWorkspace = async (id?: string) => {
-        const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc));
+        let workspaces = Cast(CurrentUserUtils.UserDocument.workspaces, Doc);
+        if (!(workspaces instanceof Doc)) return;
+        const list = Cast((CurrentUserUtils.UserDocument.workspaces as Doc).data, listSpec(Doc));
         if (list) {
             let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, width: this.pwidth * .7, height: this.pheight, title: `WS collection ${list.length + 1}` });
             var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] };
@@ -285,8 +287,24 @@ export class MainView extends React.Component {
     };
     @computed
     get flyout() {
+        let sidebar = CurrentUserUtils.UserDocument.sidebar;
+        let workspaces = CurrentUserUtils.UserDocument.workspaces;
+        let recent = CurrentUserUtils.UserDocument.recentlyClosed;
+        if (!(sidebar instanceof Doc)) return (null);
+        if (!(recent instanceof Doc)) return (null);
+        if (!(workspaces instanceof Doc)) return (null);
+        let workspacesDoc = workspaces as Doc;
+        let sidebarDoc = sidebar as Doc;
+        let recentDoc = recent as Doc;
+        let library = CurrentUserUtils.UserDocument;
+        let gridGap = NumCast(sidebar.gridGap, 10);
+        let yMargin = NumCast(sidebar.yMargin, 2 * gridGap);
+        let libraryHeight = this.getPHeight() - workspacesDoc[HeightSym]() - recentDoc[HeightSym]() - 2 * gridGap - 2 * yMargin;
+        if (library[HeightSym]() != libraryHeight) {
+            setTimeout(() => CurrentUserUtils.UserDocument.height = libraryHeight, 0);
+        }
         return <DocumentView
-            Document={CurrentUserUtils.UserDocument}
+            Document={sidebarDoc}
             DataDoc={undefined}
             addDocument={undefined}
             addDocTab={this.addDocTabFunc}
@@ -304,7 +322,7 @@ export class MainView extends React.Component {
             ContainingCollectionView={undefined}
             zoomToScale={emptyFunction}
             getScale={returnOne}>
-        </DocumentView>;
+        </DocumentView>
     }
     @computed
     get mainContent() {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index c5f8fb728..e0270fab3 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -27,6 +27,7 @@ import { MainView } from '../MainView';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faFile } from '@fortawesome/free-solid-svg-icons';
+import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
 library.add(faFile);
 
 @observer
@@ -416,6 +417,9 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
                 if (doc instanceof Doc) {
                     let theDoc = doc;
                     CollectionDockingView.Instance._removedDocs.push(theDoc);
+                    if (CurrentUserUtils.UserDocument.recentlyClosed instanceof Doc) {
+                        Doc.AddDocToList(CurrentUserUtils.UserDocument.recentlyClosed, "data", doc, undefined, true, true);
+                    }
                     SelectionManager.DeselectAll();
                 }
                 tab.contentItem.remove();
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 93a1ec1eb..c83a2d2c6 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -471,9 +471,12 @@ export class CollectionTreeView extends CollectionSubView(Document) {
     }
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
-        if (!e.isPropagationStopped() && this.props.Document.excludeFromLibrary) { // excludeFromLibrary means this is the user document
+        if (!e.isPropagationStopped() && this.props.Document.workspaceLibrary) { // excludeFromLibrary means this is the user document
             ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => MainView.Instance.createNewWorkspace()) });
             ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.remove(this.props.Document)) });
+            e.stopPropagation();
+            e.preventDefault();
+            ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
         }
     }
 
@@ -531,7 +534,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
                         TreeView.loadId = doc[Id];
                         Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true);
                     }} />
-                {this.props.Document.excludeFromLibrary ? this.notifsButton : (null)}
+                {this.props.Document.workspaceLibrary ? this.notifsButton : (null)}
                 <ul className="no-indent" style={{ width: "max-content" }} >
                     {
                         TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, addDoc, this.remove,
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index c361e3032..092205f52 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -241,12 +241,15 @@ export namespace Doc {
         return Array.from(results);
     }
 
-    export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean) {
+    export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean) {
         let list = Cast(target[key], listSpec(Doc));
         if (list) {
-            let ind = relativeTo ? list.indexOf(relativeTo) : -1;
-            if (ind === -1) list.push(doc);
-            else list.splice(before ? ind : ind + 1, 0, doc);
+            if (first) list.splice(0, 0, doc);
+            else {
+                let ind = relativeTo ? list.indexOf(relativeTo) : -1;
+                if (ind === -1) list.push(doc);
+                else list.splice(before ? ind : ind + 1, 0, doc);
+            }
         }
         return true;
     }
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 816c5f269..3134ecaff 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -31,6 +31,16 @@ export class CurrentUserUtils {
         doc.viewType = CollectionViewType.Tree;
         doc.dropAction = "alias";
         doc.layout = CollectionView.LayoutString();
+        doc.workspaces = Docs.TreeDocument([], { title: "Workspaces", height: 100 });
+        (doc.workspaces as Doc).excludeFromLibrary = true;
+        (doc.workspaces as Doc).workspaceLibrary = true;
+        doc.recentlyClosed = Docs.TreeDocument([], { title: "Recently Closed", height: 75 });
+        (doc.workspaces as Doc).excludeFromLibrary = true;
+        doc.sidebar = Docs.StackingDocument([doc.workspaces as Doc, doc, doc.recentlyClosed as Doc], { title: "Sidebar" });
+        (doc.sidebar as Doc).excludeFromLibrary = true;
+        (doc.sidebar as Doc).gridGap = 5;
+        (doc.sidebar as Doc).xMargin = 5;
+        (doc.sidebar as Doc).yMargin = 5;
         doc.title = this.email;
         doc.data = new List<Doc>();
         doc.excludeFromLibrary = true;
-- 
cgit v1.2.3-70-g09d2

