From 9505eda799fdf3b0cd2e6bde2d80c18731caee2c Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 10 Oct 2019 21:36:26 -0400
Subject: switching sidebar to a stacking view...

---
 src/client/views/collections/CollectionTreeView.tsx | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 882a0f144..37eb151b1 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -28,6 +28,7 @@ import { CollectionSchemaPreview } from './CollectionSchemaView';
 import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
 import React = require("react");
+import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils';
 
 
 export interface TreeViewProps {
@@ -198,6 +199,7 @@ class TreeView extends React.Component<TreeViewProps> {
             } else {
                 ContextMenu.Instance.addItem({ description: "Open as Workspace", event: () => MainView.Instance.openWorkspace(this.dataDoc), icon: "caret-square-right" });
                 ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" });
+                ContextMenu.Instance.addItem({ description: "Create New Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
             }
             ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.Create.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" });
             ContextMenu.Instance.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.document, StrCast(this.props.document.title), () => { }, () => { }), icon: "file" });
@@ -217,7 +219,7 @@ class TreeView extends React.Component<TreeViewProps> {
         if (de.data instanceof DragManager.LinkDragData) {
             let sourceDoc = de.data.linkSourceDocument;
             let destDoc = this.props.document;
-            DocUtils.MakeLink({doc:sourceDoc}, {doc:destDoc});
+            DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc });
             e.stopPropagation();
         }
         if (de.data instanceof DragManager.DocumentDragData) {
@@ -538,7 +540,7 @@ 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.workspaceLibrary) {
+        if (!e.isPropagationStopped() && this.props.Document === CurrentUserUtils.UserDocument.workspaces) {
             ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" });
             ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.props.Document), icon: "minus" });
             e.stopPropagation();
-- 
cgit v1.2.3-70-g09d2


From a73a72586c72cd620c16dd7bc0baad88c8e49a34 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Fri, 11 Oct 2019 01:38:48 -0400
Subject: switching search around to left-hand side.

---
 src/client/documents/DocumentTypes.ts              |  3 +-
 src/client/documents/Documents.ts                  | 10 +++++
 src/client/views/GlobalKeyHandler.ts               |  4 ++
 src/client/views/MainView.tsx                      | 10 +++--
 .../views/collections/CollectionTreeView.tsx       |  5 ++-
 src/client/views/nodes/DocumentContentsView.tsx    |  3 +-
 src/client/views/nodes/QueryBox.scss               |  0
 src/client/views/nodes/QueryBox.tsx                | 50 ++++++++++++++++++++++
 src/client/views/search/FilterBox.tsx              |  4 +-
 src/client/views/search/IconBar.tsx                | 14 ------
 src/client/views/search/SearchBox.scss             | 17 ++++++--
 src/client/views/search/SearchBox.tsx              |  2 -
 src/client/views/search/SearchItem.scss            | 39 +++++++----------
 src/client/views/search/SearchItem.tsx             | 19 +++-----
 .../authentication/models/current_user_utils.ts    |  6 +++
 15 files changed, 120 insertions(+), 66 deletions(-)
 create mode 100644 src/client/views/nodes/QueryBox.scss
 create mode 100644 src/client/views/nodes/QueryBox.tsx

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

diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index e5d5885cd..7abaa4043 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -20,5 +20,6 @@ export enum DocumentType {
     DRAGBOX = "dragbox",
     PRES = "presentation",
     LINKFOLLOW = "linkfollow",
-    PRESELEMENT = "preselement"
+    PRESELEMENT = "preselement",
+    QUERY = "search",
 }
\ No newline at end of file
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 0114f82d8..7f6ab50d8 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -45,6 +45,7 @@ import { ProxyField } from "../../new_fields/Proxy";
 import { DocumentType } from "./DocumentTypes";
 import { LinkFollowBox } from "../views/linking/LinkFollowBox";
 import { PresElementBox } from "../views/presentationview/PresElementBox";
+import { QueryBox } from "../views/nodes/QueryBox";
 var requestImageSize = require('../util/request-image-size');
 var path = require('path');
 
@@ -62,6 +63,7 @@ export interface DocumentOptions {
     panY?: number;
     page?: number;
     scale?: number;
+    fitWidth?: boolean;
     layout?: string | Doc;
     isTemplate?: boolean;
     templates?: List<string>;
@@ -119,6 +121,10 @@ export namespace Docs {
                 layout: { view: HistogramBox, collectionView: [CollectionView, data] as CollectionViewType },
                 options: { height: 300, backgroundColor: "black" }
             }],
+            [DocumentType.QUERY, {
+                layout: { view: QueryBox },
+                options: { width: 400, fitWidth: true }
+            }],
             [DocumentType.IMG, {
                 layout: { view: ImageBox, ext: anno },
                 options: {}
@@ -374,6 +380,10 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.HIST), new HistogramField(histoOp), options);
         }
 
+        export function QueryDocument(options: DocumentOptions = {}) {
+            return InstanceFromProto(Prototypes.get(DocumentType.QUERY), "", options);
+        }
+
         export function TextDocument(options: DocumentOptions = {}) {
             return InstanceFromProto(Prototypes.get(DocumentType.TEXT), "", options);
         }
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index c519991a5..6815ff926 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -7,6 +7,8 @@ import { action, runInAction } from "mobx";
 import { Doc } from "../../new_fields/Doc";
 import { DictationManager } from "../util/DictationManager";
 import SharingManager from "../util/SharingManager";
+import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
+import { Cast, PromiseValue } from "../../new_fields/Types";
 
 const modifiers = ["control", "meta", "shift", "alt"];
 type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
@@ -162,6 +164,8 @@ export default class KeyManager {
                 break;
             case "f":
                 MainView.Instance.isSearchVisible = !MainView.Instance.isSearchVisible;
+                MainView.Instance.flyoutWidth = MainView.Instance.isSearchVisible ? 400 : 0;
+                PromiseValue(Cast(CurrentUserUtils.UserDocument.searchBox, Doc)).then(pv => pv && (pv.treeViewOpen = (MainView.Instance.flyoutWidth > 0)));
                 break;
             case "o":
                 let target = SelectionManager.SelectedDocuments()[0];
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ba4224875..1ec21f638 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -12,7 +12,7 @@ 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 { BoolCast, Cast, FieldValue, StrCast } from '../../new_fields/Types';
+import { BoolCast, Cast, FieldValue, StrCast, PromiseValue } from '../../new_fields/Types';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { RouteStore } from '../../server/RouteStore';
 import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils } from '../../Utils';
@@ -463,7 +463,7 @@ export class MainView extends React.Component {
             return (null);
         }
         return <DocumentView
-            Document={sidebar}
+            Document={this.isSearchVisible ? Cast(CurrentUserUtils.UserDocument.searchBox, Doc) as Doc : sidebar}
             DataDoc={undefined}
             addDocument={undefined}
             addDocTab={this.addDocTabFunc}
