diff options
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 83 | ||||
-rw-r--r-- | src/client/views/GlobalKeyHandler.ts | 103 | ||||
-rw-r--r-- | src/client/views/MainView.scss | 60 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 326 | ||||
-rw-r--r-- | src/client/views/OverlayView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.scss | 41 |
6 files changed, 236 insertions, 383 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b9dd18a36..1b0747e9c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1,33 +1,35 @@ -import { computed, observable, reaction, action } from "mobx"; +import { computed, observable, reaction } from "mobx"; import * as rp from 'request-promise'; -import { Utils } from "../../Utils"; -import { DocServer } from "../DocServer"; -import { Docs, DocumentOptions, DocUtils } from "../documents/Documents"; -import { UndoManager } from "./UndoManager"; -import { Doc, DocListCast, DocListCastAsync, DataSym } from "../../fields/Doc"; +import { DataSym, Doc, DocListCast, DocListCastAsync } from "../../fields/Doc"; +import { Id } from "../../fields/FieldSymbols"; import { List } from "../../fields/List"; +import { PrefetchProxy } from "../../fields/Proxy"; +import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; -import { ScriptField, ComputedField } from "../../fields/ScriptField"; -import { Cast, PromiseValue, StrCast, NumCast, BoolCast } from "../../fields/Types"; +import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; +import { ComputedField, ScriptField } from "../../fields/ScriptField"; +import { BoolCast, Cast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; import { nullAudio } from "../../fields/URLField"; -import { DragManager } from "./DragManager"; -import { Scripting } from "./Scripting"; -import { CollectionViewType, CollectionView } from "../views/collections/CollectionView"; -import { makeTemplate } from "./DropConverter"; -import { RichTextField } from "../../fields/RichTextField"; -import { PrefetchProxy } from "../../fields/Proxy"; -import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; -import { MainView } from "../views/MainView"; +import { Utils } from "../../Utils"; +import { DocServer } from "../DocServer"; +import { Docs, DocumentOptions, DocUtils } from "../documents/Documents"; import { DocumentType } from "../documents/DocumentTypes"; -import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; +import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; +import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; +import { MainView } from "../views/MainView"; +import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; import { LabelBox } from "../views/nodes/LabelBox"; -import { LinkManager } from "./LinkManager"; -import { Id } from "../../fields/FieldSymbols"; +import { OverlayView } from "../views/OverlayView"; +import { DocumentManager } from "./DocumentManager"; +import { DragManager } from "./DragManager"; +import { makeTemplate } from "./DropConverter"; import { HistoryUtil } from "./History"; -import { CollectionDockingView } from "../views/collections/CollectionDockingView"; +import { LinkManager } from "./LinkManager"; +import { Scripting } from "./Scripting"; +import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; -import { DocumentManager } from "./DocumentManager"; +import { UndoManager } from "./UndoManager"; export class CurrentUserUtils { private static curr_id: string; @@ -956,6 +958,7 @@ export class CurrentUserUtils { return doc; } + public static async loadCurrentUser() { return rp.get(Utils.prepend("/getCurrentUser")).then(response => { if (response) { @@ -1018,6 +1021,41 @@ export class CurrentUserUtils { return true; } + public static importDocument = () => { + const input = document.createElement("input"); + input.type = "file"; + input.multiple = true; + input.accept = ".zip, application/pdf, video/*, image/*, audio/*"; + input.onchange = async _e => { + const upload = Utils.prepend("/uploadDoc"); + const formData = new FormData(); + const file = input.files && input.files[0]; + if (file && file.type === 'application/zip') { + formData.append('file', file); + formData.append('remap', "true"); + const response = await fetch(upload, { method: "POST", body: formData }); + const json = await response.json(); + if (json !== "error") { + const doc = await DocServer.GetRefField(json); + if (doc instanceof Doc) { + setTimeout(() => SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => + docs.docs.forEach(d => LinkManager.Instance.addLink(d))), 2000); // need to give solr some time to update so that this query will find any link docs we've added. + } + } + } else if (input.files && input.files.length !== 0) { + const importDocs = Cast(Doc.UserDoc().myImportDocs, Doc, null); + const disposer = OverlayView.ShowSpinner(); + DocListCastAsync(importDocs.data).then(async list => { + list?.push(... await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {})); + disposer(); + }); + } else { + console.log("No file selected"); + } + }; + input.click(); + } + public static snapshotDashboard = (userDoc: Doc) => { const copy = CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy); @@ -1053,6 +1091,7 @@ export class CurrentUserUtils { CurrentUserUtils.openDashboard(userDoc, dashboardDoc); } + public static get MySearchPanelDoc() { return Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null); } public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); } public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); } public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); } @@ -1078,3 +1117,5 @@ Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Insta "returns all the links to the document or its annotations", "(doc: any)"); Scripting.addGlobal(function directLinks(doc: any) { return new List(LinkManager.Instance.getAllDirectLinks(doc)); }, "returns all the links directly to the document", "(doc: any)"); +Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); }, + "imports files from device directly into the import sidebar"); diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index db3e3d0dd..40353b1b7 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -24,6 +24,8 @@ import { MainView } from "./MainView"; import { DocumentLinksButton } from "./nodes/DocumentLinksButton"; import { DocumentView } from "./nodes/DocumentView"; import { PDFMenu } from "./pdf/PDFMenu"; +import { SnappingManager } from "../util/SnappingManager"; +import { SearchBox } from "./search/SearchBox"; const modifiers = ["control", "meta", "shift", "alt"]; type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>; @@ -82,28 +84,18 @@ export class KeyManager { // MarqueeView.DragMarquee = !MarqueeView.DragMarquee; // bcz: this needs a better disclosure UI break; case "escape": - // if (DocumentLinksButton.StartLink) { - // if (DocumentLinksButton.StartLink.Document) { - // action((e: React.PointerEvent<HTMLDivElement>) => { - // Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc); - // }); - // } - // } DocumentLinksButton.StartLink = undefined; DocumentLinksButton.StartLinkView = undefined; FormatShapePane.Instance._controlBtn = false; - const main = MainView.Instance; Doc.SetSelectedTool(InkTool.None); var doDeselect = true; - if (main.isPointerDown) { + if (SnappingManager.GetIsDragging()) { DragManager.AbortDrag(); + } else if (CollectionDockingView.Instance.HasFullScreen) { + CollectionDockingView.Instance.CloseFullScreen(); } else { - if (CollectionDockingView.Instance.HasFullScreen) { - CollectionDockingView.Instance.CloseFullScreen(); - } else { - doDeselect = !ContextMenu.Instance.closeMenu(); - } + doDeselect = !ContextMenu.Instance.closeMenu(); } doDeselect && SelectionManager.DeselectAll(); DictationManager.Controls.stop(); @@ -116,28 +108,18 @@ export class KeyManager { break; case "delete": case "backspace": - if (document.activeElement) { - if (document.activeElement.tagName === "INPUT" || document.activeElement.tagName === "TEXTAREA") { - return { stopPropagation: false, preventDefault: false }; - } + if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") { + return { stopPropagation: false, preventDefault: false }; } const selected = SelectionManager.SelectedDocuments().slice(); UndoManager.RunInBatch(() => selected.map(dv => dv.props.removeDocument?.(dv.props.Document)), "delete"); SelectionManager.DeselectAll(); break; - case "arrowleft": - UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(-1, 0)), "nudge left"); - break; - case "arrowright": - UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(1, 0)), "nudge right"); - break; - case "arrowup": - UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, -1)), "nudge up"); - break; - case "arrowdown": - UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, 1)), "nudge down"); - break; + case "arrowleft": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(-1, 0)), "nudge left"); break; + case "arrowright": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(1, 0)), "nudge right"); break; + case "arrowup": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, -1)), "nudge up"); break; + case "arrowdown": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, 1)), "nudge down"); break; } return { @@ -151,22 +133,10 @@ export class KeyManager { const preventDefault = false; switch (keyname) { - // case "~": - // DictationManager.Controls.listen({ useOverlay: true, tryExecute: true }); - // stopPropagation = true; - // preventDefault = true; - case "arrowleft": - UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(-10, 0)), "nudge left"); - break; - case "arrowright": - UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(10, 0)), "nudge right"); - break; - case "arrowup": - UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, -10)), "nudge up"); - break; - case "arrowdown": - UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, 10)), "nudge down"); - break; + case "arrowleft": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(-10, 0)), "nudge left"); break; + case "arrowright": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(10, 0)), "nudge right"); break; + case "arrowup": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, -10)), "nudge up"); break; + case "arrowdown": UndoManager.RunInBatch(() => SelectionManager.SelectedDocuments().map(dv => dv.props.nudge?.(0, 10)), "nudge down"); break; } return { @@ -205,51 +175,30 @@ export class KeyManager { switch (keyname) { case "arrowright": - if (document.activeElement) { - if (document.activeElement.tagName === "INPUT" || document.activeElement.tagName === "TEXTAREA") { - return { stopPropagation: false, preventDefault: false }; - } + if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") { + return { stopPropagation: false, preventDefault: false }; } MainView.Instance.mainFreeform && CollectionDockingView.AddSplit(MainView.Instance.mainFreeform, "right"); break; case "arrowleft": - if (document.activeElement) { - if (document.activeElement.tagName === "INPUT" || document.activeElement.tagName === "TEXTAREA") { - return { stopPropagation: false, preventDefault: false }; - } + if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") { + return { stopPropagation: false, preventDefault: false }; } MainView.Instance.mainFreeform && CollectionDockingView.CloseSplit(MainView.Instance.mainFreeform); break; case "backspace": - if (document.activeElement) { - if (document.activeElement.tagName === "INPUT" || document.activeElement.tagName === "TEXTAREA") { - return { stopPropagation: false, preventDefault: false }; - } + if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") { + return { stopPropagation: false, preventDefault: false }; } break; case "t": PromiseValue(Cast(Doc.UserDoc()["tabs-button-tools"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); - if (MainView.Instance.flyoutWidth === 240) { - MainView.Instance.flyoutWidth = 0; - } else { - MainView.Instance.flyoutWidth = 240; - } - break; - case "l": - PromiseValue(Cast(Doc.UserDoc()["tabs-button-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 = 250; - } + MainView.Instance.flyoutWidth = MainView.Instance.flyoutWidth === 240 ? 0 : 240; break; case "f": - PromiseValue(Cast(Doc.UserDoc()["tabs-button-search"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); - if (MainView.Instance.flyoutWidth === 400) { - MainView.Instance.flyoutWidth = 0; - } else { - MainView.Instance.flyoutWidth = 400; - } + SearchBox.Instance._searchFullDB = "My Stuff"; + SearchBox.Instance.newsearchstring = ""; + SearchBox.Instance.enter(undefined); break; case "o": const target = SelectionManager.SelectedDocuments()[0]; diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 05a8b5c80..6cec5995c 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -110,7 +110,7 @@ height: 55px; width: 17px; position: absolute; - top: 55%; + top: 50%; border: 1px black solid; border-radius: 0; border-top-left-radius: 10px; @@ -143,15 +143,20 @@ cursor: auto; } -.mainView-flyoutContainer { - display: flex; - flex-direction: column; +.mainView-innerContent { + display: contents; + flex-direction: row; position: relative; - height: 100%; - background: dimgray; + .mainView-flyoutContainer { + display: flex; + flex-direction: column; + position: relative; + height: 100%; + background: dimgray; - .documentView-node-topmost { - background: lightgrey; + .documentView-node-topmost { + background: lightgrey; + } } } @@ -265,6 +270,7 @@ transform: none !important; } +.mainView-libraryFlyout-out, .mainView-libraryFlyout { height: 100%; width: 100%; @@ -273,6 +279,13 @@ flex-direction: column; z-index: 2; + .mainView-contentArea { + position: relative; + height: 100%; + width: 100%; + overflow: visible; + } + .mainView-libraryFlyout-close { right: 6; top: 5; @@ -282,6 +295,10 @@ margin-bottom: 10; } } +.mainView-libraryFlyout-out { + transition: width .5s; + box-shadow: rgb(156, 147, 150) 0.2vw 0.2vw 0.2vw; +} .mainView-expandFlyoutButton { position: absolute; @@ -303,6 +320,16 @@ background-color: lightgrey; cursor: grab; + .mainView-libraryDragger { + width: 100%; + position: absolute; + } + .mainView-libraryDragger-out { + width: 3vw; + position: fixed; + top: 0; + } + .mainView-libraryHandle-icon { width: 10px; height: 10px; @@ -329,4 +356,21 @@ height: 25%; position: relative; display: flex; +} + +.mainVew-webRef { + position: absolute; + left: -1000; + top: -1000; + display: block; + width: 200px; + height: 800px; +} +.mainVew-invisibleWebRef { + position: absolute; + left: 50; + top: 50; + display: block; + width: 500px; + height: 1000px; }
\ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 338d9544e..52c598d7d 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -2,34 +2,28 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons'; import * as fa from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; +import { action, computed, configure, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import Measure from 'react-measure'; -import * as rp from 'request-promise'; -import { Doc, DocListCast, Field, Opt, DocListCastAsync } from '../../fields/Doc'; +import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { List } from '../../fields/List'; import { PrefetchProxy } from '../../fields/Proxy'; -import { listSpec } from '../../fields/Schema'; -import { BoolCast, Cast, FieldValue, StrCast, PromiseValue } from '../../fields/Types'; +import { BoolCast, PromiseValue, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; -import { Docs, DocUtils } from '../documents/Documents'; +import { Docs } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; -import { Networking } from '../Network'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; import { DocumentManager } from '../util/DocumentManager'; import { GroupManager } from '../util/GroupManager'; import { HistoryUtil } from '../util/History'; import { Hypothesis } from '../util/HypothesisUtils'; -import { LinkManager } from '../util/LinkManager'; import { Scripting } from '../util/Scripting'; -import { SearchUtil } from '../util/SearchUtil'; -import { SelectionManager } from '../util/SelectionManager'; import { SettingsManager } from '../util/SettingsManager'; import { SharingManager } from '../util/SharingManager'; import { SnappingManager } from '../util/SnappingManager'; @@ -63,8 +57,6 @@ import { PDFMenu } from './pdf/PDFMenu'; import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; import { SearchBox } from './search/SearchBox'; -import { CollectionSubView, CollectionSubViewLoader } from './collections/CollectionSubView'; -import ReactLoading from 'react-loading'; @observer export class MainView extends React.Component { @@ -72,49 +64,29 @@ export class MainView extends React.Component { private _buttonBarHeight = 36; private _docBtnRef = React.createRef<HTMLDivElement>(); private _mainViewRef = React.createRef<HTMLDivElement>(); + private _lastButton: Doc | undefined; @observable private _panelWidth: number = 0; @observable private _panelHeight: number = 0; @observable private _flyoutTranslate: boolean = false; + @observable private _sidebarContent: any = this.userDoc?.sidebar; + @observable private _panelContent: string = "none"; @observable public flyoutWidth: number = 0; - private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); } + @computed private get topOffset() { return (CollectionMenu.Instance?.Pinned ? 35 : 0) + Number(SEARCH_PANEL_HEIGHT.replace("px", "")); } @computed private get userDoc() { return Doc.UserDoc(); } + @computed private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); } @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; } @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } - @computed public get searchDoc() { return Cast(this.userDoc.mySearchPanelDoc, Doc) as Doc; } - - @observable public sidebarContent: any = this.userDoc?.sidebar; - @observable public panelContent: string = "none"; - @observable public showProperties: boolean = false; - public isPointerDown = false; - @computed get selectedDocumentView() { - if (SelectionManager.SelectedDocuments().length) { - return SelectionManager.SelectedDocuments()[0]; - } else { return undefined; } - } propertiesWidth = () => Math.max(0, Math.min(this._panelWidth - 50, CurrentUserUtils.propertiesWidth || 0)); - @computed get propertiesIcon() { - if (this.propertiesWidth() < 10) { - return "chevron-left"; - } else { - return "chevron-right"; - } - } - @observable propertiesDownX: number | undefined; - componentDidMount() { DocServer.setPlaygroundFields(["dataTransition", "_viewTransition", "_panX", "_panY", "_viewScale", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's - const tag = document.createElement('script'); - - const proto = DocServer.GetRefField("rtfProto").then(proto => { - (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), - msg => msg && alert(msg)); - }); + DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg))); + const tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; const firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag); @@ -123,11 +95,7 @@ export class MainView extends React.Component { window.addEventListener("paste", KeyManager.Instance.paste as any); document.addEventListener("dash", (e: any) => { // event used by chrome plugin to tell Dash which document to focus on const id = FormattedTextBox.GetDocFromUrl(e.detail); - DocServer.GetRefField(id).then(doc => { - if (doc instanceof Doc) { - DocumentManager.Instance.jumpToDocument(doc, false, undefined); - } - }); + DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined) : (null)); }); document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener); } @@ -135,7 +103,6 @@ export class MainView extends React.Component { componentWillUnMount() { window.removeEventListener("keydown", KeyManager.Instance.handle); window.removeEventListener("pointerdown", this.globalPointerDown); - window.removeEventListener("pointerup", this.globalPointerUp); window.removeEventListener("paste", KeyManager.Instance.paste as any); document.removeEventListener("linkAnnotationToDash", Hypothesis.linkListener); } @@ -143,21 +110,19 @@ export class MainView extends React.Component { constructor(props: Readonly<{}>) { super(props); MainView.Instance = this; - this.sidebarContent.proto = undefined; + this._sidebarContent.proto = undefined; CurrentUserUtils._urlState = HistoryUtil.parseUrl(window.location) || {} as any; - // causes errors to be generated when modifying an observable outside of an action + // causes errors to be generated when modifying an observable outside of an action configure({ enforceActions: "observed" }); + if (window.location.pathname !== "/home") { const pathname = window.location.pathname.substr(1).split("/"); if (pathname.length > 1) { - const type = pathname[0]; - if (type === "doc") { + if (pathname[0] === "doc") { CurrentUserUtils.MainDocId = pathname[1]; if (!this.userDoc) { - runInAction(() => this.closeFlyout()); - DocServer.GetRefField(CurrentUserUtils.MainDocId).then(action((field: Opt<Field>) => - field instanceof Doc && (CurrentUserUtils.GuestTarget = field))); + DocServer.GetRefField(CurrentUserUtils.MainDocId).then(action(field => field instanceof Doc && (CurrentUserUtils.GuestTarget = field))); } } } @@ -189,49 +154,37 @@ export class MainView extends React.Component { } globalPointerDown = action((e: PointerEvent) => { - this.isPointerDown = true; AudioBox.Enabled = true; const targets = document.elementsFromPoint(e.x, e.y); - if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) { - ContextMenu.Instance.closeMenu(); - } - if (targets && (targets.length && targets[0].className.toString() !== "timeline-menu-desc" && targets[0].className.toString() !== "timeline-menu-item" && targets[0].className.toString() !== "timeline-menu-input")) { - TimelineMenu.Instance.closeMenu(); - } - if (targets && targets.length && SearchBox.Instance._searchbarOpen) { - let check = false; - const icon = "icon"; - targets.forEach((thing) => { - if (thing.className.toString() === "collectionSchemaView-searchContainer" || (thing as any)?.dataset[icon] === "filter" || thing.className.toString() === "collectionSchema-header-menuOptions" || thing.className.toString() === "altcollectionTimeView-treeView") { - check = true; - } - }); - if (check === false) { - SearchBox.Instance.resetSearch(true); + if (targets.length) { + if (targets[0].className.toString().indexOf("contextMenu") === -1) { + ContextMenu.Instance.closeMenu(); + } + if (targets[0].className.toString() !== "timeline-menu-desc" && targets[0].className.toString() !== "timeline-menu-item" && targets[0].className.toString() !== "timeline-menu-input") { + TimelineMenu.Instance.closeMenu(); + } + if (SearchBox.Instance._searchbarOpen) { + const check = targets.some((thing) => + (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset["icon"] === "filter" || + thing.className === "collectionSchema-header-menuOptions")); + !check && SearchBox.Instance.resetSearch(true); } } - }); - globalPointerUp = () => this.isPointerDown = false; - initEventListeners = () => { window.addEventListener("drop", (e) => { e.preventDefault(); }, false); // drop event handler window.addEventListener("dragover", (e) => { e.preventDefault(); }, false); // drag event handler // click interactions for the context menu document.addEventListener("pointerdown", this.globalPointerDown); - document.addEventListener("pointerup", this.globalPointerUp); } initAuthenticationRouters = async () => { // Load the user's active dashboard, or create a new one if initial session after signup const received = CurrentUserUtils.MainDocId; if (received && !this.userDoc) { - reaction(() => CurrentUserUtils.GuestTarget, - target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), - { fireImmediately: true } - ); + reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true }); } else { if (received && CurrentUserUtils._urlState.sharing) { reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized, @@ -252,8 +205,7 @@ export class MainView extends React.Component { @action createNewPresentation = async () => { - await this.userDoc.myPresentations; - if (this.userDoc.myPresentations === undefined) { + if (!await this.userDoc.myPresentations) { this.userDoc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "PRESENTATION TRAILS", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true })); @@ -261,12 +213,10 @@ export class MainView extends React.Component { const pres = Docs.Create.PresDocument(new List<Doc>(), { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeStatus: "replaced", boxShadow: "0 0", system: true }); CollectionDockingView.AddSplit(pres, "right"); - Doc.UserDoc().activePresentation = pres; - const myPresentations = Doc.UserDoc().myPresentations as Doc; - Doc.AddDocToList(myPresentations, "data", pres); + this.userDoc.activePresentation = pres; + Doc.AddDocToList(this.userDoc.myPresentations as Doc, "data", pres); } - onDrop = (e: React.DragEvent<HTMLDivElement>) => { e.preventDefault(); e.stopPropagation(); @@ -274,13 +224,11 @@ export class MainView extends React.Component { @action onResize = (r: any) => { - this._panelWidth = r.offset.width;// - this.propertiesWidth(); + this._panelWidth = r.offset.width; this._panelHeight = r.offset.height; } - @action - getPWidth = () => this._panelWidth - this.propertiesWidth() - + getPWidth = () => this._panelWidth - this.propertiesWidth(); getPHeight = () => this._panelHeight; getContentsHeight = () => this._panelHeight - this._buttonBarHeight; @@ -294,9 +242,8 @@ export class MainView extends React.Component { case DocumentType.FONTICON: return "white"; case DocumentType.RTF || DocumentType.LABEL || DocumentType.BUTTON: return "#2d2d2d"; case DocumentType.LINK: - case DocumentType.COL: { + case DocumentType.COL: if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)"; - } default: return "black"; } } else { @@ -306,9 +253,8 @@ export class MainView extends React.Component { case DocumentType.BUTTON: case DocumentType.LABEL: return "lightgray"; case DocumentType.LINK: - case DocumentType.COL: { + case DocumentType.COL: if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "lightgray"; - } default: return "white"; } } @@ -346,10 +292,9 @@ export class MainView extends React.Component { @computed get dockingContent() { TraceMobx(); - const mainContainer = this.mainContainer; const width = this.flyoutWidth + this.propertiesWidth(); return <div className="mainContent-div" onDrop={this.onDrop} style={{ width: `calc(100% - ${width}px)`, height: "100%" }}> - {!mainContainer ? (null) : this.mainDocView} + {!this.mainContainer ? (null) : this.mainDocView} </div>; } @@ -367,39 +312,32 @@ export class MainView extends React.Component { setupMoveUpEvents(this, e, action((e: PointerEvent) => { this.flyoutWidth = Math.max(e.clientX - 58, 0); if (this.flyoutWidth < 5) { - this.panelContent = "none"; + this._panelContent = "none"; this._lastButton && (this._lastButton.color = "white"); this._lastButton && (this._lastButton._backgroundColor = ""); } return false; }), emptyFunction, action(() => { - if (this.flyoutWidth < 15) MainView.expandFlyout(); + if (this.flyoutWidth < 15) this.expandFlyout(); else this.closeFlyout(); })); } } - @computed get topOffset() { return (CollectionMenu.Instance?.Pinned ? 35 : 0) + Number(SEARCH_PANEL_HEIGHT.replace("px", "")); } flyoutWidthFunc = () => this.flyoutWidth; addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => { return where === "close" ? CollectionDockingView.CloseSplit(doc) : - doc.dockingConfig ? CurrentUserUtils.openDashboard(Doc.UserDoc(), doc) : - CollectionDockingView.AddSplit(doc, "right"); + doc.dockingConfig ? CurrentUserUtils.openDashboard(Doc.UserDoc(), doc) : CollectionDockingView.AddSplit(doc, "right"); } sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0) - Number(SEARCH_PANEL_HEIGHT.replace("px", "")), 1); mainContainerXf = () => this.sidebarScreenToLocal().translate(-58, 0); @computed get flyout() { - if (!this.sidebarContent) return null; + if (!this._sidebarContent) return null; return <div className="mainView-libraryFlyout"> - <div className="mainView-contentArea" style={{ position: "relative", height: "100%", width: "100%", overflow: "visible" }}> - {/* {this.flyoutWidth > 0 ? <div className="mainView-libraryFlyout-close" - onPointerDown={this.closeFlyout}> - <FontAwesomeIcon icon="times" color="black" size="lg" /> - </div> : null} */} - + <div className="mainView-contentArea"> <DocumentView - Document={this.sidebarContent} + Document={this._sidebarContent} DataDoc={undefined} LibraryPath={emptyPath} addDocument={undefined} @@ -428,7 +366,8 @@ export class MainView extends React.Component { forcedBackgroundColor={() => "lightgrey"} /> </div> - {this.docButtons}</div>; + {this.docButtons} + </div>; } @computed get menuPanel() { @@ -470,101 +409,76 @@ export class MainView extends React.Component { closeFlyout = () => { this._lastButton && (this._lastButton.color = "white"); this._lastButton && (this._lastButton._backgroundColor = ""); - this.panelContent = "none"; + this._panelContent = "none"; this.flyoutWidth = 0; } - get groupManager() { return GroupManager.Instance; } - - _lastButton: Doc | undefined; @action selectMenu = (button: Doc) => { const title = StrCast(Doc.GetProto(button).title); this._lastButton && (this._lastButton.color = "white"); this._lastButton && (this._lastButton._backgroundColor = ""); - if (this.panelContent === title && this.flyoutWidth !== 0) { - this.panelContent = "none"; + if (this._panelContent === title && this.flyoutWidth !== 0) { + this._panelContent = "none"; this.flyoutWidth = 0; } else { - let panelDoc: Doc | undefined; - switch (this.panelContent = title) { - case "Settings": SettingsManager.Instance.open(); break; - case "Catalog": SearchBox.Instance._searchFullDB = "My Stuff"; + switch (this._panelContent = title) { + case "Settings": + SettingsManager.Instance.open(); + this.flyoutWidth = 0; + break; + case "Catalog": + SearchBox.Instance._searchFullDB = "My Stuff"; SearchBox.Instance.newsearchstring = ""; SearchBox.Instance.enter(undefined); + this.flyoutWidth = 0; break; default: - panelDoc = button.target as any; break; + this._sidebarContent.proto = button.target as any; + this.expandFlyout(); + button._backgroundColor = "lightgrey"; + button.color = "black"; + this._lastButton = button; } - this.sidebarContent.proto = panelDoc; - if (panelDoc) { - MainView.expandFlyout(); - button._backgroundColor = "lightgrey"; - button.color = "black"; - this._lastButton = button; - } else this.flyoutWidth = 0; } return true; } - @action - closeProperties = () => { - CurrentUserUtils.propertiesWidth = 0; - } - @computed get propertiesView() { TraceMobx(); - return <div className="mainView-propertiesView" style={{ - overflow: this.propertiesWidth() < 15 ? "hidden" : undefined - }}> + return <div className="mainView-propertiesView"> <PropertiesView width={this.propertiesWidth()} height={this.getContentsHeight()} renderDepth={1} ScreenToLocalTransform={Transform.Identity} - onDown={this.closeProperties} - /> + onDown={action(() => CurrentUserUtils.propertiesWidth = 0)} /> </div>; } @computed get mainInnerContent() { - const rightFlyout = this.propertiesWidth() - 1; return <> {this.menuPanel} - <div style={{ display: "contents", flexDirection: "row", position: "relative" }}> + <div className="mainView-innerContent" > <div className="mainView-flyoutContainer" style={{ width: this.flyoutWidth }}> - {this.flyoutWidth !== 0 ? <div className="mainView-libraryHandle" - onPointerDown={this.onFlyoutPointerDown} - //style={{ backgroundColor: '#8c8b8b' }} - > - <span title="library View Dragger" style={{ - width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "3vw", - //height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "100vh", - position: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "absolute" : "fixed", - top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "0" - }} /> - <div className="mainview-libraryHandle-icon"> - <FontAwesomeIcon icon="chevron-left" color="black" size="sm" /> - </div> - </div> : null} - <div className="mainView-libraryFlyout" style={{ - //transformOrigin: this._flyoutTranslate ? "" : "left center", - transition: this._flyoutTranslate ? "" : "width .5s", - //transform: `scale(${this._flyoutTranslate ? 1 : 0.8})`, - boxShadow: this._flyoutTranslate ? "" : "rgb(156, 147, 150) 0.2vw 0.2vw 0.2vw" - }}> + {this.flyoutWidth === 0 ? (null) : + <div className="mainView-libraryHandle" onPointerDown={this.onFlyoutPointerDown} > + <span className={`mainView-libraryDragger${this._flyoutTranslate ? "" : "-out"}`} /> + <div className="mainview-libraryHandle-icon"> + <FontAwesomeIcon icon="chevron-left" color="black" size="sm" /> + </div> + </div>} + <div className={`mainView-libraryFlyout${this._flyoutTranslate ? "" : "-out"}`} > {this.flyout} {this.expandButton} </div> </div> {this.dockingContent} - {this.showProperties ? (null) : - <div className="mainView-propertiesDragger" title="Properties View Dragger" onPointerDown={this.onPropertiesPointerDown} - style={{ right: rightFlyout, top: "50%" }}> - <div className="mainView-propertiesDragger-icon"> - <FontAwesomeIcon icon={this.propertiesIcon} color="black" size="sm" /> </div> + <div className="mainView-propertiesDragger" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}> + <div className="mainView-propertiesDragger-icon"> + <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color="black" size="sm" /> </div> - } + </div> {this.propertiesWidth() < 10 ? (null) : this.propertiesView} </div> </>; @@ -579,7 +493,6 @@ export class MainView extends React.Component { {({ measureRef }) => <div className="mainView-mainContent" ref={measureRef} style={{ color: this.darkScheme ? "rgb(205,205,205)" : "black", - //change to times 2 for both pinned height, width: pinned ? `calc(100% - 200px)` : "100%" }} > @@ -589,14 +502,13 @@ export class MainView extends React.Component { </Measure>); } - public static expandFlyout = action(() => { - MainView.Instance._flyoutTranslate = true; - MainView.Instance.flyoutWidth = (MainView.Instance.flyoutWidth || 250); - + expandFlyout = action(() => { + this._flyoutTranslate = true; + this.flyoutWidth = (this.flyoutWidth || 250); }); @computed get expandButton() { - return !this._flyoutTranslate ? (<div className="mainView-expandFlyoutButton" title="Re-attach sidebar" onPointerDown={MainView.expandFlyout}></div>) : (null); + return !this._flyoutTranslate ? (<div className="mainView-expandFlyoutButton" title="Re-attach sidebar" onPointerDown={this.expandFlyout}></div>) : (null); } addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => { @@ -612,11 +524,11 @@ export class MainView extends React.Component { const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current); return new Transform(-translateX, -translateY, 1 / scale); } + @computed get docButtons() { const dockedBtns = Doc.UserDoc()?.dockedBtns; - if (dockedBtns instanceof Doc) { - return <div className="mainView-docButtons" ref={this._docBtnRef} - style={{ height: !dockedBtns.linearViewIsExpanded ? "42px" : undefined }} > + return !(dockedBtns instanceof Doc) ? (null) : + <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !dockedBtns.linearViewIsExpanded ? "42px" : undefined }} > <CollectionLinearView Document={dockedBtns} DataDoc={undefined} @@ -651,18 +563,7 @@ export class MainView extends React.Component { ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} /> </div>; - } - return (null); } - - get mainViewElement() { - return document.getElementById("mainView-container"); - } - - get mainViewRef() { - return this._mainViewRef; - } - @computed get snapLines() { return !Doc.UserDoc().showSnapLines ? (null) : <div className="mainView-snapLines"> <svg style={{ width: "100%", height: "100%" }}> @@ -697,8 +598,7 @@ export class MainView extends React.Component { @computed get search() { return <div className="mainView-searchPanel"> - {/* <div style={{ float: "left", marginLeft: "10px" }}>{Doc.CurrentUserEmail}</div> */} - <div><DocumentView Document={this.searchDoc} + <DocumentView Document={CurrentUserUtils.MySearchPanelDoc} DataDoc={undefined} LibraryPath={emptyPath} addDocument={undefined} @@ -723,13 +623,13 @@ export class MainView extends React.Component { searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} - /></div> + /> </div>; } @computed get invisibleWebBox() { // see note under the makeLink method in HypothesisUtils.ts return !DocumentLinksButton.invisibleWebDoc ? null : - <div style={{ position: 'absolute', left: 50, top: 50, display: 'block', width: '500px', height: '1000px' }} ref={DocumentLinksButton.invisibleWebRef}> + <div className="mainView-invisibleWebRef" ref={DocumentLinksButton.invisibleWebRef}> <WebBox fieldKey={"data"} ContainingCollectionView={undefined} @@ -761,7 +661,6 @@ export class MainView extends React.Component { render() { return (<div className={"mainView-container" + (this.darkScheme ? "-dark" : "")} ref={this._mainViewRef}> - {this.inkResources} <DictationOverlay /> <SharingManager /> @@ -787,11 +686,10 @@ export class MainView extends React.Component { <RadialMenu /> <PDFMenu /> <MarqueeOptionsMenu /> - <OverlayView /> <TimelineMenu /> {this.snapLines} - <div ref={this.makeWebRef} style={{ position: 'absolute', left: -1000, top: -1000, display: 'block', width: '200px', height: '800px' }} /> + <div className="mainView-webRef" ref={this.makeWebRef} /> </div >); } @@ -800,7 +698,7 @@ export class MainView extends React.Component { invisibleDoc => { ReactDOM.unmountComponentAtNode(ele); invisibleDoc && ReactDOM.render(<span title="Drag as document" className="invisible-webbox" > - <div style={{ position: 'absolute', left: -1000, top: -1000, display: 'block', width: '200px', height: '800px' }} ref={DocumentLinksButton.invisibleWebRef}> + <div className="mainView-webRef" ref={DocumentLinksButton.invisibleWebRef}> <WebBox fieldKey={"data"} ContainingCollectionView={undefined} @@ -839,55 +737,11 @@ export class MainView extends React.Component { // For some reason, Hypothes.is annotations don't load until a click is registered on the page, // so we keep simulating clicks until annotations have loaded and editing is successful - const interval = setInterval(() => { - !success && simulateMouseClick(ele, 50, 50, 50, 50); - }, 500); - + const interval = setInterval(() => !success && simulateMouseClick(ele, 50, 50, 50, 50), 500); setTimeout(() => !success && clearInterval(interval), 10000); // give up if no success after 10s document.addEventListener("editSuccess", onSuccess); }); } - - importDocument = () => { - const input = document.createElement("input"); - input.type = "file"; - input.multiple = true; - input.accept = ".zip, application/pdf, video/*, image/*, audio/*"; - input.onchange = async _e => { - const upload = Utils.prepend("/uploadDoc"); - const formData = new FormData(); - const file = input.files && input.files[0]; - if (file && file.type === 'application/zip') { - formData.append('file', file); - formData.append('remap', "true"); - const response = await fetch(upload, { method: "POST", body: formData }); - const json = await response.json(); - if (json !== "error") { - const doc = await DocServer.GetRefField(json); - if (doc instanceof Doc) { - setTimeout(() => { - SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => { - docs.docs.forEach(d => LinkManager.Instance.addLink(d)); - }); - }, 2000); // need to give solr some time to update so that this query will find any link docs we've added. - } - } - } else if (input.files && input.files.length !== 0) { - const importDocs = Cast(Doc.UserDoc().myImportDocs, Doc, null); - const disposer = OverlayView.Instance.addElement(<ReactLoading type={"spinningBubbles"} color={"green"} height={250} width={250} />, { x: 300, y: 200 }); - - DocListCastAsync(importDocs.data).then(async list => { - list?.push(... await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {})); - disposer(); - }); - } else { - console.log("No file selected"); - } - }; - input.click(); - } } Scripting.addGlobal(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); }); Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().fontFamily = "Comic Sans MS"; Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }); -Scripting.addGlobal(function importDocument() { return MainView.Instance.importDocument(); }, - "imports files from device directly into the import sidebar"); diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 5188ab9a8..cc0dd0443 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -1,6 +1,7 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; +import ReactLoading from 'react-loading'; import { Doc, DocListCast, Opt } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { NumCast, Cast } from "../../fields/Types"; @@ -212,6 +213,11 @@ export class OverlayView extends React.Component { }); } + public static ShowSpinner() { + return OverlayView.Instance.addElement(<ReactLoading type="spinningBubbles" color="green" height={250} width={250} />, { x: 300, y: 200 }); + } + + render() { return ( <div className="overlayView" id="overlayView"> diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index 858d7859f..a8ec1db39 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -374,47 +374,6 @@ button.add-column { } } -.altcollectionTimeView-treeView { - display: flex; - flex-direction: column; - width: 175px; - height: auto; - position: fixed; - border-left: solid 1px; - z-index: 1; - - .collectionTimeView-addfacet { - display: inline-block; - width: 200px; - height: 30px; - background: darkGray; - text-align: left; - - .collectionTimeView-button { - align-items: center; - display: flex; - width: 100%; - height: 100%; - - .collectionTimeView-span { - margin: auto; - } - } - - >div, - >div>div { - width: 100%; - height: 100%; - } - } - - .altcollectionTimeView-tree { - display: inline-block; - width: 100%; - height: calc(100% - 30px); - } -} - .collectionSchema-row { height: 100%; background-color: white; |