From c96e61de2cea0fee1f6c80caf426535dbdc3d37c Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 16 May 2019 16:08:53 -0400 Subject: Changed Clear DB Button --- src/client/views/Main.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/Main.tsx') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 7aef7d3e5..866da010a 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -244,7 +244,7 @@ export class Main extends React.Component { let logoutRef = React.createRef(); return [ - , + ,
-- cgit v1.2.3-70-g09d2 From 6e65b1e32eb972358d9c8dbd7f8d984d3baefc4b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 16 May 2019 22:56:19 -0400 Subject: fixed links so that you can adjust the pan location of a collection when its a link endoint --- src/client/util/DocumentManager.ts | 5 +++-- src/client/util/DragManager.ts | 2 +- src/client/util/TooltipTextMenu.tsx | 1 - src/client/views/Main.tsx | 2 +- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 3 ++- src/client/views/nodes/DocumentView.tsx | 11 ++++++----- src/client/views/nodes/FormattedTextBox.tsx | 4 +--- src/client/views/nodes/LinkBox.tsx | 2 +- src/client/views/nodes/LinkMenu.tsx | 5 ++--- src/new_fields/Doc.ts | 10 ++++++---- 10 files changed, 23 insertions(+), 22 deletions(-) (limited to 'src/client/views/Main.tsx') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 51f5fbe9f..c6962ad8d 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -110,7 +110,8 @@ export class DocumentManager { } @undoBatch - public jumpToDocument = async (doc: Doc): Promise => { + public jumpToDocument = async (docDelegate: Doc, makeCopy: boolean = true): Promise => { + let doc = docDelegate.proto ? docDelegate.proto : docDelegate; const page = NumCast(doc.page, undefined); const contextDoc = await Cast(doc.annotationOn, Doc); if (contextDoc) { @@ -122,7 +123,7 @@ export class DocumentManager { docView.props.focus(docView.props.Document); } else { if (!contextDoc) { - CollectionDockingView.Instance.AddRightSplit(Doc.MakeDelegate(doc)); + CollectionDockingView.Instance.AddRightSplit(docDelegate ? (makeCopy ? Doc.MakeCopy(docDelegate) : docDelegate) : Doc.MakeDelegate(doc)); } else { let contextView = DocumentManager.Instance.getDocumentView(contextDoc); if (contextView) { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 05eb5c38a..7f75a95f0 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -336,7 +336,7 @@ export namespace DragManager { x: e.x, y: e.y, data: dragData, - mods: e.ctrlKey ? "Control" : "" + mods: e.altKey ? "AltKey" : "" } }) ); diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 223921428..4d40d09b2 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -186,7 +186,6 @@ export class TooltipTextMenu { let node = this.view.state.selection.$from.nodeAfter; let link = node && node.marks.find(m => m.type.name === "link"); if (link) { - console.log("Link to : " + link.attrs.href); let href: string = link.attrs.href; if (href.indexOf(DocServer.prepend("/doc/")) === 0) { let docid = href.replace(DocServer.prepend("/doc/"), ""); diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 866da010a..51ad0aa49 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -244,7 +244,7 @@ export class Main extends React.Component { let logoutRef = React.createRef(); return [ - , + ,
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index d4f660b3f..001ce3095 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -205,11 +205,12 @@ export class CollectionFreeFormDocumentView extends DocComponent(Docu let sourceDoc = de.data.linkSourceDocument; let destDoc = this.props.Document; - const protoDest = destDoc.proto; - const protoSrc = sourceDoc.proto; - if (de.mods === "Control") { + if (de.mods === "AltKey") { + const protoDest = destDoc.proto; + const protoSrc = sourceDoc.proto; let src = protoSrc ? protoSrc : sourceDoc; let dst = protoDest ? protoDest : destDoc; - dst.data = src; + dst.data = (src.data! as ObjectField)[Copy](); dst.nativeWidth = src.nativeWidth; dst.nativeHeight = src.nativeHeight; } else { - Doc.MakeLink(protoSrc ? protoSrc : sourceDoc, protoDest ? protoDest : destDoc); + Doc.MakeLink(sourceDoc, destDoc); de.data.droppedDocuments.push(destDoc); } e.stopPropagation(); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index f30022508..bf98fb40b 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -111,9 +111,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let sourceDoc = de.data.linkSourceDocument; let destDoc = this.props.Document; - const protoDest = destDoc.proto; - const protoSrc = sourceDoc.proto; - Doc.MakeLink(protoSrc ? protoSrc : sourceDoc, protoDest ? protoDest : destDoc); + Doc.MakeLink(sourceDoc, destDoc); de.data.droppedDocuments.push(destDoc); e.stopPropagation(); } diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 611cb66b6..68b692aad 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -31,7 +31,7 @@ export class LinkBox extends React.Component { @undoBatch onViewButtonPressed = async (e: React.PointerEvent): Promise => { e.stopPropagation(); - DocumentManager.Instance.jumpToDocument(this.props.pairedDoc); + DocumentManager.Instance.jumpToDocument(this.props.pairedDoc, e.altKey); } onEditButtonPressed = (e: React.PointerEvent): void => { diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 11117122d..4cf798249 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -6,8 +6,7 @@ import { LinkEditor } from "./LinkEditor"; import './LinkMenu.scss'; import React = require("react"); import { Doc, DocListCast } from "../../../new_fields/Doc"; -import { Cast, FieldValue } from "../../../new_fields/Types"; -import { listSpec } from "../../../new_fields/Schema"; +import { Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/RefField"; interface Props { @@ -24,7 +23,7 @@ export class LinkMenu extends React.Component { return links.map(link => { let doc = FieldValue(Cast(link[key], Doc)); if (doc) { - return this._editingLink = link)} type={type} />; + return this._editingLink = link)} type={type} />; } }); } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index c898f697b..ad3b880cd 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -216,6 +216,8 @@ export namespace Doc { } export function MakeLink(source: Doc, target: Doc) { + let protoSrc = source.proto ? source.proto : source; + let protoTarg = target.proto ? target.proto : target; UndoManager.RunInBatch(() => { let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); //let linkDoc = new Doc; @@ -226,15 +228,15 @@ export namespace Doc { linkDoc.proto!.linkedTo = target; linkDoc.proto!.linkedFrom = source; - let linkedFrom = Cast(target.linkedFromDocs, listSpec(Doc)); + let linkedFrom = Cast(protoTarg.linkedFromDocs, listSpec(Doc)); if (!linkedFrom) { - target.linkedFromDocs = linkedFrom = new List(); + protoTarg.linkedFromDocs = linkedFrom = new List(); } linkedFrom.push(linkDoc); - let linkedTo = Cast(source.linkedToDocs, listSpec(Doc)); + let linkedTo = Cast(protoSrc.linkedToDocs, listSpec(Doc)); if (!linkedTo) { - source.linkedToDocs = linkedTo = new List(); + protoSrc.linkedToDocs = linkedTo = new List(); } linkedTo.push(linkDoc); return linkDoc; -- cgit v1.2.3-70-g09d2 From 4e244951b7b18d7973360f423e8de80c42466228 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 17 May 2019 19:50:32 -0400 Subject: added right-click "open Tab" and double-click open tab --- src/client/views/Main.tsx | 1 + .../views/collections/CollectionDockingView.tsx | 20 ++++++++++++++++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 1 + .../views/nodes/CollectionFreeFormDocumentView.tsx | 19 +++++++++++++++++++ src/client/views/nodes/DocumentView.tsx | 2 ++ src/client/views/nodes/FieldView.tsx | 2 ++ src/client/views/nodes/ImageBox.tsx | 4 +++- 7 files changed, 46 insertions(+), 3 deletions(-) (limited to 'src/client/views/Main.tsx') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 51ad0aa49..177047a9a 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -166,6 +166,7 @@ export class Main extends React.Component { { @observable private _panelWidth = 0; @observable private _panelHeight = 0; @observable private _document: Opt; - + _stack: any; constructor(props: any) { super(props); + this._stack = (this.props as any).glContainer.parent.parent; DocServer.GetRefField(this.props.documentId).then(action((f: Opt) => this._document = f as Doc)); } @@ -420,6 +432,9 @@ export class DockedFrameRenderer extends React.Component { } get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; } + addDocTab = (doc: Doc) => { + CollectionDockingView.Instance.AddTab(this._stack, doc); + } get content() { if (!this._document) { return (null); @@ -441,6 +456,7 @@ export class DockedFrameRenderer extends React.Component { parentActive={returnTrue} whenActiveChanged={emptyFunction} focus={emptyFunction} + addDocTab={this.addDocTab} ContainingCollectionView={undefined} />
); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 6e4c79e62..ad32f70dc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -300,6 +300,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { parentActive: this.props.active, whenActiveChanged: this.props.whenActiveChanged, bringToFront: this.bringToFront, + addDocTab: this.props.addDocTab, }; } diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 00aa71656..a0efc3154 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -159,6 +159,7 @@ export class CollectionFreeFormDocumentView extends DocComponent { this._downX = e.clientX; @@ -166,6 +167,24 @@ export class CollectionFreeFormDocumentView extends DocComponent { + + document.removeEventListener("pointerup", this.onPointerUp); + if (Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2) { + this.props.addDocTab(this.props.Document); + } + e.stopPropagation(); } onClick = async (e: React.MouseEvent) => { e.stopPropagation(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5264773b0..9a5d21b49 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -57,6 +57,7 @@ export interface DocumentViewProps { whenActiveChanged: (isActive: boolean) => void; toggleMinimized: () => void; bringToFront: (doc: Doc) => void; + addDocTab: (doc: Doc) => void; } const schema = createSchema({ @@ -307,6 +308,7 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeButton }); cm.addItem({ description: "Fields", event: this.fieldsClicked }); cm.addItem({ description: "Center", event: () => this.props.focus(this.props.Document) }); + cm.addItem({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document) }); cm.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.Document) }); cm.addItem({ description: "Find aliases", event: async () => { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 34b6c5e70..b5dfd18eb 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -35,6 +35,7 @@ export interface FieldViewProps { isTopMost: boolean; selectOnLoad: boolean; addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean; + addDocTab: (document: Doc) => boolean; removeDocument?: (document: Doc) => boolean; moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; ScreenToLocalTransform: () => Transform; @@ -87,6 +88,7 @@ export class FieldView extends React.Component { return ( (ImageD let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx return ( -
+
Date: Sat, 18 May 2019 15:25:42 -0400 Subject: Refactored some things to get Debug viewer working again --- src/client/util/History.ts | 4 +- src/client/views/Main.tsx | 280 +--------------- src/client/views/MainView.tsx | 274 ++++++++++++++++ src/client/views/PresentationView.tsx | 1 - .../views/collections/CollectionTreeView.tsx | 8 +- src/debug/Viewer.tsx | 364 +++++++++++---------- 6 files changed, 468 insertions(+), 463 deletions(-) create mode 100644 src/client/views/MainView.tsx (limited to 'src/client/views/Main.tsx') diff --git a/src/client/util/History.ts b/src/client/util/History.ts index 92d2b2b44..545ea8629 100644 --- a/src/client/util/History.ts +++ b/src/client/util/History.ts @@ -1,7 +1,7 @@ import { Doc, Opt, Field } from "../../new_fields/Doc"; import { DocServer } from "../DocServer"; -import { Main } from "../views/Main"; import { RouteStore } from "../../server/RouteStore"; +import { MainView } from "../views/MainView"; export namespace HistoryUtil { export interface DocInitializerList { @@ -114,7 +114,7 @@ export namespace HistoryUtil { const field = await DocServer.GetRefField(url.docId); await Promise.all(Object.keys(url.initializers).map(id => initDoc(id, url.initializers[id]))); if (field instanceof Doc) { - Main.Instance.openWorkspace(field, true); + MainView.Instance.openWorkspace(field, true); } } diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 177047a9a..3d9750a85 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -1,281 +1,11 @@ -import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, configure, observable, runInAction, trace } from 'mobx'; -import { observer } from 'mobx-react'; -import "normalize.css"; -import * as React from 'react'; +import { MainView } from "./MainView"; +import { Docs } from "../documents/Documents"; +import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import * as ReactDOM from 'react-dom'; -import Measure from 'react-measure'; -import * as request from 'request'; -import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; -import { RouteStore } from '../../server/RouteStore'; -import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils'; -import { Docs } from '../documents/Documents'; -import { SetupDrag, DragManager } from '../util/DragManager'; -import { Transform } from '../util/Transform'; -import { UndoManager } from '../util/UndoManager'; -import { PresentationView } from './PresentationView'; -import { CollectionDockingView } from './collections/CollectionDockingView'; -import { ContextMenu } from './ContextMenu'; -import { DocumentDecorations } from './DocumentDecorations'; -import { InkingControl } from './InkingControl'; -import "./Main.scss"; -import { MainOverlayTextBox } from './MainOverlayTextBox'; -import { DocumentView } from './nodes/DocumentView'; -import { PreviewCursor } from './PreviewCursor'; -import { SearchBox } from './SearchBox'; -import { SelectionManager } from '../util/SelectionManager'; -import { FieldResult, Field, Doc, Opt } from '../../new_fields/Doc'; -import { Cast, FieldValue, StrCast } from '../../new_fields/Types'; -import { DocServer } from '../DocServer'; -import { listSpec } from '../../new_fields/Schema'; -import { Id } from '../../new_fields/RefField'; -import { HistoryUtil } from '../util/History'; - - -@observer -export class Main extends React.Component { - public static Instance: Main; - @observable private _workspacesShown: boolean = false; - @observable public pwidth: number = 0; - @observable public pheight: number = 0; - - @computed private get mainContainer(): Opt { - return FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc)); - } - private set mainContainer(doc: Opt) { - if (doc) { - if (!("presentationView" in doc)) { - doc.presentationView = new Doc(); - } - CurrentUserUtils.UserDocument.activeWorkspace = doc; - } - } - - constructor(props: Readonly<{}>) { - super(props); - Main.Instance = this; - // causes errors to be generated when modifying an observable outside of an action - configure({ enforceActions: "observed" }); - if (window.location.pathname !== RouteStore.home) { - let pathname = window.location.pathname.split("/"); - if (pathname.length > 1 && pathname[pathname.length - 2] === 'doc') { - CurrentUserUtils.MainDocId = pathname[pathname.length - 1]; - } - } - - library.add(faFont); - library.add(faImage); - library.add(faFilePdf); - library.add(faObjectGroup); - library.add(faTable); - library.add(faGlobeAsia); - library.add(faUndoAlt); - library.add(faRedoAlt); - library.add(faPenNib); - library.add(faFilm); - library.add(faMusic); - library.add(faTree); - this.initEventListeners(); - this.initAuthenticationRouters(); - } - - initEventListeners = () => { - // window.addEventListener("pointermove", (e) => this.reportLocation(e)) - window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler - window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler - window.addEventListener("keydown", (e) => { - if (e.key === "Escape") { - DragManager.AbortDrag(); - SelectionManager.DeselectAll(); - } - }, false); // drag event handler - // click interactions for the context menu - document.addEventListener("pointerdown", action(function (e: PointerEvent) { - if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) { - ContextMenu.Instance.clearItems(); - } - }), true); - } - - initAuthenticationRouters = async () => { - // Load the user's active workspace, or create a new one if initial session after signup - if (!CurrentUserUtils.MainDocId) { - const doc = await Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc); - if (doc) { - this.openWorkspace(doc); - } else { - this.createNewWorkspace(); - } - } else { - DocServer.GetRefField(CurrentUserUtils.MainDocId).then(field => - field instanceof Doc ? this.openWorkspace(field) : - this.createNewWorkspace(CurrentUserUtils.MainDocId)); - } - } - - @action - createNewWorkspace = async (id?: string) => { - const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc)); - if (list) { - let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: `WS collection ${list.length + 1}` }); - var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] }; - let mainDoc = Docs.DockDocument([CurrentUserUtils.UserDocument, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }); - list.push(mainDoc); - // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) - setTimeout(() => { - this.openWorkspace(mainDoc); - let pendingDocument = Docs.SchemaDocument(["title"], [], { title: "New Mobile Uploads" }); - mainDoc.optionalRightCollection = pendingDocument; - }, 0); - } - } - - @action - openWorkspace = async (doc: Doc, fromHistory = false) => { - CurrentUserUtils.MainDocId = doc[Id]; - this.mainContainer = doc; - fromHistory || HistoryUtil.pushState({ type: "doc", docId: doc[Id], initializers: {} }); - const col = await Cast(CurrentUserUtils.UserDocument.optionalRightCollection, Doc); - // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized) - setTimeout(async () => { - if (col) { - const l = Cast(col.data, listSpec(Doc)); - if (l && l.length > 0) { - CollectionDockingView.Instance.AddRightSplit(col); - } - } - }, 100); - } - @action - onResize = (r: any) => { - this.pwidth = r.offset.width; - this.pheight = r.offset.height; - } - getPWidth = () => { - return this.pwidth; - } - getPHeight = () => { - return this.pheight; - } - @computed - get mainContent() { - let mainCont = this.mainContainer; - let content = !mainCont ? (null) : - ; - const pres = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined; - return - {({ measureRef }) => -
- {content} - {pres ? : null} -
- } -
; - } - - /* 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 pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c27211_sample_explain.pdf"; - let weburl = "https://cs.brown.edu/courses/cs166/"; - let audiourl = "http://techslides.com/demos/samples/sample.mp3"; - let videourl = "http://techslides.com/demos/sample-videos/small.mp4"; - - let addTextNode = action(() => Docs.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note" })); - let addColNode = action(() => Docs.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" })); - let addSchemaNode = action(() => Docs.SchemaDocument(["title"], [], { width: 200, height: 200, title: "a schema collection" })); - let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" })); - // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy" })); - let addVideoNode = action(() => Docs.VideoDocument(videourl, { width: 200, title: "video node" })); - let addPDFNode = action(() => Docs.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" })); - let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })); - let addWebNode = action(() => Docs.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" })); - let addAudioNode = action(() => Docs.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" })); - - let btns: [React.RefObject, IconName, string, () => Doc][] = [ - [React.createRef(), "font", "Add Textbox", addTextNode], - [React.createRef(), "image", "Add Image", addImageNode], - [React.createRef(), "file-pdf", "Add PDF", addPDFNode], - [React.createRef(), "film", "Add Video", addVideoNode], - [React.createRef(), "music", "Add Audio", addAudioNode], - [React.createRef(), "globe-asia", "Add Web Clipping", addWebNode], - [React.createRef(), "object-group", "Add Collection", addColNode], - [React.createRef(), "tree", "Add Tree", addTreeNode], - [React.createRef(), "table", "Add Schema", addSchemaNode], - ]; - - return < div id="add-nodes-menu" > - - - -
-
    - {btns.map(btn => -
  • - -
  • )} -
-
-
; - } - - /* @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() { - let logoutRef = React.createRef(); - - return [ - , -
- - - -
, -
, -
-
- ]; - - } - - render() { - return ( -
- - {this.mainContent} - - - {this.nodesMenu()} - {this.miscButtons} - - -
- ); - } -} +import * as React from 'react'; (async () => { await Docs.initProtos(); await CurrentUserUtils.loadCurrentUser(); - ReactDOM.render(
, document.getElementById('root')); + ReactDOM.render(, document.getElementById('root')); })(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx new file mode 100644 index 000000000..d0d77bbf4 --- /dev/null +++ b/src/client/views/MainView.tsx @@ -0,0 +1,274 @@ +import { IconName, library } from '@fortawesome/fontawesome-svg-core'; +import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, computed, configure, observable, runInAction, trace } from 'mobx'; +import { observer } from 'mobx-react'; +import "normalize.css"; +import * as React from 'react'; +import Measure from 'react-measure'; +import * as request from 'request'; +import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; +import { RouteStore } from '../../server/RouteStore'; +import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils'; +import { Docs } from '../documents/Documents'; +import { SetupDrag, DragManager } from '../util/DragManager'; +import { Transform } from '../util/Transform'; +import { UndoManager } from '../util/UndoManager'; +import { PresentationView } from './PresentationView'; +import { CollectionDockingView } from './collections/CollectionDockingView'; +import { ContextMenu } from './ContextMenu'; +import { DocumentDecorations } from './DocumentDecorations'; +import { InkingControl } from './InkingControl'; +import "./Main.scss"; +import { MainOverlayTextBox } from './MainOverlayTextBox'; +import { DocumentView } from './nodes/DocumentView'; +import { PreviewCursor } from './PreviewCursor'; +import { SearchBox } from './SearchBox'; +import { SelectionManager } from '../util/SelectionManager'; +import { FieldResult, Field, Doc, Opt } from '../../new_fields/Doc'; +import { Cast, FieldValue, StrCast } from '../../new_fields/Types'; +import { DocServer } from '../DocServer'; +import { listSpec } from '../../new_fields/Schema'; +import { Id } from '../../new_fields/RefField'; +import { HistoryUtil } from '../util/History'; + + +@observer +export class MainView extends React.Component { + public static Instance: MainView; + @observable private _workspacesShown: boolean = false; + @observable public pwidth: number = 0; + @observable public pheight: number = 0; + + @computed private get mainContainer(): Opt { + return FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc)); + } + private set mainContainer(doc: Opt) { + if (doc) { + if (!("presentationView" in doc)) { + doc.presentationView = new Doc(); + } + CurrentUserUtils.UserDocument.activeWorkspace = doc; + } + } + + constructor(props: Readonly<{}>) { + super(props); + MainView.Instance = this; + // causes errors to be generated when modifying an observable outside of an action + configure({ enforceActions: "observed" }); + if (window.location.pathname !== RouteStore.home) { + let pathname = window.location.pathname.split("/"); + if (pathname.length > 1 && pathname[pathname.length - 2] === 'doc') { + CurrentUserUtils.MainDocId = pathname[pathname.length - 1]; + } + } + + library.add(faFont); + library.add(faImage); + library.add(faFilePdf); + library.add(faObjectGroup); + library.add(faTable); + library.add(faGlobeAsia); + library.add(faUndoAlt); + library.add(faRedoAlt); + library.add(faPenNib); + library.add(faFilm); + library.add(faMusic); + library.add(faTree); + this.initEventListeners(); + this.initAuthenticationRouters(); + } + + initEventListeners = () => { + // window.addEventListener("pointermove", (e) => this.reportLocation(e)) + window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler + window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler + window.addEventListener("keydown", (e) => { + if (e.key === "Escape") { + DragManager.AbortDrag(); + SelectionManager.DeselectAll(); + } + }, false); // drag event handler + // click interactions for the context menu + document.addEventListener("pointerdown", action(function (e: PointerEvent) { + if (!ContextMenu.Instance.intersects(e.pageX, e.pageY)) { + ContextMenu.Instance.clearItems(); + } + }), true); + } + + initAuthenticationRouters = async () => { + // Load the user's active workspace, or create a new one if initial session after signup + if (!CurrentUserUtils.MainDocId) { + const doc = await Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc); + if (doc) { + this.openWorkspace(doc); + } else { + this.createNewWorkspace(); + } + } else { + DocServer.GetRefField(CurrentUserUtils.MainDocId).then(field => + field instanceof Doc ? this.openWorkspace(field) : + this.createNewWorkspace(CurrentUserUtils.MainDocId)); + } + } + + @action + createNewWorkspace = async (id?: string) => { + const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc)); + if (list) { + let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: `WS collection ${list.length + 1}` }); + var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] }; + let mainDoc = Docs.DockDocument([CurrentUserUtils.UserDocument, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }); + list.push(mainDoc); + // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) + setTimeout(() => { + this.openWorkspace(mainDoc); + let pendingDocument = Docs.SchemaDocument(["title"], [], { title: "New Mobile Uploads" }); + mainDoc.optionalRightCollection = pendingDocument; + }, 0); + } + } + + @action + openWorkspace = async (doc: Doc, fromHistory = false) => { + CurrentUserUtils.MainDocId = doc[Id]; + this.mainContainer = doc; + fromHistory || HistoryUtil.pushState({ type: "doc", docId: doc[Id], initializers: {} }); + const col = await Cast(CurrentUserUtils.UserDocument.optionalRightCollection, Doc); + // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized) + setTimeout(async () => { + if (col) { + const l = Cast(col.data, listSpec(Doc)); + if (l && l.length > 0) { + CollectionDockingView.Instance.AddRightSplit(col); + } + } + }, 100); + } + @action + onResize = (r: any) => { + this.pwidth = r.offset.width; + this.pheight = r.offset.height; + } + getPWidth = () => { + return this.pwidth; + } + getPHeight = () => { + return this.pheight; + } + @computed + get mainContent() { + let mainCont = this.mainContainer; + let content = !mainCont ? (null) : + ; + const pres = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined; + return + {({ measureRef }) => +
+ {content} + {pres ? : null} +
+ } +
; + } + + /* 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 pdfurl = "http://www.adobe.com/support/products/enterprise/knowledgecenter/media/c27211_sample_explain.pdf"; + let weburl = "https://cs.brown.edu/courses/cs166/"; + let audiourl = "http://techslides.com/demos/samples/sample.mp3"; + let videourl = "http://techslides.com/demos/sample-videos/small.mp4"; + + let addTextNode = action(() => Docs.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note" })); + let addColNode = action(() => Docs.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" })); + let addSchemaNode = action(() => Docs.SchemaDocument(["title"], [], { width: 200, height: 200, title: "a schema collection" })); + let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" })); + // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy" })); + let addVideoNode = action(() => Docs.VideoDocument(videourl, { width: 200, title: "video node" })); + let addPDFNode = action(() => Docs.PdfDocument(pdfurl, { width: 200, height: 200, title: "a pdf doc" })); + let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })); + let addWebNode = action(() => Docs.WebDocument(weburl, { width: 200, height: 200, title: "a sample web page" })); + let addAudioNode = action(() => Docs.AudioDocument(audiourl, { width: 200, height: 200, title: "audio node" })); + + let btns: [React.RefObject, IconName, string, () => Doc][] = [ + [React.createRef(), "font", "Add Textbox", addTextNode], + [React.createRef(), "image", "Add Image", addImageNode], + [React.createRef(), "file-pdf", "Add PDF", addPDFNode], + [React.createRef(), "film", "Add Video", addVideoNode], + [React.createRef(), "music", "Add Audio", addAudioNode], + [React.createRef(), "globe-asia", "Add Web Clipping", addWebNode], + [React.createRef(), "object-group", "Add Collection", addColNode], + [React.createRef(), "tree", "Add Tree", addTreeNode], + [React.createRef(), "table", "Add Schema", addSchemaNode], + ]; + + return < div id="add-nodes-menu" > + + + +
+
    + {btns.map(btn => +
  • + +
  • )} +
+
+
; + } + + /* @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() { + let logoutRef = React.createRef(); + + return [ + , +
+ + + +
, +
, +
+
+ ]; + + } + + render() { + return ( +
+ + {this.mainContent} + + + {this.nodesMenu()} + {this.miscButtons} + + +
+ ); + } +} diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index 7d0dc2913..9c37e9000 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -2,7 +2,6 @@ import { observer } from "mobx-react"; import React = require("react"); import { observable, action, runInAction, reaction } from "mobx"; import "./PresentationView.scss"; -import "./Main.tsx"; import { DocumentManager } from "../util/DocumentManager"; import { Utils } from "../../Utils"; import { Doc, DocListCast, DocListCastAsync } from "../../new_fields/Doc"; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 35a9f34cf..4cc4ae6b6 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -14,12 +14,12 @@ import { Doc, DocListCast } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/RefField'; import { ContextMenu } from '../ContextMenu'; import { undoBatch } from '../../util/UndoManager'; -import { Main } from '../Main'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; import { CollectionDockingView } from './CollectionDockingView'; import { DocumentManager } from '../../util/DocumentManager'; import { List } from '../../../new_fields/List'; import { Docs } from '../../documents/Documents'; +import { MainView } from '../MainView'; export interface TreeViewProps { @@ -52,7 +52,7 @@ class TreeView extends React.Component { @undoBatch openRight = async () => { if (this.props.document.dockingConfig) { - Main.Instance.openWorkspace(this.props.document); + MainView.Instance.openWorkspace(this.props.document); } else { CollectionDockingView.Instance.AddRightSplit(this.props.document); } @@ -140,7 +140,7 @@ class TreeView extends React.Component { onWorkspaceContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => Main.Instance.openWorkspace(this.props.document)) }); + ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.props.document)) }); ContextMenu.Instance.addItem({ description: "Open Right", event: () => CollectionDockingView.Instance.AddRightSplit(this.props.document) }); ContextMenu.Instance.addItem({ description: "Open Fields", event: () => CollectionDockingView.Instance.AddRightSplit(Docs.KVPDocument(this.props.document, @@ -215,7 +215,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { } onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => Main.Instance.createNewWorkspace()) }); + ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => MainView.Instance.createNewWorkspace()) }); } if (!ContextMenu.Instance.getItems().some(item => item.description === "Delete")) { ContextMenu.Instance.addItem({ description: "Delete", event: undoBatch(() => this.remove(this.props.Document)) }); diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx index 4cac09dee..d9b07aac6 100644 --- a/src/debug/Viewer.tsx +++ b/src/debug/Viewer.tsx @@ -3,184 +3,186 @@ import "normalize.css"; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { observer } from 'mobx-react'; - -// configure({ -// enforceActions: "observed" -// }); - -// @observer -// class FieldViewer extends React.Component<{ field: BasicField }> { -// render() { -// return {JSON.stringify(this.props.field.Data)} ({this.props.field.Id}); -// } -// } - -// @observer -// class KeyViewer extends React.Component<{ field: Key }> { -// render() { -// return this.props.field.Name; -// } -// } - -// @observer -// class ListViewer extends React.Component<{ field: ListField }>{ -// @observable -// expanded = false; - -// render() { -// let content; -// if (this.expanded) { -// content = ( -//
-// {this.props.field.Data.map(field => )} -//
-// ); -// } else { -// content = <>[...] ({this.props.field.Id}); -// } -// return ( -//
-// -// {content} -//
-// ); -// } -// } - -// @observer -// class DocumentViewer extends React.Component<{ field: Document }> { -// private keyMap: ObservableMap = new ObservableMap; - -// private disposer?: Lambda; - -// componentDidMount() { -// let f = () => { -// Array.from(this.props.field._proxies.keys()).forEach(id => { -// if (!this.keyMap.has(id)) { -// Server.GetField(id, (field) => { -// if (field && field instanceof Key) { -// this.keyMap.set(id, field); -// } -// }); -// } -// }); -// }; -// this.disposer = this.props.field._proxies.observe(f); -// f(); -// } - -// componentWillUnmount() { -// if (this.disposer) { -// this.disposer(); -// } -// } - -// render() { -// let fields = Array.from(this.props.field._proxies.entries()).map(kv => { -// let key = this.keyMap.get(kv[0]); -// return ( -//
-// ({key ? key.Name : kv[0]}): -// -//
-// ); -// }); -// return ( -//
-// Document ({this.props.field.Id}) -//
-// {fields} -//
-//
-// ); -// } -// } - -// @observer -// class DebugViewer extends React.Component<{ fieldId: string }> { -// @observable -// private field?: Field; - -// @observable -// private error?: string; - -// constructor(props: { fieldId: string }) { -// super(props); -// this.update(); -// } - -// update() { -// Server.GetField(this.props.fieldId, action((field: Opt) => { -// this.field = field; -// if (!field) { -// this.error = `Field with id ${this.props.fieldId} not found`; -// } -// })); - -// } - -// render() { -// let content; -// if (this.field) { -// // content = this.field.ToJson(); -// if (this.field instanceof ListField) { -// content = (); -// } else if (this.field instanceof Document) { -// content = (); -// } else if (this.field instanceof BasicField) { -// content = (); -// } else if (this.field instanceof Key) { -// content = (); -// } else { -// content = (Unrecognized field type); -// } -// } else if (this.error) { -// content = Field {this.props.fieldId} not found ; -// } else { -// content = Field loading: {this.props.fieldId}; -// } -// return content; -// } -// } - -// @observer -// class Viewer extends React.Component { -// @observable -// private idToAdd: string = ''; - -// @observable -// private ids: string[] = []; - -// @action -// inputOnChange = (e: React.ChangeEvent) => { -// this.idToAdd = e.target.value; -// } - -// @action -// onKeyPress = (e: React.KeyboardEvent) => { -// if (e.key === "Enter") { -// this.ids.push(this.idToAdd); -// this.idToAdd = ""; -// } -// } - -// render() { -// return ( -// <> -// -//
-// {this.ids.map(id => )} -//
-// -// ); -// } -// } - -// ReactDOM.render(( -//
-// -//
), -// document.getElementById('root') -// ); \ No newline at end of file +import { CurrentUserUtils } from '../server/authentication/models/current_user_utils'; +import { RouteStore } from '../server/RouteStore'; +import { emptyFunction } from '../Utils'; +import { Docs } from '../client/documents/Documents'; +import { SetupDrag } from '../client/util/DragManager'; +import { Transform } from '../client/util/Transform'; +import { UndoManager } from '../client/util/UndoManager'; +import { PresentationView } from '../client/views/PresentationView'; +import { CollectionDockingView } from '../client/views/collections/CollectionDockingView'; +import { ContextMenu } from '../client/views/ContextMenu'; +import { DocumentDecorations } from '../client/views/DocumentDecorations'; +import { InkingControl } from '../client/views/InkingControl'; +import { MainOverlayTextBox } from '../client/views/MainOverlayTextBox'; +import { DocumentView } from '../client/views/nodes/DocumentView'; +import { PreviewCursor } from '../client/views/PreviewCursor'; +import { SearchBox } from '../client/views/SearchBox'; +import { SelectionManager } from '../client/util/SelectionManager'; +import { Doc, Field, FieldResult } from '../new_fields/Doc'; +import { Cast } from '../new_fields/Types'; +import { DocServer } from '../client/DocServer'; +import { listSpec } from '../new_fields/Schema'; +import { Id } from '../new_fields/RefField'; +import { HistoryUtil } from '../client/util/History'; +import { List } from '../new_fields/List'; +import { URLField } from '../new_fields/URLField'; + +CurrentUserUtils; +RouteStore; +emptyFunction; +Docs; +SetupDrag; +Transform; +UndoManager; +PresentationView; +CollectionDockingView; +ContextMenu; +DocumentDecorations; +InkingControl; +MainOverlayTextBox; +DocumentView; +PreviewCursor; +SearchBox; +SelectionManager; +Doc; +Cast; +DocServer; +listSpec; +Id; +HistoryUtil; + +configure({ + enforceActions: "observed" +}); + +@observer +class ListViewer extends React.Component<{ field: List }>{ + @observable + expanded = false; + + render() { + let content; + if (this.expanded) { + content = ( +
+ {this.props.field.map((field, index) => )} +
+ ); + } else { + content = <>[...]; + } + return ( +
+ + {content} +
+ ); + } +} + +@observer +class DocumentViewer extends React.Component<{ field: Doc }> { + @observable + expanded = false; + render() { + let content; + if (this.expanded) { + const keys = Object.keys(this.props.field); + let fields = keys.map(key => { + return ( +
+ ({key}): + +
+ ); + }); + content = ( +
+ Document ({this.props.field[Id]}) +
+ {fields} +
+
+ ); + } else { + content = <>[...] ({this.props.field[Id]}); + } + return ( +
+ + {content} +
+ ); + } +} + +@observer +class DebugViewer extends React.Component<{ field: FieldResult }> { + + render() { + let content; + const field = this.props.field; + if (field instanceof List) { + content = (); + } else if (field instanceof Doc) { + content = (); + } else if (typeof field === "string") { + content =

"{field}"

; + } else if (typeof field === "number" || typeof field === "boolean") { + content =

{field}

; + } else if (field instanceof URLField) { + content =

{field.url.href}

; + } else { + content =

Unrecognized field type

; + } + return content; + } +} + +@observer +class Viewer extends React.Component { + @observable + private idToAdd: string = ''; + + @observable + private fields: Field[] = []; + + @action + inputOnChange = (e: React.ChangeEvent) => { + this.idToAdd = e.target.value; + } + + @action + onKeyPress = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + DocServer.GetRefField(this.idToAdd).then(action((field: any) => { + if (field !== undefined) { + this.fields.push(field); + } + })); + this.idToAdd = ""; + } + } + + render() { + return ( + <> + +
+ {this.fields.map((field, index) => )} +
+ + ); + } +} + +ReactDOM.render(( +
+ +
), + document.getElementById('root') +); \ No newline at end of file -- cgit v1.2.3-70-g09d2