@@ -640,7 +640,7 @@ export class MainView extends React.Component {
     @computed
     get miscButtons() {
         return [
-            this.isSearchVisible ? <div className="main-searchDiv" key="search" style={{ top: '34px', right: '1px', position: 'absolute' }} > <FilterBox /> </div> : null,
+            //this.isSearchVisible ? <div className="main-searchDiv" key="search" style={{ top: '34px', right: '1px', position: 'absolute' }} > <FilterBox /> </div> : null,
         ];
 
     }
@@ -648,7 +648,9 @@ export class MainView extends React.Component {
     @observable isSearchVisible = false;
     @action.bound
     toggleSearch = () => {
-        this.isSearchVisible = !this.isSearchVisible;
+        this.isSearchVisible = !MainView.Instance.isSearchVisible;
+        MainView.Instance.flyoutWidth = MainView.Instance.isSearchVisible ? 400 : 0;
+        PromiseValue(Cast(CurrentUserUtils.UserDocument.searchBox, Doc)).then(pv => pv && (pv.treeViewOpen = (MainView.Instance.flyoutWidth > 0)));
     }
 
     @computed private get dictationOverlay() {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 37eb151b1..6f5587b5a 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -260,7 +260,10 @@ class TreeView extends React.Component<TreeViewProps> {
             let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth);
             if (aspect) return this.docWidth() * aspect;
             if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x);
-            return NumCast(this.props.document.height) ? NumCast(this.props.document.height) : 50;
+            return this.props.document.fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection.height) :
+                Math.min(this.docWidth() * NumCast(this.props.document.scrollHeight, NumCast(this.props.document.nativeHeight)) / NumCast(this.props.document.nativeWidth,
+                    NumCast(this.props.containingCollection.height)))) :
+                NumCast(this.props.document.height) ? NumCast(this.props.document.height) : 50;
         })());
     }
 
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index e4b2ecffd..d4e7c6d4e 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -24,6 +24,7 @@ import { ImageBox } from "./ImageBox";
 import { KeyValueBox } from "./KeyValueBox";
 import { PDFBox } from "./PDFBox";
 import { PresBox } from "./PresBox";
+import { QueryBox } from "./QueryBox";
 import { PresElementBox } from "../presentationview/PresElementBox";
 import { VideoBox } from "./VideoBox";
 import { WebBox } from "./WebBox";
@@ -99,7 +100,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         if (!this.layout && this.props.layoutKey !== "overlayLayout") return (null);
         return <ObserverJsxParser
             blacklistedAttrs={[]}
-            components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, DragBox, ButtonBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, LinkFollowBox, PresElementBox }}
+            components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, DragBox, ButtonBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, LinkFollowBox, PresElementBox, QueryBox }}
             bindings={this.CreateBindings()}
             jsx={this.finalLayout}
             showWarnings={true}
diff --git a/src/client/views/nodes/QueryBox.scss b/src/client/views/nodes/QueryBox.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx
new file mode 100644
index 000000000..7a8e0df63
--- /dev/null
+++ b/src/client/views/nodes/QueryBox.tsx
@@ -0,0 +1,50 @@
+import React = require("react");
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { faArrowLeft, faArrowRight, faEdit, faMinus, faPlay, faPlus, faStop, faTimes } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed, reaction, IReactionDisposer } from "mobx";
+import { observer } from "mobx-react";
+import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc";
+import { listSpec } from "../../../new_fields/Schema";
+import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
+import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
+import { DocumentManager } from "../../util/DocumentManager";
+import { undoBatch } from "../../util/UndoManager";
+import { CollectionViewType } from "../collections/CollectionBaseView";
+import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { CollectionView } from "../collections/CollectionView";
+import { ContextMenu } from "../ContextMenu";
+import { FieldView, FieldViewProps } from './FieldView';
+import "./PresBox.scss";
+import { DocumentType } from "../../documents/DocumentTypes";
+import { Docs } from "../../documents/Documents";
+import { ComputedField } from "../../../new_fields/ScriptField";
+import { SearchBox } from "../search/SearchBox";
+import { FilterBox } from "../search/FilterBox";
+
+library.add(faArrowLeft);
+library.add(faArrowRight);
+library.add(faPlay);
+library.add(faStop);
+library.add(faPlus);
+library.add(faTimes);
+library.add(faMinus);
+library.add(faEdit);
+
+@observer
+export class QueryBox extends React.Component<FieldViewProps> {
+    public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(QueryBox, fieldKey); }
+    _docListChangedReaction: IReactionDisposer | undefined;
+    componentDidMount() {
+    }
+
+    componentWillUnmount() {
+        this._docListChangedReaction && this._docListChangedReaction();
+    }
+
+    render() {
+        return <div style={{ width: "100%", height: "100%", position: "absolute", pointerEvents: "all" }}>
+            <FilterBox></FilterBox>
+        </div>
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx
index da733d64b..b841190d4 100644
--- a/src/client/views/search/FilterBox.tsx
+++ b/src/client/views/search/FilterBox.tsx
@@ -33,7 +33,7 @@ export enum Keys {
 export class FilterBox extends React.Component {
 
     static Instance: FilterBox;
-    public _allIcons: string[] = [DocumentType.AUDIO, DocumentType.COL, DocumentType.HIST, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.TEXT, DocumentType.VID, DocumentType.WEB];
+    public _allIcons: string[] = [DocumentType.AUDIO, DocumentType.COL, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.TEXT, DocumentType.VID, DocumentType.WEB];
 
     //if true, any keywords can be used. if false, all keywords are required.
     //this also serves as an indicator if the word status filter is applied
@@ -393,7 +393,7 @@ export class FilterBox extends React.Component {
             <div>
                 <div style={{ display: "flex", flexDirection: "row-reverse" }}>
                     <SearchBox />
-                    {this.getActiveFilters()}
+                    {/* {this.getActiveFilters()} */}
                 </div>
                 {this._filterOpen ? (
                     <div className="filter-form" onPointerDown={this.stopProp} id="filter-form" style={this._filterOpen ? { display: "flex" } : { display: "none" }}>
diff --git a/src/client/views/search/IconBar.tsx b/src/client/views/search/IconBar.tsx
index c9924222f..bdeb57d5c 100644
--- a/src/client/views/search/IconBar.tsx
+++ b/src/client/views/search/IconBar.tsx
@@ -59,23 +59,9 @@ export class IconBar extends React.Component {
     render() {
         return (
             <div className="icon-bar">
-                <div className="type-outer">
-                    <div className={"type-icon all"}
-                        onClick={this.selectAll}>
-                        <FontAwesomeIcon className="fontawesome-icon" icon={faCheckCircle} />
-                    </div>
-                    <div className="filter-description">Select All</div>
-                </div>
                 {FilterBox.Instance._allIcons.map((type: string) =>
                     <IconButton type={type} />
                 )}
-                <div className="type-outer">
-                    <div className={"type-icon none"}
-                        onClick={this.resetSelf}>
-                        <FontAwesomeIcon className="fontawesome-icon" icon={faTimesCircle} />
-                    </div>
-                    <div className="filter-description">Clear</div>
-                </div>
             </div>
         );
     }
diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss
index 0dd4d3dc5..bc11604a5 100644
--- a/src/client/views/search/SearchBox.scss
+++ b/src/client/views/search/SearchBox.scss
@@ -1,6 +1,15 @@
 @import "../globalCssVariables";
 @import "./NaviconButton.scss";
 
+.searchBox-container {
+    display: flex;
+    flex-direction: column;
+    width:100%;
+    height:100%;
+    position: absolute;
+    font-size: 10px;
+    line-height: 1;
+}
 .searchBox-bar {
     height: 32px;
     display: flex;
@@ -49,17 +58,17 @@
 }
 
 .searchBox-quickFilter {
-    width: 500px;
-    margin-left: 25px;
+    width: 100%;
+    height: 40px;
     margin-top: 10px;
 }
 
 .searchBox-results {
-    margin-right: 136px;
+    display:flex;
+    flex-direction: column;
     top: 300px;
     display: flex;
     flex-direction: column;
-    margin-right: 72px;
     // height: 560px;
     height: 100%;
     // overflow: hidden;
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index be75a29e0..62c8c255e 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -346,8 +346,6 @@ export class SearchBox extends React.Component {
                         className="searchBox-barChild searchBox-input" onPointerDown={this.openSearch} onKeyPress={this.enter} onFocus={this.openSearch}
                         style={{ width: this._searchbarOpen ? "500px" : "100px" }} />
                     <button className="searchBox-barChild searchBox-filter" title="Advanced Filtering Options" onClick={FilterBox.Instance.openFilter} onPointerDown={FilterBox.Instance.stopProp}><FontAwesomeIcon icon="ellipsis-v" color="white" /></button>
-                    <button className="searchBox-barChild searchBox-submit" onClick={this.submitSearch} onPointerDown={FilterBox.Instance.stopProp}>Submit</button>
-                    <button className="searchBox-barChild searchBox-close" title={"Close Search Bar"} onPointerDown={MainView.Instance.toggleSearch}><FontAwesomeIcon icon={faTimes} size="lg" /></button>
                 </div>
                 {(this._numTotalResults > 0 || !this._searchbarOpen) ? (null) :
                     (<div className="searchBox-quickFilter" onPointerDown={this.openSearch}>
diff --git a/src/client/views/search/SearchItem.scss b/src/client/views/search/SearchItem.scss
index 62715c5eb..9f12994c3 100644
--- a/src/client/views/search/SearchItem.scss
+++ b/src/client/views/search/SearchItem.scss
@@ -2,19 +2,27 @@
 
 .search-overview {
     display: flex;
-    flex-direction: row-reverse;
+    flex-direction: reverse;
     justify-content: flex-end;
     z-index: 0;
 }
+.link-count {
+     background: black;
+    border-radius: 20px;
+    color: white;
+    width: 15px;
+    text-align: center;
+    margin-top: 5px;
+}
 
 .searchBox-placeholder,
 .search-overview .search-item {
-    width: 500px;
+    width: 100%;
     background: $light-color-secondary;
     border-color: $intermediate-color;
     border-bottom-style: solid;
     padding: 10px;
-    min-height: 70px;
+    min-height: 50px;
     max-height: 150px;
     height: auto;
     z-index: 0;
@@ -61,16 +69,6 @@
                 overflow: hidden;
                 position: relative;
 
-                .link-count {
-                    opacity: 1;
-                    position: absolute;
-                    z-index: 1000;
-                    text-align: center;
-                    -webkit-transition: opacity 0.2s ease-in-out;
-                    -moz-transition: opacity 0.2s ease-in-out;
-                    -o-transition: opacity 0.2s ease-in-out;
-                    transition: opacity 0.2s ease-in-out;
-                }
 
                 .link-extended {
                     // display: none;
@@ -112,7 +110,7 @@
 
             .icon-icons,
             .icon-live {
-                height: 50px;
+                height: auto;
                 margin: auto;
                 overflow: hidden;
 
@@ -174,9 +172,7 @@
 .searchBox-instances:active {
     opacity: 1;
     background: $lighter-alt-accent;
-    -webkit-transform: scale(1);
-    -ms-transform: scale(1);
-    transform: scale(1);
+    width:150px
 }
 
 .search-item:hover {
@@ -193,13 +189,10 @@
 .searchBox-instances {
     float: left;
     opacity: 1;
-    width: 150px;
+    width: 0px;
     transition: all 0.2s ease;
     color: black;
-    transform-origin: top right;
-    -webkit-transform: scale(0);
-    -ms-transform: scale(0);
-    transform: scale(0);
+    overflow: hidden;
 }
 
 
@@ -208,7 +201,7 @@
 }
 
 .searchBox-placeholder {
-    min-height: 70px;
+    min-height: 50px;
     margin-left: 150px;
     text-transform: uppercase;
     text-align: left;
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index a7822ed46..b8cff16f2 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -213,15 +213,6 @@ export class SearchItem extends React.Component<SearchItemProps> {
     @computed
     get linkCount() { return LinkManager.Instance.getAllRelatedLinks(this.props.doc).length; }
 
-    @computed
-    get linkString(): string {
-        let num = this.linkCount;
-        if (num === 1) {
-            return num.toString() + " link";
-        }
-        return num.toString() + " links";
-    }
-
     @action
     pointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e); }
 
@@ -290,7 +281,11 @@ export class SearchItem extends React.Component<SearchItemProps> {
                 <div className="search-item" onPointerDown={this.nextHighlight} onPointerEnter={this.highlightDoc} onPointerLeave={this.unHighlightDoc} id="result"
                     onClick={this.onClick}>
                     <div className="main-search-info">
-                        <div title="Drag as document" onPointerDown={this.onPointerDown} style={{ marginRight: "7px" }}> <FontAwesomeIcon icon="file" size="lg" /> </div>
+                        <div title="Drag as document" onPointerDown={this.onPointerDown} style={{ marginRight: "7px" }}> <FontAwesomeIcon icon="file" size="lg" />
+                            <div className="link-container item">
+                                <div className="link-count" title={`${this.linkCount + " links"}`}>{this.linkCount}</div>
+                            </div>
+                        </div>
                         <div className="search-title-container">
                             <div className="search-title">{StrCast(this.props.doc.title)}</div>
                             <div className="search-highlighting">{this.props.highlighting.length ? "Matched fields:" + this.props.highlighting.join(", ") : this.props.lines.length ? this.props.lines[0] : ""}</div>
@@ -301,10 +296,6 @@ export class SearchItem extends React.Component<SearchItemProps> {
                                 <div className="search-type" title="Click to Preview">{this.DocumentIcon()}</div>
                                 <div className="search-label">{this.props.doc.type ? this.props.doc.type : "Other"}</div>
                             </div>
-                            <div className="link-container item">
-                                <div className="link-count">{this.linkCount}</div>
-                                <div className="link-extended">{this.linkString}</div>
-                            </div>
                         </div>
                     </div>
                 </div>
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index f3d5555ed..32da29932 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -97,6 +97,12 @@ export class CurrentUserUtils {
             curPresentation.boxShadow = "0 0";
             doc.curPresentation = curPresentation;
         }
+        if (doc.searchBox === undefined) {
+            const searchBox = Docs.Create.QueryDocument({ title: "Searching" });
+            searchBox.boxShadow = "0 0";
+            searchBox.ignoreClick = true;
+            doc.searchBox = searchBox;
+        }
 
         if (doc.sidebar === undefined) {
             const sidebar = Docs.Create.TreeDocument([doc.workspaces as Doc, doc, doc.recentlyClosed as Doc], { title: "Sidebar" });
-- 
cgit v1.2.3-70-g09d2


From c03012fe8c9b77ddf3b7e6c589339eb9a0d967e6 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 12 Oct 2019 01:33:20 -0400
Subject: working version of new docked sidebar + configurable bottom buttons 
 by drag/drop

---
 src/client/views/GlobalKeyHandler.ts               |  18 ++-
 src/client/views/Main.scss                         |  13 +-
 src/client/views/MainView.tsx                      | 157 ++++++++++-----------
 .../views/collections/CollectionTreeView.tsx       |   2 +-
 .../nodes/CollectionFreeFormDocumentView.scss      |   2 +
 src/client/views/nodes/DocumentView.tsx            |   1 +
 src/client/views/nodes/DragBox.tsx                 |  12 +-
 src/client/views/nodes/PDFBox.tsx                  |   2 +-
 .../authentication/models/current_user_utils.ts    |  45 +++---
 9 files changed, 126 insertions(+), 126 deletions(-)

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

diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 9d239d0bf..82f5a573c 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -164,29 +164,27 @@ export default class KeyManager {
                 }
                 break;
             case "c":
-                if (MainView.Instance.flyoutWidth > 0) {
+                PromiseValue(Cast(CurrentUserUtils.UserDocument.Create, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                if (MainView.Instance.flyoutWidth === 75) {
                     MainView.Instance.flyoutWidth = 0;
-                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
                 } else {
-                    MainView.Instance.flyoutWidth = 400;
-                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Create, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                    MainView.Instance.flyoutWidth = 75;
                 }
                 break;
             case "l":
-                if (MainView.Instance.flyoutWidth > 0) {
+                PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                if (MainView.Instance.flyoutWidth === 250) {
                     MainView.Instance.flyoutWidth = 0;
                 } else {
-                    MainView.Instance.flyoutWidth = 400;
-                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                    MainView.Instance.flyoutWidth = 250;
                 }
                 break;
             case "f":
-                if (MainView.Instance.flyoutWidth > 0) {
+                PromiseValue(Cast(CurrentUserUtils.UserDocument.Search, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                if (MainView.Instance.flyoutWidth === 400) {
                     MainView.Instance.flyoutWidth = 0;
-                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
                 } else {
                     MainView.Instance.flyoutWidth = 400;
-                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Search, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
                 }
                 break;
             case "o":
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 4cd860da2..705da7b35 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -211,14 +211,21 @@ button:hover {
 }
 
 #add-options-content {
-    display: table;
+    display: flex;
     opacity: 1;
     margin: 0;
     padding: 0;
     position: relative;
     float: right;
-    bottom: 0.3em;
-    margin-bottom: -1.68em;
+    .mainView-docBtn {
+        position:relative;
+        margin-right: 10px;
+        width: 35px;
+        height: 35px;
+    }
+    .collectionFreeFormDocumentView-container {
+        position: relative;
+    }
 }
 
 ul#add-options-list {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 6f60de1c4..1fa231659 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,5 +1,5 @@
 import { IconName, library } from '@fortawesome/fontawesome-svg-core';
-import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv, faChevronRight, faEllipsisV } from '@fortawesome/free-solid-svg-icons';
+import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faChevronRight, faClone, faCloudUploadAlt, faCommentAlt, faCut, faEllipsisV, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -12,15 +12,16 @@ 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 { BoolCast, Cast, FieldValue, StrCast, PromiseValue } from '../../new_fields/Types';
+import { ScriptField } from '../../new_fields/ScriptField';
+import { BoolCast, Cast, FieldValue, PromiseValue, StrCast, NumCast } from '../../new_fields/Types';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { RouteStore } from '../../server/RouteStore';
-import { emptyFunction, returnEmptyString, returnOne, returnTrue, returnFalse, Utils } from '../../Utils';
+import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils } from '../../Utils';
+import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { Docs, DocumentOptions } from '../documents/Documents';
-import { ClientUtils } from '../util/ClientUtils';
 import { DictationManager } from '../util/DictationManager';
-import { SetupDrag } from '../util/DragManager';
+import { SetupDrag, DragManager } from '../util/DragManager';
 import { HistoryUtil } from '../util/History';
 import SharingManager from '../util/SharingManager';
 import { Transform } from '../util/Transform';
@@ -35,13 +36,10 @@ import { InkingControl } from './InkingControl';
 import "./Main.scss";
 import MainViewModal from './MainViewModal';
 import { DocumentView } from './nodes/DocumentView';
+import { OverlayView } from './OverlayView';
 import PDFMenu from './pdf/PDFMenu';
 import { PreviewCursor } from './PreviewCursor';
-import { FilterBox } from './search/FilterBox';
-import { OverlayView } from './OverlayView';
-import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
-import { CollectionStackingView } from './collections/CollectionStackingView';
-import { ScriptField } from '../../new_fields/ScriptField';
+import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView';
 
 @observer
 export class MainView extends React.Component {
@@ -49,6 +47,7 @@ export class MainView extends React.Component {
     @observable addMenuToggle = React.createRef<HTMLInputElement>();
     @observable public pwidth: number = 0;
     @observable public pheight: number = 0;
+    private dropDisposer?: DragManager.DragDropDisposer;
 
     @observable private dictationState = DictationManager.placeholder;
     @observable private dictationSuccessState: boolean | undefined = undefined;
@@ -164,6 +163,7 @@ export class MainView extends React.Component {
         //close presentation 
         window.removeEventListener("pointerdown", this.globalPointerDown);
         window.removeEventListener("pointerup", this.globalPointerUp);
+        this.dropDisposer && this.dropDisposer();
     }
 
     constructor(props: Readonly<{}>) {
@@ -287,18 +287,15 @@ export class MainView extends React.Component {
         let workspaces: FieldResult<Doc>;
         let freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] };
-        let mainDoc = Docs.Create.DockDocument([this.userDoc, freeformDoc], JSON.stringify(dockingLayout), {}, id);
+        let mainDoc = Docs.Create.DockDocument([freeformDoc], JSON.stringify(dockingLayout), {}, id);
         if (this.userDoc && ((workspaces = Cast(this.userDoc.workspaces, Doc)) instanceof Doc)) {
-            const list = Cast((workspaces).data, listSpec(Doc));
-            if (list) {
-                if (!this.userDoc.linkManagerDoc) {
-                    let linkManagerDoc = new Doc();
-                    linkManagerDoc.allLinks = new List<Doc>([]);
-                    this.userDoc.linkManagerDoc = linkManagerDoc;
-                }
-                list.push(mainDoc);
-                mainDoc.title = `Workspace ${list.length}`;
+            if (!this.userDoc.linkManagerDoc) {
+                let linkManagerDoc = new Doc();
+                linkManagerDoc.allLinks = new List<Doc>([]);
+                this.userDoc.linkManagerDoc = linkManagerDoc;
             }
+            Doc.AddDocToList(workspaces, "data", mainDoc);
+            mainDoc.title = `Workspace ${DocListCast(workspaces.data).length}`;
         }
         // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
         setTimeout(() => {
@@ -350,6 +347,10 @@ export class MainView extends React.Component {
         return true;
     }
 
+    drop = action((e: Event, de: DragManager.DropEvent) => {
+        (de.data as DragManager.DocumentDragData).draggedDocuments.map(doc => Doc.AddDocToList(CurrentUserUtils.UserDocument, "docButtons", doc));
+    })
+
     onDrop = (e: React.DragEvent<HTMLDivElement>) => {
         e.preventDefault();
         e.stopPropagation();
@@ -460,13 +461,14 @@ export class MainView extends React.Component {
     }
     @computed
     get flyout() {
-        let sidebar: FieldResult<Field>;
-        if (!this.userDoc || !((sidebar = this.userDoc.sidebarContainer) instanceof Doc)) {
+        let sidebarContent = this.userDoc && this.userDoc.sidebarContainer;
+        if (!(sidebarContent instanceof Doc)) {
             return (null);
         }
         (Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc).columnWidth = this.flyoutWidthFunc() / 3 - 30;
+        let buttonBarHeight = 85;
         return <div style={{ display: "flex", flexDirection: "column", position: "absolute", width: "100%", height: "100%" }}>
-            <div style={{ position: "relative", height: "85px", width: "100%" }}>
+            <div style={{ position: "relative", height: `${buttonBarHeight}px`, width: "100%" }}>
                 <DocumentView
                     Document={Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc}
                     DataDoc={undefined}
@@ -492,9 +494,9 @@ export class MainView extends React.Component {
                     getScale={returnOne}>
                 </DocumentView>
             </div>
-            <div style={{ position: "relative", height: "calc(100% - 80px)", width: "100%" }}>
+            <div style={{ position: "relative", height: `calc(100% - ${buttonBarHeight}px)`, width: "100%", overflow: "auto" }}>
                 <DocumentView
-                    Document={this.isSearchVisible ? Cast(CurrentUserUtils.UserDocument.searchBox, Doc) as Doc : sidebar}
+                    Document={sidebarContent}
                     DataDoc={undefined}
                     addDocument={undefined}
                     addDocTab={this.addDocTabFunc}
@@ -598,65 +600,69 @@ export class MainView extends React.Component {
         DocServer.setFieldWriteMode("viewType", mode2);
     }
 
+    protected createDropTarget = (ele: HTMLLabelElement) => { //used for stacking and masonry view
+        this.dropDisposer && this.dropDisposer();
+        if (ele) {
+            this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+        }
+    }
 
     @observable private _colorPickerDisplay = false;
     /* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */
     nodesMenu() {
-        let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg";
-
-        let addColNode = action(() => Docs.Create.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" }));
-        let addPresNode = action(() => Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List<Doc>(), { width: 200, height: 500, title: "a presentation trail" }));
-        let addWebNode = action(() => Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" }));
-        let addDragboxNode = action(() => Docs.Create.DragboxDocument({ width: 40, height: 40, title: "drag collection" }));
-        let addImageNode = action(() => Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" }));
-        let addButtonDocument = action(() => Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" }));
-        let addImportCollectionNode = action(() => Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 }));
         // let youtubeurl = "https://www.youtube.com/embed/TqcApsGRzWw";
         // let addYoutubeSearcher = action(() => Docs.Create.YoutubeDocument(youtubeurl, { width: 600, height: 600, title: "youtube search" }));
 
         // let googlePhotosSearch = () => GooglePhotosClientUtils.CollectionFromSearch(Docs.Create.MasonryDocument, { included: [GooglePhotosClientUtils.ContentCategories.LANDSCAPES] });
 
-        let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc | Promise<Doc>][] = [
-            //[React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
-            //[React.createRef<HTMLDivElement>(), "tv", "Add Presentation Trail", addPresNode],
-            //[React.createRef<HTMLDivElement>(), "globe-asia", "Add Website", addWebNode],
-            //[React.createRef<HTMLDivElement>(), "bolt", "Add Button", addButtonDocument],
-            //[React.createRef<HTMLDivElement>(), "file", "Add Document Dragger", addDragboxNode],
-            // [React.createRef<HTMLDivElement>(), "object-group", "Test Google Photos Search", googlePhotosSearch],
-            //[React.createRef<HTMLDivElement>(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], //remove at some point in favor of addImportCollectionNode
-            //[React.createRef<HTMLDivElement>(), "play", "Add Youtube Searcher", addYoutubeSearcher],
-        ];
-        //if (!ClientUtils.RELEASE) btns.unshift([React.createRef<HTMLDivElement>(), "cat", "Add Cat Image", addImageNode]);
-
         return < div id="add-nodes-menu" style={{ left: (this.flyoutTranslate ? this.flyoutWidth : 0) + 20, bottom: 20 }} >
 
             <input type="checkbox" id="add-menu-toggle" ref={this.addMenuToggle} />
-            <label htmlFor="add-menu-toggle" style={{ marginTop: 2 }} title="Close Menu"><p>+</p></label>
+            <label htmlFor="add-menu-toggle" ref={this.createDropTarget} title="Close Menu"><p>+</p></label>
 
             <div id="add-options-content">
-                <ul id="add-options-list">
-                    {/* <li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li> */}
-                    <li key="undo"><button className="add-button round-button" title="Undo" style={{ opacity: UndoManager.CanUndo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button></li>
-                    <li key="redo"><button className="add-button round-button" title="Redo" style={{ opacity: UndoManager.CanRedo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button></li>
-                    {btns.map(btn =>
-                        <li key={btn[1]} ><div ref={btn[0]}>
-                            <button className="round-button add-button" title={btn[2]} onPointerDown={SetupDrag(btn[0], btn[3])}>
-                                <FontAwesomeIcon icon={btn[1]} size="sm" />
-                            </button>
-                        </div></li>)}
-                    {/* <li key="undoTest"><button className="add-button round-button" title="Click if undo isn't working" onClick={() => UndoManager.TraceOpenBatches()}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li> */}
-                    <li key="color"><button className="add-button round-button" title="Select Color" style={{ zIndex: 1000 }} onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} >
-                        <div className="toolbar-color-picker" onClick={this.onColorClick} style={this._colorPickerDisplay ? { color: "black", display: "block" } : { color: "black", display: "none" }}>
-                            <SketchPicker color={InkingControl.Instance.selectedColor} onChange={InkingControl.Instance.switchColor} />
-                        </div>
-                    </div></button></li>
-                    <li key="ink" style={{ paddingRight: "6px" }}><button className="toolbar-button round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /> </button></li>
-                    <li key="pen"><button onClick={() => InkingControl.Instance.switchTool(InkTool.Pen)} title="Pen" style={this.selected(InkTool.Pen)}><FontAwesomeIcon icon="pen" size="lg" /></button></li>
-                    <li key="marker"><button onClick={() => InkingControl.Instance.switchTool(InkTool.Highlighter)} title="Highlighter" style={this.selected(InkTool.Highlighter)}><FontAwesomeIcon icon="highlighter" size="lg" /></button></li>
-                    <li key="eraser"><button onClick={() => InkingControl.Instance.switchTool(InkTool.Eraser)} title="Eraser" style={this.selected(InkTool.Eraser)}><FontAwesomeIcon icon="eraser" size="lg" /></button></li>
-                    <li key="inkControls"><InkingControl /></li>
-                    <li key="logout"><button onClick={() => window.location.assign(Utils.prepend(RouteStore.logout))}>{CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}</button></li>
-                </ul>
+                {/* <li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li> */}
+                <button key="undo" className="add-button round-button" title="Undo" style={{ opacity: UndoManager.CanUndo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button>
+                <button key="redo" className="add-button round-button" title="Redo" style={{ opacity: UndoManager.CanRedo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button>
+
+                {DocListCast(CurrentUserUtils.UserDocument.docButtons).map(doc => <div className="mainView-docBtn" key={StrCast(doc.title)} >
+                    <DocumentView
+                        Document={doc}
+                        DataDoc={undefined}
+                        addDocument={undefined}
+                        addDocTab={this.addDocTabFunc}
+                        pinToPres={emptyFunction}
+                        removeDocument={undefined}
+                        ruleProvider={undefined}
+                        onClick={undefined}
+                        ScreenToLocalTransform={Transform.Identity}
+                        ContentScaling={() => 35 / NumCast(doc.nativeWidth, 35)}
+                        PanelWidth={() => 35}
+                        PanelHeight={() => 35}
+                        renderDepth={0}
+                        focus={emptyFunction}
+                        backgroundColor={returnEmptyString}
+                        parentActive={returnTrue}
+                        whenActiveChanged={emptyFunction}
+                        bringToFront={emptyFunction}
+                        ContainingCollectionView={undefined}
+                        ContainingCollectionDoc={undefined}
+                        zoomToScale={emptyFunction}
+                        getScale={returnOne}>
+                    </DocumentView>
+                </div>)}
+                {/* <li key="undoTest"><button className="add-button round-button" title="Click if undo isn't working" onClick={() => UndoManager.TraceOpenBatches()}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li> */}
+                <button key="color" className="add-button round-button" title="Select Color" style={{ zIndex: 1000 }} onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} >
+                    <div className="toolbar-color-picker" onClick={this.onColorClick} style={this._colorPickerDisplay ? { color: "black", display: "block" } : { color: "black", display: "none" }}>
+                        <SketchPicker color={InkingControl.Instance.selectedColor} onChange={InkingControl.Instance.switchColor} />
+                    </div>
+                </div></button>
+                <button className="toolbar-button round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /> </button>
+                <button key="pen" onClick={() => InkingControl.Instance.switchTool(InkTool.Pen)} title="Pen" style={this.selected(InkTool.Pen)}><FontAwesomeIcon icon="pen" size="lg" /></button>
+                <button key="marker" onClick={() => InkingControl.Instance.switchTool(InkTool.Highlighter)} title="Highlighter" style={this.selected(InkTool.Highlighter)}><FontAwesomeIcon icon="highlighter" size="lg" /></button>
+                <button key="eraser" onClick={() => InkingControl.Instance.switchTool(InkTool.Eraser)} title="Eraser" style={this.selected(InkTool.Eraser)}><FontAwesomeIcon icon="eraser" size="lg" /></button>
+                <InkingControl key="inkControls" />
+                <button key="logout" onClick={() => window.location.assign(Utils.prepend(RouteStore.logout))}>{CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}</button>
             </div>
         </div >;
     }
@@ -668,16 +674,6 @@ export class MainView extends React.Component {
         this._colorPickerDisplay = close ? false : !this._colorPickerDisplay;
     }
 
-    /* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */
-    @computed
-    get miscButtons() {
-        return [
-            //this.isSearchVisible ? <div className="main-searchDiv" key="search" style={{ top: '34px', right: '1px', position: 'absolute' }} > <FilterBox /> </div> : null,
-        ];
-
-    }
-
-    @observable isSearchVisible = false;
     @action.bound
     toggleSearch = () => {
         PromiseValue(Cast(CurrentUserUtils.UserDocument.Search, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
@@ -716,7 +712,6 @@ export class MainView extends React.Component {
                 <PreviewCursor />
                 <ContextMenu />
                 {this.nodesMenu()}
-                {this.miscButtons}
                 <PDFMenu />
                 <OverlayView />
             </div >
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 6f5587b5a..a325e86c6 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -188,7 +188,7 @@ class TreeView extends React.Component<TreeViewProps> {
 
     onWorkspaceContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) {
+            if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking && this.props.document !== CurrentUserUtils.UserDocument.workspaces) {
                 ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.document), icon: "tv" });
                 ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "inTab"), icon: "folder" });
                 ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "onRight"), icon: "caret-square-right" });
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.scss b/src/client/views/nodes/CollectionFreeFormDocumentView.scss
index c0d9e1267..af9232c2f 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.scss
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.scss
@@ -2,4 +2,6 @@
     transform-origin: left top;
     position: absolute;
     background-color: transparent;
+    top:0;
+    left:0;
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 758222e52..20920a9b8 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -614,6 +614,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             DataDoc={this.props.DataDoc} />);
     }
     render() {
+        if (!this.props.Document) return (null);
         let animDims = this.props.Document.animateToDimensions ? Array.from(Cast(this.props.Document.animateToDimensions, listSpec("number"))!) : undefined;
         const ruleColor = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleColor_" + this.Document.heading]) : undefined;
         const ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx
index 8429382e3..ab3368b94 100644
--- a/src/client/views/nodes/DragBox.tsx
+++ b/src/client/views/nodes/DragBox.tsx
@@ -51,9 +51,15 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
             const onDragStart = this.Document.onDragStart;
             e.stopPropagation();
             e.preventDefault();
-            let res = onDragStart && onDragStart.script.run({ this: this.props.Document }).result;
-            let doc = (res as Doc) || Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
-            DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY);
+            DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([this.props.Document]), e.clientX, e.clientY, {
+                finishDrag: (dropData) => {
+                    let res = onDragStart && onDragStart.script.run({ this: this.props.Document }).result;
+                    let doc = (res as Doc) || Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
+                    dropData.droppedDocuments = [doc];
+                },
+                handlers: { dragComplete: emptyFunction },
+                hideSource: false
+            });
         }
         e.stopPropagation();
         e.preventDefault();
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 02a82e0ed..63b412a23 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -93,7 +93,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
         if (e.key === "PageUp" || e.key === "ArrowUp" || e.key === "ArrowLeft") {
             this.backPage();
         }
-    })
+    });
 
     @undoBatch
     @action
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index e1bb91838..7408fd92b 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -127,46 +127,38 @@ export class CurrentUserUtils {
             Search.targetContainer = doc.sidebarContainer;
             Search.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.searchBox");
 
-            let createCollection = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Collection", icon: "folder" });
-            let createWebPage = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Web Page", icon: "globe-asia" });
+            let createCollection = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Collection", icon: "folder" });
+            let createWebPage = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Web Page", icon: "globe-asia" });
             createWebPage.onDragStart = ScriptField.MakeFunction('Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })');
-            let createCatImage = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Image", icon: "cat" });
+            let createCatImage = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Image", icon: "cat" });
             createCatImage.onDragStart = ScriptField.MakeFunction('Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })');
-            let createButton = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Button", icon: "bolt" });
+            let createButton = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Button", icon: "bolt" });
             createButton.onDragStart = ScriptField.MakeFunction('Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })');
-            let createPresentation = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Presentation", icon: "tv" });
+            let createPresentation = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Presentation", icon: "tv" });
             createPresentation.onDragStart = ScriptField.MakeFunction('Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List<Doc>(), { width: 200, height: 500, title: "a presentation trail" })');
-            let createFolderImport = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Import Folder", icon: "cloud-upload-alt" });
+            let createFolderImport = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Import Folder", icon: "cloud-upload-alt" });
             createFolderImport.onDragStart = ScriptField.MakeFunction('Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })');
             const creators = Docs.Create.MasonryDocument([createCollection, createWebPage, createCatImage, createButton, createPresentation, createFolderImport], { width: 500, height: 50, columnWidth: 35, chromeStatus: "disabled", title: "buttons" });
             Create.targetContainer = doc.sidebarContainer;
             Create.creators = creators;
             Create.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.creators");
 
-            const buttons = Docs.Create.StackingDocument([Search, Library, Create], { width: 500, height: 80, chromeStatus: "disabled", title: "buttons" });
-            buttons.sectionFilter = "title";
-            buttons.boxShadow = "0 0";
-            buttons.ignoreClick = true;
-            buttons.hideHeadings = true;
-            doc.libraryButtons = buttons;
+            const libraryButtons = Docs.Create.StackingDocument([Search, Library, Create], { width: 500, height: 80, chromeStatus: "disabled", title: "buttons" });
+            libraryButtons.sectionFilter = "title";
+            libraryButtons.boxShadow = "0 0";
+            libraryButtons.ignoreClick = true;
+            libraryButtons.hideHeadings = true;
+            libraryButtons.backgroundColor = "lightgrey";
 
+            doc.libraryButtons = libraryButtons;
             doc.Library = Library;
             doc.Create = Create;
             doc.Search = Search;
-            (Library.onClick as ScriptField).script.run({ this: Library });
-            //(doc.sidebarContainer as Doc).proto = library;
         }
-        PromiseValue(Cast(doc.libraryButtons, Doc)).then(libraryButtons => {
-            if (libraryButtons) {
-                libraryButtons.backgroundColor = "lightgrey";
-            }
-        });
-
-        PromiseValue(Cast(doc.sidebar, Doc)).then(sidebar => {
-            if (sidebar) {
-                sidebar.backgroundColor = "lightgrey";
-            }
-        });
+        PromiseValue(Cast(doc.libraryButtons, Doc)).then(libraryButtons => { });
+        PromiseValue(Cast(doc.Library, Doc)).then(library => library && library.library && library.targetContainer && (library.onClick as ScriptField).script.run({ this: library }));
+        PromiseValue(Cast(doc.Create, Doc)).then(async create => create && create.creators && create.targetContainer);
+        PromiseValue(Cast(doc.Search, Doc)).then(async search => search && search.searchBox && search.targetContainer);
 
         if (doc.overlays === undefined) {
             const overlays = Docs.Create.FreeformDocument([], { title: "Overlays" });
@@ -178,8 +170,7 @@ export class CurrentUserUtils {
             PromiseValue(Cast(doc.overlays, Doc)).then(overlays => overlays && Doc.AddDocToList(overlays, "data", doc.linkFollowBox = Docs.Create.LinkFollowBoxDocument({ x: 250, y: 20, width: 500, height: 370, title: "Link Follower" })));
         }
 
-        StrCast(doc.title).indexOf("@") !== -1 && (doc.title = (StrCast(doc.title).split("@")[0] + "'s Library").toUpperCase());
-        StrCast(doc.title).indexOf("'s Library") !== -1 && (doc.title = StrCast(doc.title).toUpperCase());
+        doc.title = "DOCUMENTS";
         doc.backgroundColor = "#eeeeee";
         doc.width = 100;
         doc.preventTreeViewOpen = true;
-- 
cgit v1.2.3-70-g09d2


From 34a8b9dd402a247e7ad0a57115935b1e3a04a8d3 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 12 Oct 2019 12:25:02 -0400
Subject: cleaned up notifs and bottom menu icons

---
 src/client/views/Main.scss                         | 21 +++-----
 src/client/views/MainView.tsx                      | 57 +++++++++-------------
 src/client/views/MainViewNotifs.scss               | 18 +++++++
 src/client/views/MainViewNotifs.tsx                | 32 ++++++++++++
 .../views/collections/CollectionTreeView.tsx       | 26 +---------
 .../authentication/models/current_user_utils.ts    |  1 -
 6 files changed, 79 insertions(+), 76 deletions(-)
 create mode 100644 src/client/views/MainViewNotifs.scss
 create mode 100644 src/client/views/MainViewNotifs.tsx

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

diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 705da7b35..debb941c8 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -93,21 +93,6 @@ button:hover {
     right: 0px;
 }
 
-.main-notifs-badge {
-    position: absolute;
-    top: -10px;
-    right: -10px;
-    color: white;
-    background: #f44b42;
-    font-weight: 300;
-    border-radius: 100%;
-    width: 25px;
-    height: 25px;
-    text-align: center;
-    padding-top: 4px;
-    font-size: 12px;
-}
-
 //toolbar stuff
 #toolbar {
     position: absolute;
@@ -237,6 +222,12 @@ ul#add-options-list {
         padding: 0;
     }
 }
+.mainView-logout {
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    font-size: 8px;
+}
 
 .mainView-libraryFlyout {
     height: 100%;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 1fa231659..e8ffa5987 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -16,7 +16,7 @@ import { ScriptField } from '../../new_fields/ScriptField';
 import { BoolCast, Cast, FieldValue, PromiseValue, StrCast, NumCast } from '../../new_fields/Types';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { RouteStore } from '../../server/RouteStore';
-import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils } from '../../Utils';
+import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils, returnFalse } from '../../Utils';
 import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { Docs, DocumentOptions } from '../documents/Documents';
@@ -40,6 +40,7 @@ import { OverlayView } from './OverlayView';
 import PDFMenu from './pdf/PDFMenu';
 import { PreviewCursor } from './PreviewCursor';
 import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView';
+import { MainViewNotifs } from './MainViewNotifs';
 
 @observer
 export class MainView extends React.Component {
@@ -47,6 +48,7 @@ export class MainView extends React.Component {
     @observable addMenuToggle = React.createRef<HTMLInputElement>();
     @observable public pwidth: number = 0;
     @observable public pheight: number = 0;
+    private _buttonBarHeight = 85;
     private dropDisposer?: DragManager.DragDropDisposer;
 
     @observable private dictationState = DictationManager.placeholder;
@@ -138,24 +140,6 @@ export class MainView extends React.Component {
         firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag);
         window.removeEventListener("keydown", KeyManager.Instance.handle);
         window.addEventListener("keydown", KeyManager.Instance.handle);
-
-        if (this.userDoc) {
-            reaction(() => {
-                let workspaces = this.userDoc.workspaces;
-                let recent = this.userDoc.recentlyClosed;
-                if (!(recent instanceof Doc)) return 0;
-                if (!(workspaces instanceof Doc)) return 0;
-                let workspacesDoc = workspaces;
-                let recentDoc = recent;
-                let libraryHeight = this.getPHeight() - workspacesDoc[HeightSym]() - recentDoc[HeightSym]() - 20 + this.userDoc[HeightSym]() * 0.00001;
-                return libraryHeight;
-            }, (libraryHeight: number) => {
-                if (libraryHeight && Math.abs(this.userDoc[HeightSym]() - libraryHeight) > 5) {
-                    this.userDoc.height = libraryHeight;
-                }
-                (Cast(this.userDoc.recentlyClosed, Doc) as Doc).allowClear = true;
-            }, { fireImmediately: true });
-        }
     }
 
     componentWillUnMount() {
@@ -340,7 +324,7 @@ export class MainView extends React.Component {
             if (this.userDoc && (col = await Cast(this.userDoc.optionalRightCollection, Doc))) {
                 const l = Cast(col.data, listSpec(Doc));
                 if (l) {
-                    runInAction(() => CollectionTreeView.NotifsCol = col);
+                    runInAction(() => MainViewNotifs.NotifsCol = col);
                 }
             }
         }, 100);
@@ -349,7 +333,7 @@ export class MainView extends React.Component {
 
     drop = action((e: Event, de: DragManager.DropEvent) => {
         (de.data as DragManager.DocumentDragData).draggedDocuments.map(doc => Doc.AddDocToList(CurrentUserUtils.UserDocument, "docButtons", doc));
-    })
+    });
 
     onDrop = (e: React.DragEvent<HTMLDivElement>) => {
         e.preventDefault();
@@ -368,6 +352,9 @@ export class MainView extends React.Component {
     getPHeight = () => {
         return this.pheight;
     }
+    getContentsHeight = () => {
+        return this.pheight - this._buttonBarHeight;
+    }
 
     @observable flyoutWidth: number = 250;
     @observable flyoutTranslate: boolean = true;
@@ -465,12 +452,12 @@ export class MainView extends React.Component {
         if (!(sidebarContent instanceof Doc)) {
             return (null);
         }
-        (Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc).columnWidth = this.flyoutWidthFunc() / 3 - 30;
-        let buttonBarHeight = 85;
+        let libraryButtonDoc = Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc;
+        libraryButtonDoc.columnWidth = this.flyoutWidthFunc() / 3 - 30;
         return <div style={{ display: "flex", flexDirection: "column", position: "absolute", width: "100%", height: "100%" }}>
-            <div style={{ position: "relative", height: `${buttonBarHeight}px`, width: "100%" }}>
+            <div style={{ position: "relative", height: `${this._buttonBarHeight}px`, width: "100%" }}>
                 <DocumentView
-                    Document={Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc}
+                    Document={libraryButtonDoc}
                     DataDoc={undefined}
                     addDocument={undefined}
                     addDocTab={this.addDocTabFunc}
@@ -494,20 +481,20 @@ export class MainView extends React.Component {
                     getScale={returnOne}>
                 </DocumentView>
             </div>
-            <div style={{ position: "relative", height: `calc(100% - ${buttonBarHeight}px)`, width: "100%", overflow: "auto" }}>
+            <div style={{ position: "relative", height: `calc(100% - ${this._buttonBarHeight}px)`, width: "100%", overflow: "auto" }}>
                 <DocumentView
                     Document={sidebarContent}
                     DataDoc={undefined}
                     addDocument={undefined}
                     addDocTab={this.addDocTabFunc}
                     pinToPres={emptyFunction}
-                    removeDocument={undefined}
+                    removeDocument={returnFalse}
                     ruleProvider={undefined}
                     onClick={undefined}
                     ScreenToLocalTransform={Transform.Identity}
                     ContentScaling={returnOne}
                     PanelWidth={this.flyoutWidthFunc}
-                    PanelHeight={this.getPHeight}
+                    PanelHeight={this.getContentsHeight}
                     renderDepth={0}
                     focus={emptyFunction}
                     backgroundColor={returnEmptyString}
@@ -519,16 +506,16 @@ export class MainView extends React.Component {
                     zoomToScale={emptyFunction}
                     getScale={returnOne}>
                 </DocumentView>
+                <button className="mainView-logout" key="logout" onClick={() => window.location.assign(Utils.prepend(RouteStore.logout))}>
+                    {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}
+                </button>
             </div></div>;
     }
 
     @computed
     get mainContent() {
-        if (!this.userDoc) {
-            return (<div>{this.dockingContent}</div>);
-        }
-        let sidebar = this.userDoc.sidebarContainer;
-        if (!(sidebar instanceof Doc)) {
+        let sidebar = this.userDoc && this.userDoc.sidebarContainer;
+        if (!this.userDoc || !(sidebar instanceof Doc)) {
             return (null);
         }
         return (
@@ -617,6 +604,7 @@ export class MainView extends React.Component {
 
         return < div id="add-nodes-menu" style={{ left: (this.flyoutTranslate ? this.flyoutWidth : 0) + 20, bottom: 20 }} >
 
+            <MainViewNotifs></MainViewNotifs>
             <input type="checkbox" id="add-menu-toggle" ref={this.addMenuToggle} />
             <label htmlFor="add-menu-toggle" ref={this.createDropTarget} title="Close Menu"><p>+</p></label>
 
@@ -632,7 +620,7 @@ export class MainView extends React.Component {
                         addDocument={undefined}
                         addDocTab={this.addDocTabFunc}
                         pinToPres={emptyFunction}
-                        removeDocument={undefined}
+                        removeDocument={(doc: Doc) => Doc.RemoveDocFromList(CurrentUserUtils.UserDocument, "docButtons", doc)}
                         ruleProvider={undefined}
                         onClick={undefined}
                         ScreenToLocalTransform={Transform.Identity}
@@ -662,7 +650,6 @@ export class MainView extends React.Component {
                 <button key="marker" onClick={() => InkingControl.Instance.switchTool(InkTool.Highlighter)} title="Highlighter" style={this.selected(InkTool.Highlighter)}><FontAwesomeIcon icon="highlighter" size="lg" /></button>
                 <button key="eraser" onClick={() => InkingControl.Instance.switchTool(InkTool.Eraser)} title="Eraser" style={this.selected(InkTool.Eraser)}><FontAwesomeIcon icon="eraser" size="lg" /></button>
                 <InkingControl key="inkControls" />
-                <button key="logout" onClick={() => window.location.assign(Utils.prepend(RouteStore.logout))}>{CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}</button>
             </div>
         </div >;
     }
diff --git a/src/client/views/MainViewNotifs.scss b/src/client/views/MainViewNotifs.scss
new file mode 100644
index 000000000..25ec95643
--- /dev/null
+++ b/src/client/views/MainViewNotifs.scss
@@ -0,0 +1,18 @@
+.mainNotifs-container {
+    position:absolute;
+
+    .mainNotifs-badge {
+        position: absolute;
+        top: -10px;
+        right: -10px;
+        color: white;
+        background: #f44b42;
+        font-weight: 300;
+        border-radius: 100%;
+        width: 25px;
+        height: 25px;
+        text-align: center;
+        padding-top: 4px;
+        font-size: 12px;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/MainViewNotifs.tsx b/src/client/views/MainViewNotifs.tsx
new file mode 100644
index 000000000..09fa1cb0c
--- /dev/null
+++ b/src/client/views/MainViewNotifs.tsx
@@ -0,0 +1,32 @@
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import "normalize.css";
+import * as React from 'react';
+import { Doc, DocListCast, Opt } from '../../new_fields/Doc';
+import { emptyFunction } from '../../Utils';
+import { SetupDrag } from '../util/DragManager';
+import "./MainViewNotifs.scss";
+import { CollectionDockingView } from './collections/CollectionDockingView';
+
+
+@observer
+export class MainViewNotifs extends React.Component {
+
+    @observable static NotifsCol: Opt<Doc>;
+    openNotifsCol = () => {
+        if (MainViewNotifs.NotifsCol) {
+            CollectionDockingView.AddRightSplit(MainViewNotifs.NotifsCol, undefined);
+        }
+    }
+    render() {
+        const length = MainViewNotifs.NotifsCol ? DocListCast(MainViewNotifs.NotifsCol.data).length : 0;
+        const notifsRef = React.createRef<HTMLDivElement>();
+        const dragNotifs = action(() => MainViewNotifs.NotifsCol!);
+        return <div className="mainNotifs-container" ref={notifsRef}>
+            <button className="mainNotifs-badge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}
+                onClick={this.openNotifsCol} onPointerDown={MainViewNotifs.NotifsCol ? SetupDrag(notifsRef, dragNotifs) : emptyFunction}>
+                {length}
+            </button>
+        </div>;
+    }
+}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index a325e86c6..abaa9662c 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -517,8 +517,6 @@ export class CollectionTreeView extends CollectionSubView(Document) {
     private treedropDisposer?: DragManager.DragDropDisposer;
     private _mainEle?: HTMLDivElement;
 
-    @observable static NotifsCol: Opt<Doc>;
-
     @computed get resolvedDataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; }
 
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
@@ -557,31 +555,10 @@ export class CollectionTreeView extends CollectionSubView(Document) {
     }
     outerXf = () => Utils.GetScreenTransform(this._mainEle!);
     onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {});
-    openNotifsCol = () => {
-        if (CollectionTreeView.NotifsCol) {
-            this.props.addDocTab(CollectionTreeView.NotifsCol, undefined, "onRight");
-        }
-    }
 
-    @computed get renderNotifsButton() {
-        const length = CollectionTreeView.NotifsCol ? DocListCast(CollectionTreeView.NotifsCol.data).length : 0;
-        const notifsRef = React.createRef<HTMLDivElement>();
-        const dragNotifs = action(() => CollectionTreeView.NotifsCol!);
-        return <div id="toolbar" key="toolbar">
-            <div ref={notifsRef}>
-                <button className="toolbar-button round-button" title="Notifs"
-                    onClick={this.openNotifsCol} onPointerDown={CollectionTreeView.NotifsCol ? SetupDrag(notifsRef, dragNotifs) : emptyFunction}>
-                    <FontAwesomeIcon icon={faBell} size="sm" />
-                </button>
-                <div className="main-notifs-badge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}>
-                    {length}
-                </div>
-            </div>
-        </div >;
-    }
     @computed get renderClearButton() {
         return <div id="toolbar" key="toolbar">
-            <button className="toolbar-button round-button" title="Notifs"
+            <button className="toolbar-button round-button" title="Empty"
                 onClick={undoBatch(action(() => Doc.GetProto(this.props.Document)[this.props.fieldKey] = undefined))}>
                 <FontAwesomeIcon icon={faTrash} size="sm" />
             </button>
@@ -614,7 +591,6 @@ 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, false, false, false);
                     })} />
-                {this.props.Document.workspaceLibrary ? this.renderNotifsButton : (null)}
                 {this.props.Document.allowClear ? this.renderClearButton : (null)}
                 <ul className="no-indent" style={{ width: "max-content" }} >
                     {
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 7408fd92b..874f49f10 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -115,7 +115,6 @@ export class CurrentUserUtils {
             library.xMargin = 5;
             library.yMargin = 5;
             library.boxShadow = "1 1 3";
-            library.workspaceLibrary = true; // flag that this is the document that shows the Notifications button when documents are shared
             Library.targetContainer = doc.sidebarContainer;
             Library.library = library;
             Library.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.library");
-- 
cgit v1.2.3-70-g09d2

