diff options
Diffstat (limited to 'src/client')
21 files changed, 304 insertions, 238 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 1b0ba6bc3..5a34fcf11 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -77,7 +77,7 @@ export namespace DocServer { } export function getFieldWriteMode(field: string) { - return fieldWriteModes[field] || WriteMode.Default; + return Doc.CurrentUserEmail === 'guest' ? WriteMode.LiveReadonly : fieldWriteModes[field] || WriteMode.Default; } export function registerDocWithCachedUpdate(doc: Doc, field: string, oldValue: any) { @@ -178,7 +178,7 @@ export namespace DocServer { _isReadOnly = true; _CreateField = field => (_cache[field[Id]] = field); _UpdateField = emptyFunction; - _RespondToUpdate = emptyFunction; // bcz: option: don't clear RespondToUpdate to continue to receive updates as others change the DB + // _RespondToUpdate = emptyFunction; // bcz: option: don't clear RespondToUpdate to continue to receive updates as others change the DB } } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 6c80cf0f4..bbf2ff3f9 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1,6 +1,7 @@ import { reaction } from "mobx"; import * as rp from 'request-promise'; import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc"; +import { Id } from "../../fields/FieldSymbols"; import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; @@ -14,6 +15,7 @@ import { DocServer } from "../DocServer"; import { Docs, DocumentOptions, DocUtils, FInfo } from "../documents/Documents"; import { CollectionViewType, DocumentType } from "../documents/DocumentTypes"; import { TreeViewType } from "../views/collections/CollectionTreeView"; +import { DashboardView } from "../views/DashboardView"; import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; @@ -762,8 +764,6 @@ export class CurrentUserUtils { doc["clickFuncs-child"] = Docs.Create.TreeDocument([openInTarget, openDetail], { title: "on Child Click function templates", system: true }); } - // this is equivalent to using PrefetchProxies to make sure all the childClickFuncs have been retrieved. - PromiseValue(Cast(doc["clickFuncs-child"], Doc)).then(func => func && PromiseValue(func.data).then(DocListCast)); if (doc.clickFuncs === undefined) { const onClick = Docs.Create.ScriptingDocument(undefined, { @@ -789,7 +789,6 @@ export class CurrentUserUtils { }, "onCheckedClick"); doc.clickFuncs = Docs.Create.TreeDocument([onClick, onChildClick, onDoubleClick, onCheckedClick], { title: "onClick funcs", system: true }); } - PromiseValue(Cast(doc.clickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast)); return doc.clickFuncs as Doc; } @@ -886,13 +885,20 @@ export class CurrentUserUtils { public static async loadUserDocument(id: string) { await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => { const { userDocumentId, sharingDocumentId, linkDatabaseId } = JSON.parse(ids); - if (userDocumentId !== "guest") { + if (userDocumentId) { return DocServer.GetRefField(userDocumentId).then(async field => { Docs.newAccount = !(field instanceof Doc); await Docs.Prototypes.initialize(); const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc; - Docs.newAccount &&(userDoc.activePage = "home"); - return this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId); + this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId); + if (Docs.newAccount) { + if (Doc.CurrentUserEmail === "guest") { + DashboardView.createNewDashboard(undefined, "guest dashboard"); + } else { + userDoc.activePage = "home"; + } + } + return userDoc; }); } else { throw new Error("There should be a user id! Why does Dash think there isn't one?"); diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 59334f6a2..c8b784390 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -14,6 +14,7 @@ import { GroupMemberView } from './GroupMemberView'; import { SharingManager, User } from './SharingManager'; import { listSpec } from '../../fields/Schema'; import { DateField } from '../../fields/DateField'; +import { Id } from '../../fields/FieldSymbols'; /** * Interface for options for the react-select component @@ -49,9 +50,11 @@ export class GroupManager extends React.Component<{}> { * Fetches the list of users stored on the database. */ populateUsers = async () => { - const userList = await RequestPromise.get(Utils.prepend('/getUsers')); - const raw = JSON.parse(userList) as User[]; - raw.map(action(user => !this.users.some(umail => umail === user.email) && this.users.push(user.email))); + if (Doc.UserDoc()[Id] !== '__guest__') { + const userList = await RequestPromise.get(Utils.prepend('/getUsers')); + const raw = JSON.parse(userList) as User[]; + raw.map(action(user => !this.users.some(umail => umail === user.email) && this.users.push(user.email))); + } }; /** diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 12d1793af..cf143c5e8 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -4,6 +4,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { ColorState, SketchPicker } from 'react-color'; import { Doc } from '../../fields/Doc'; +import { Id } from '../../fields/FieldSymbols'; import { BoolCast, Cast, StrCast } from '../../fields/Types'; import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; @@ -81,7 +82,7 @@ export class SettingsManager extends React.Component<{}> { if (this.playgroundMode) { DocServer.Control.makeReadOnly(); addStyleSheetRule(SettingsManager._settingsStyle, 'topbar-inner-container', { background: 'red !important' }); - } else DocServer.Control.makeEditable(); + } else Doc.CurrentUserEmail !== 'guest' && DocServer.Control.makeEditable(); }); @undoBatch diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 793027ea1..895bd3374 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -23,6 +23,7 @@ import { GroupMemberView } from './GroupMemberView'; import { SelectionManager } from './SelectionManager'; import './SharingManager.scss'; import { LinkManager } from './LinkManager'; +import { Id } from '../../fields/FieldSymbols'; export interface User { email: string; @@ -136,7 +137,7 @@ export class SharingManager extends React.Component<{}> { * Populates the list of validated users (this.users) by adding registered users which have a sharingDocument. */ populateUsers = async () => { - if (!this.populating) { + if (!this.populating && Doc.UserDoc()[Id] !== '__guest__') { this.populating = true; const userList = await RequestPromise.get(Utils.prepend('/getUsers')); const raw = JSON.parse(userList) as User[]; diff --git a/src/client/util/TrackMovements.ts b/src/client/util/TrackMovements.ts index d512e4802..4a2ccd706 100644 --- a/src/client/util/TrackMovements.ts +++ b/src/client/util/TrackMovements.ts @@ -1,27 +1,26 @@ -import { IReactionDisposer, observable, observe, reaction } from "mobx"; -import { NumCast } from "../../fields/Types"; -import { Doc, DocListCast } from "../../fields/Doc"; -import { CollectionDockingView } from "../views/collections/CollectionDockingView"; -import { Id } from "../../fields/FieldSymbols"; +import { IReactionDisposer, observable, observe, reaction } from 'mobx'; +import { NumCast } from '../../fields/Types'; +import { Doc, DocListCast } from '../../fields/Doc'; +import { CollectionDockingView } from '../views/collections/CollectionDockingView'; +import { Id } from '../../fields/FieldSymbols'; export type Movement = { - time: number, - panX: number, - panY: number, - scale: number, - docId: string, -} + time: number; + panX: number; + panY: number; + scale: number; + docId: string; +}; export type Presentation = { - movements: Movement[] | null, - totalTime: number, - meta: Object | Object[], -} + movements: Movement[] | null; + totalTime: number; + meta: Object | Object[]; +}; export class TrackMovements { - private static get NULL_PRESENTATION(): Presentation { - return { movements: null, meta: {}, totalTime: -1, } + return { movements: null, meta: {}, totalTime: -1 }; } // instance variables @@ -32,16 +31,17 @@ export class TrackMovements { private recordingFFViews: Map<string, IReactionDisposer> | null; private tabChangeDisposeFunc: IReactionDisposer | null; - // create static instance and getter for global use @observable static _instance: TrackMovements; - static get Instance(): TrackMovements { return TrackMovements._instance } + static get Instance(): TrackMovements { + return TrackMovements._instance; + } constructor() { // init the global instance TrackMovements._instance = this; // init the instance variables - this.currentPresentation = TrackMovements.NULL_PRESENTATION + this.currentPresentation = TrackMovements.NULL_PRESENTATION; this.tracking = false; this.absoluteStart = -1; @@ -52,28 +52,37 @@ export class TrackMovements { // little helper :) private get nullPresentation(): boolean { - return this.currentPresentation.movements === null + return this.currentPresentation.movements === null; } private addRecordingFFView(doc: Doc, key: string = doc[Id]): void { // console.info('adding dispose func : docId', key, 'doc', doc); - if (this.recordingFFViews === null) { console.warn('addFFView on null RecordingApi'); return; } - if (this.recordingFFViews.has(key)) { console.warn('addFFView : key already in map'); return; } + if (this.recordingFFViews === null) { + console.warn('addFFView on null RecordingApi'); + return; + } + if (this.recordingFFViews.has(key)) { + console.warn('addFFView : key already in map'); + return; + } const disposeFunc = reaction( - () => ({ x: NumCast(doc.panX, -1), y: NumCast(doc.panY, -1), scale: NumCast(doc.viewScale, 0)}), - (res) => (res.x !== -1 && res.y !== -1 && this.tracking) && this.trackMovement(res.x, res.y, key, res.scale), + () => ({ x: NumCast(doc.panX, -1), y: NumCast(doc.panY, -1), scale: NumCast(doc.viewScale, 0) }), + res => res.x !== -1 && res.y !== -1 && this.tracking && this.trackMovement(res.x, res.y, key, res.scale) ); this.recordingFFViews?.set(key, disposeFunc); } private removeRecordingFFView = (key: string) => { // console.info('removing dispose func : docId', key); - if (this.recordingFFViews === null) { console.warn('removeFFView on null RecordingApi'); return; } + if (this.recordingFFViews === null) { + console.warn('removeFFView on null RecordingApi'); + return; + } this.recordingFFViews.get(key)?.(); this.recordingFFViews.delete(key); - } + }; // in the case where only one tab was changed (updates not across dashboards), set only one to true private updateRecordingFFViewsFromTabs = (tabbedDocs: Doc[], onlyOne = false) => { @@ -86,7 +95,6 @@ export class TrackMovements { if (isFFView(DashDoc)) tabbedFFViews.add(DashDoc[Id]); } - // new tab was added - need to add it if (tabbedFFViews.size > this.recordingFFViews.size) { for (const DashDoc of tabbedDocs) { @@ -103,13 +111,13 @@ export class TrackMovements { // tab was removed - need to remove it from recordingFFViews else if (tabbedFFViews.size < this.recordingFFViews.size) { for (const [key] of this.recordingFFViews) { - if (!tabbedFFViews.has(key)) { + if (!tabbedFFViews.has(key)) { this.removeRecordingFFView(key); if (onlyOne) return; - } + } } } - } + }; private initTabTracker = () => { if (this.recordingFFViews === null) { @@ -117,18 +125,19 @@ export class TrackMovements { } // init the dispose funcs on the page - const docList = DocListCast(CollectionDockingView.Instance.props.Document.data); + const docList = DocListCast(CollectionDockingView.Instance?.props.Document.data); this.updateRecordingFFViewsFromTabs(docList); // create a reaction to monitor changes in tabs - this.tabChangeDisposeFunc = - reaction(() => CollectionDockingView.Instance.props.Document.data, - (change) => { - // TODO: consider changing between dashboards - // console.info('change in tabs', change); - this.updateRecordingFFViewsFromTabs(DocListCast(change), true); - }); - } + this.tabChangeDisposeFunc = reaction( + () => CollectionDockingView.Instance?.props.Document.data, + change => { + // TODO: consider changing between dashboards + // console.info('change in tabs', change); + this.updateRecordingFFViewsFromTabs(DocListCast(change), true); + } + ); + }; start = (meta?: Object) => { this.initTabTracker(); @@ -142,12 +151,12 @@ export class TrackMovements { this.absoluteStart = startDate.getTime(); // (2) assign meta content if it exists - this.currentPresentation.meta = meta || {} + this.currentPresentation.meta = meta || {}; // (3) assign start date to currentPresenation - this.currentPresentation.movements = [] + this.currentPresentation.movements = []; // (4) set tracking true to allow trackMovements - this.tracking = true - } + this.tracking = true; + }; /* stops the video and returns the presentatation; if no presentation, returns undefined */ yieldPresentation(clearData: boolean = true): Presentation | null { @@ -157,7 +166,7 @@ export class TrackMovements { // set the previus recording view to the play view // this.playFFView = this.recordingFFView; - // ensure we add the endTime now that they are done recording + // ensure we add the endTime now that they are done recording const cpy = { ...this.currentPresentation, totalTime: new Date().getTime() - this.absoluteStart }; // reset the current presentation @@ -169,10 +178,10 @@ export class TrackMovements { finish = (): void => { // make is tracking false - this.tracking = false + this.tracking = false; // reset the RecordingApi instance this.clear(); - } + }; private clear = (): void => { // clear the disposeFunc if we are done (not tracking) @@ -187,44 +196,46 @@ export class TrackMovements { } // clear presenation data - this.currentPresentation = TrackMovements.NULL_PRESENTATION + this.currentPresentation = TrackMovements.NULL_PRESENTATION; // clear absoluteStart - this.absoluteStart = -1 - } + this.absoluteStart = -1; + }; removeAllRecordingFFViews = () => { - if (this.recordingFFViews === null) { console.warn('removeAllFFViews on null RecordingApi'); return; } + if (this.recordingFFViews === null) { + console.warn('removeAllFFViews on null RecordingApi'); + return; + } for (const [id, disposeFunc] of this.recordingFFViews) { // console.info('calling dispose func : docId', id); disposeFunc(); this.recordingFFViews.delete(id); } - } + }; private trackMovement = (panX: number, panY: number, docId: string, scale: number = 0) => { // ensure we are recording to track if (!this.tracking) { - console.error('[recordingApi.ts] trackMovements(): tracking is false') + console.error('[recordingApi.ts] trackMovements(): tracking is false'); return; } // check to see if the presetation is init - if not, we are between segments // TODO: make this more clear - tracking should be "live tracking", not always true when the recording api being used (between start and yieldPres) - // bacuse tracking should be false inbetween segments high key + // bacuse tracking should be false inbetween segments high key if (this.nullPresentation) { - console.warn('[recordingApi.ts] trackMovements(): trying to store movemetns between segments') + console.warn('[recordingApi.ts] trackMovements(): trying to store movemetns between segments'); return; } // get the time - const time = new Date().getTime() - this.absoluteStart + const time = new Date().getTime() - this.absoluteStart; // make new movement object - const movement: Movement = { time, panX, panY, scale, docId } + const movement: Movement = { time, panX, panY, scale, docId }; // add that movement to the current presentation data's movement array - this.currentPresentation.movements && this.currentPresentation.movements.push(movement) - } - + this.currentPresentation.movements && this.currentPresentation.movements.push(movement); + }; // method that concatenates an array of presentatations into one public concatPresentations = (presentations: Presentation[]): Presentation => { @@ -233,13 +244,15 @@ export class TrackMovements { let sumTime = 0; let combinedMetas: any[] = []; - presentations.forEach((presentation) => { + presentations.forEach(presentation => { const { movements, totalTime, meta } = presentation; // update movements if they had one if (movements) { // add the summed time to the movements - const addedTimeMovements = movements.map(move => { return { ...move, time: move.time + sumTime } }); + const addedTimeMovements = movements.map(move => { + return { ...move, time: move.time + sumTime }; + }); // concat the movements already in the combined presentation with these new ones combinedMovements.push(...addedTimeMovements); } @@ -252,6 +265,6 @@ export class TrackMovements { }); // return the combined presentation with the updated total summed time - return { movements: combinedMovements, totalTime: sumTime, meta: combinedMetas }; - } + return { movements: combinedMovements, totalTime: sumTime, meta: combinedMetas }; + }; } diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index ea24dbf6d..1e6a377de 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -1,10 +1,10 @@ -@import "global/globalCssVariables"; +@import 'global/globalCssVariables'; .contextMenu-cont { position: absolute; display: flex; z-index: 100000; - box-shadow: 0px 3px 4px rgba(0,0,0,30%); + box-shadow: 0px 3px 4px rgba(0, 0, 0, 30%); flex-direction: column; background: whitesmoke; border-radius: 3px; @@ -28,9 +28,9 @@ position: absolute; display: flex; z-index: 1000; - box-shadow: #AAAAAA .2vw .2vw .4vw; + box-shadow: #aaaaaa 0.2vw 0.2vw 0.4vw; flex-direction: column; - border: 1px solid #BBBBBBBB; + border: 1px solid #bbbbbbbb; border-radius: 15px; padding-top: 10px; padding-bottom: 10px; @@ -49,7 +49,7 @@ -moz-user-select: none; -ms-user-select: none; user-select: none; - transition: all .1s; + transition: all 0.1s; border-style: none; // padding: 10px 0px 10px 0px; white-space: nowrap; @@ -58,7 +58,7 @@ text-transform: uppercase; padding-right: 30px; - .icon-background { + .contextMenu-item-icon-background { pointer-events: all; background-color: transparent; width: 35px; @@ -78,7 +78,7 @@ -moz-user-select: none; -ms-user-select: none; user-select: none; - transition: all .1s; + transition: all 0.1s; border-style: none; // padding: 10px 0px 10px 0px; white-space: nowrap; @@ -89,7 +89,7 @@ } .contextMenu-item:hover { - border-width: .11px; + border-width: 0.11px; border-style: none; border-color: $medium-gray; // rgb(187, 186, 186); border-bottom-style: solid; @@ -116,8 +116,8 @@ -moz-user-select: none; -ms-user-select: none; user-select: none; - transition: all .1s; - border-width: .11px; + transition: all 0.1s; + border-width: 0.11px; border-style: none; border-color: $medium-gray; // rgb(187, 186, 186); // padding: 10px 0px 10px 0px; @@ -152,4 +152,4 @@ padding-left: 10px; border: solid black 1px; border-radius: 5px; -}
\ No newline at end of file +} diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index 30073e21f..dc9c2eb6c 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -1,9 +1,9 @@ -import React = require("react"); -import { observable, action, runInAction } from "mobx"; -import { observer } from "mobx-react"; +import React = require('react'); +import { observable, action, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { UndoManager } from "../util/UndoManager"; +import { UndoManager } from '../util/UndoManager'; export interface OriginalMenuProps { description: string; @@ -37,7 +37,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select } handleEvent = async (e: React.MouseEvent<HTMLDivElement>) => { - if ("event" in this.props) { + if ('event' in this.props) { this.props.closeMenu?.(); let batch: UndoManager.Batch | undefined; if (this.props.undoable !== false) { @@ -46,7 +46,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select await this.props.event({ x: e.clientX, y: e.clientY }); batch?.end(); } - } + }; currentTimeout?: any; static readonly timeout = 300; @@ -62,8 +62,11 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select } this._overPosY = e.clientY; this._overPosX = e.clientX; - this.currentTimeout = setTimeout(action(() => this.overItem = true), ContextMenuItem.timeout); - } + this.currentTimeout = setTimeout( + action(() => (this.overItem = true)), + ContextMenuItem.timeout + ); + }; onPointerLeave = () => { if (this.currentTimeout) { @@ -73,57 +76,68 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select if (!this.overItem) { return; } - this.currentTimeout = setTimeout(action(() => this.overItem = false), ContextMenuItem.timeout); - } + this.currentTimeout = setTimeout( + action(() => (this.overItem = false)), + ContextMenuItem.timeout + ); + }; render() { - if ("event" in this.props) { + if ('event' in this.props) { return ( - <div className={"contextMenu-item" + (this.props.selected ? " contextMenu-itemSelected" : "")} onPointerDown={this.handleEvent}> + <div className={'contextMenu-item' + (this.props.selected ? ' contextMenu-itemSelected' : '')} onPointerDown={this.handleEvent}> {this.props.icon ? ( - <span className="icon-background"> + <span className="contextMenu-item-icon-background"> <FontAwesomeIcon icon={this.props.icon} size="sm" /> </span> ) : null} - <div className="contextMenu-description"> - {this.props.description.replace(":", "")} - </div> + <div className="contextMenu-description">{this.props.description.replace(':', '')}</div> </div> ); - } else if ("subitems" in this.props) { - const where = !this.overItem ? "" : this._overPosY < window.innerHeight / 3 ? "flex-start" : this._overPosY > window.innerHeight * 2 / 3 ? "flex-end" : "center"; - const marginTop = !this.overItem ? "" : this._overPosY < window.innerHeight / 3 ? "20px" : this._overPosY > window.innerHeight * 2 / 3 ? "-20px" : ""; + } else if ('subitems' in this.props) { + const where = !this.overItem ? '' : this._overPosY < window.innerHeight / 3 ? 'flex-start' : this._overPosY > (window.innerHeight * 2) / 3 ? 'flex-end' : 'center'; + const marginTop = !this.overItem ? '' : this._overPosY < window.innerHeight / 3 ? '20px' : this._overPosY > (window.innerHeight * 2) / 3 ? '-20px' : ''; // here - const submenu = !this.overItem ? (null) : - <div className="contextMenu-subMenu-cont" + const submenu = !this.overItem ? null : ( + <div + className="contextMenu-subMenu-cont" style={{ - marginLeft: window.innerHeight - this._overPosX - 50 > 0 ? "90%" : "20%", marginTop + marginLeft: window.innerHeight - this._overPosX - 50 > 0 ? '90%' : '20%', + marginTop, }}> - {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)} - </div>; + {this._items.map(prop => ( + <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} /> + ))} + </div> + ); if (!(this.props as SubmenuProps).noexpand) { - return <div className="contextMenu-inlineMenu"> - {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)} - </div>; + return ( + <div className="contextMenu-inlineMenu"> + {this._items.map(prop => ( + <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} /> + ))} + </div> + ); } return ( - <div className={"contextMenu-item" + (this.props.selected ? " contextMenu-itemSelected" : "")} - style={{ alignItems: where, borderTop: this.props.addDivider ? "solid 1px" : undefined }} - onMouseLeave={this.onPointerLeave} onMouseEnter={this.onPointerEnter}> + <div + className={'contextMenu-item' + (this.props.selected ? ' contextMenu-itemSelected' : '')} + style={{ alignItems: where, borderTop: this.props.addDivider ? 'solid 1px' : undefined }} + onMouseLeave={this.onPointerLeave} + onMouseEnter={this.onPointerEnter}> {this.props.icon ? ( - <span className="icon-background" onMouseEnter={this.onPointerLeave} style={{ alignItems: "center", alignSelf: "center" }}> + <span className="contextMenu-item-icon-background" onMouseEnter={this.onPointerLeave} style={{ alignItems: 'center', alignSelf: 'center' }}> <FontAwesomeIcon icon={this.props.icon} size="sm" /> </span> ) : null} - <div className="contextMenu-description" onMouseEnter={this.onPointerEnter} - style={{ alignItems: "center", alignSelf: "center" }} > + <div className="contextMenu-description" onMouseEnter={this.onPointerEnter} style={{ alignItems: 'center', alignSelf: 'center' }}> {this.props.description} - <FontAwesomeIcon icon={"angle-right"} size="lg" style={{ position: "absolute", right: "10px" }} /> + <FontAwesomeIcon icon={'angle-right'} size="lg" style={{ position: 'absolute', right: '10px' }} /> </div> {submenu} </div> ); } } -}
\ No newline at end of file +} diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 526a32f5a..9ea9128bd 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -238,7 +238,7 @@ export class DashboardView extends React.Component { } else if (doc.readOnly) { DocServer.Control.makeReadOnly(); } else { - DocServer.Control.makeEditable(); + Doc.CurrentUserEmail !== 'guest' && DocServer.Control.makeEditable(); } } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 73e0c9933..85f579975 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -116,8 +116,8 @@ export class KeyManager { var doDeselect = true; if (SnappingManager.GetIsDragging()) { DragManager.AbortDrag(); - } else if (CollectionDockingView.Instance.HasFullScreen) { - CollectionDockingView.Instance.CloseFullScreen(); + } else if (CollectionDockingView.Instance?.HasFullScreen) { + CollectionDockingView.Instance?.CloseFullScreen(); } else if (CollectionStackedTimeline.SelectingRegion) { CollectionStackedTimeline.SelectingRegion = undefined; doDeselect = false; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index e998f1fb9..4cb1183d0 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -5,6 +5,8 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { AssignAllExtensions } from '../../extensions/General/Extensions'; +import { Utils } from '../../Utils'; +import { DocServer } from '../DocServer'; import { Docs } from '../documents/Documents'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; import { LinkManager } from '../util/LinkManager'; // this must come before importing Docs and CurrentUserUtils @@ -19,31 +21,28 @@ AssignAllExtensions(); MainView.Live = window.location.search.includes('live'); window.location.search.includes('safe') && CollectionView.SetSafeMode(true); const info = await CurrentUserUtils.loadCurrentUser(); - if (info.id !== '__guest__') { - // a guest will not have an id registered - await CurrentUserUtils.loadUserDocument(info.id); - } else { - await Docs.Prototypes.initialize(); + if (info.email === 'guest') DocServer.Control.makeReadOnly(); + await CurrentUserUtils.loadUserDocument(info.id); + setTimeout(() => { + document.getElementById('root')!.addEventListener( + 'wheel', + event => { + if (event.ctrlKey) { + event.preventDefault(); + } + }, + true + ); + const startload = (document as any).startLoad; + const loading = Date.now() - (startload ? Number(startload) : Date.now() - 3000); + console.log('Loading Time = ' + loading); + const d = new Date(); + d.setTime(d.getTime() + 100 * 24 * 60 * 60 * 1000); + const expires = 'expires=' + d.toUTCString(); + document.cookie = `loadtime=${loading};${expires};path=/`; new LinkManager(); - } - document.getElementById('root')!.addEventListener( - 'wheel', - event => { - if (event.ctrlKey) { - event.preventDefault(); - } - }, - true - ); - const startload = (document as any).startLoad; - const loading = Date.now() - (startload ? Number(startload) : Date.now() - 3000); - console.log('Loading Time = ' + loading); - const d = new Date(); - d.setTime(d.getTime() + 100 * 24 * 60 * 60 * 1000); - const expires = 'expires=' + d.toUTCString(); - document.cookie = `loadtime=${loading};${expires};path=/`; - new LinkManager(); - new TrackMovements(); - new ReplayMovements(); - ReactDOM.render(<MainView />, document.getElementById('root')); + new TrackMovements(); + new ReplayMovements(); + ReactDOM.render(<MainView />, document.getElementById('root')); + }, 0); })(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index b61cd3409..7e032af5e 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -3,7 +3,7 @@ import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons'; import * as far from '@fortawesome/free-regular-svg-icons'; import * as fa from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, configure, observable, reaction } from 'mobx'; +import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; @@ -77,12 +77,15 @@ export class MainView extends React.Component { @observable private _panelContent: string = 'none'; @observable private _sidebarContent: any = Doc.MyLeftSidebarPanel; @observable private _leftMenuFlyoutWidth: number = 0; + @computed get _hideUI() { + return this.mainDoc && this.mainDoc._viewType !== CollectionViewType.Docking; + } @computed private get dashboardTabHeight() { - return 27; + return this._hideUI ? 0 : 27; } // 27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js @computed private get topOfDashUI() { - return Number(DASHBOARD_SELECTOR_HEIGHT.replace('px', '')); + return this._hideUI ? 0 : Number(DASHBOARD_SELECTOR_HEIGHT.replace('px', '')); } @computed private get topOfHeaderBarDoc() { return this.topOfDashUI; @@ -105,7 +108,12 @@ export class MainView extends React.Component { @computed private get colorScheme() { return StrCast(Doc.ActiveDashboard?.colorScheme); } + @observable mainDoc: Opt<Doc>; @computed private get mainContainer() { + if (window.location.pathname.startsWith('/doc/')) { + DocServer.GetRefField(window.location.pathname.substring('/doc/'.length)).then(main => runInAction(() => (this.mainDoc = main as Doc))); + return this.mainDoc; + } return this.userDoc ? Doc.ActiveDashboard : Doc.GuestDashboard; } @computed private get headerBarDoc() { @@ -116,10 +124,10 @@ export class MainView extends React.Component { } headerBarDocWidth = () => this.mainDocViewWidth(); - headerBarDocHeight = () => SettingsManager.headerBarHeight ?? 0; - topMenuHeight = () => 35; + headerBarDocHeight = () => (this._hideUI ? 0 : SettingsManager.headerBarHeight ?? 0); + topMenuHeight = () => (this._hideUI ? 0 : 35); topMenuWidth = returnZero; // value is ignored ... - leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace('px', '')); + leftMenuWidth = () => (this._hideUI ? 0 : Number(LEFT_MENU_WIDTH.replace('px', ''))); leftMenuHeight = () => this._dashUIHeight; leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth; leftMenuFlyoutHeight = () => this._dashUIHeight; @@ -212,7 +220,8 @@ export class MainView extends React.Component { const pathname = window.location.pathname.substr(1).split('/'); if (pathname.length > 1 && pathname[0] === 'doc') { Doc.MainDocId = pathname[1]; - !this.userDoc && DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (Doc.GuestTarget = field))); + //!this.userDoc && + DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (Doc.GuestTarget = field))); } } @@ -455,7 +464,11 @@ export class MainView extends React.Component { AudioBox.Enabled = true; const targets = document.elementsFromPoint(e.x, e.y); if (targets.length) { - const targClass = targets[0].className.toString(); + let targClass = targets[0].className.toString(); + for (let i = 0; i < targets.length - 1; i++) { + if (typeof targets[i].className === 'object') targClass = targets[i + 1].className.toString(); + else break; + } !targClass.includes('contextMenu') && ContextMenu.Instance.closeMenu(); !['timeline-menu-desc', 'timeline-menu-item', 'timeline-menu-input'].includes(targClass) && TimelineMenu.Instance.closeMenu(); } @@ -517,7 +530,7 @@ export class MainView extends React.Component { return () => (this._exploreMode ? ScriptField.MakeScript('CollectionBrowseClick(documentView, clientX, clientY)', { documentView: 'any', clientX: 'number', clientY: 'number' })! : undefined); } headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1); - + mainScreenToLocalXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.topOfMainDocContent, 1); @computed get headerBarDocView() { return ( <div className="mainView-headerBar" style={{ height: this.headerBarDocHeight() }}> @@ -557,7 +570,7 @@ export class MainView extends React.Component { @computed get mainDocView() { return ( <> - {this.headerBarDocView} + {this._hideUI ? null : this.headerBarDocView} <DocumentView key="main" Document={this.mainContainer!} @@ -566,11 +579,11 @@ export class MainView extends React.Component { addDocTab={this.addDocTabFunc} pinToPres={emptyFunction} docViewPath={returnEmptyDoclist} - styleProvider={undefined} + styleProvider={this._hideUI ? DefaultStyleProvider : undefined} rootSelected={returnTrue} isContentActive={returnTrue} removeDocument={undefined} - ScreenToLocalTransform={Transform.Identity} + ScreenToLocalTransform={this._hideUI ? this.mainScreenToLocalXf : Transform.Identity} PanelWidth={this.mainDocViewWidth} PanelHeight={this.mainDocViewHeight} focus={DocUtils.DefaultFocus} @@ -582,7 +595,7 @@ export class MainView extends React.Component { ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} suppressSetHeight={true} - renderDepth={-1} + renderDepth={this._hideUI ? 0 : -1} /> </> ); @@ -750,7 +763,7 @@ export class MainView extends React.Component { const width = this.propertiesWidth() + leftMenuFlyoutWidth; return ( <> - {this.leftMenuPanel} + {this._hideUI ? null : this.leftMenuPanel} <div key="inner" className={`mainView-innerContent${this.colorScheme}`}> {this.flyout} <div className="mainView-libraryHandle" style={{ left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? 'none' : undefined }} onPointerDown={this.onFlyoutPointerDown}> @@ -759,9 +772,11 @@ export class MainView extends React.Component { <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}> {this.dockingContent} - <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}> - <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? 'chevron-left' : 'chevron-right'} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" /> - </div> + {this._hideUI ? null : ( + <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}> + <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? 'chevron-left' : 'chevron-right'} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" /> + </div> + )} <div className="properties-container" style={{ width: this.propertiesWidth() }}> {this.propertiesWidth() < 10 ? null : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={this.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />} </div> @@ -962,7 +977,7 @@ export class MainView extends React.Component { <GoogleAuthenticationManager /> <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfHeaderBarDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} /> <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} /> - <TopBar /> + {this._hideUI ? null : <TopBar />} {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null} {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => (DocumentLinksButton.LinkEditorDocView = undefined))} docView={DocumentLinksButton.LinkEditorDocView} /> : null} {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : null} @@ -973,7 +988,7 @@ export class MainView extends React.Component { default: return ( <> - <div style={{ position: 'relative', display: LightboxView.LightboxDoc ? 'none' : undefined, zIndex: 1999 }}> + <div style={{ position: 'relative', display: this._hideUI || LightboxView.LightboxDoc ? 'none' : undefined, zIndex: 1999 }}> <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} /> </div> {this.mainDashboardArea} diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx index 0f63ebc1d..9d89ee036 100644 --- a/src/client/views/PropertiesDocContextSelector.tsx +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -40,7 +40,7 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC .keys() ); return doclayouts - .filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)) + .filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance?.props.Document)) .filter(doc => !Doc.IsSystem(doc)) .map(doc => ({ col: doc, target })); } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 42f9bb981..ed9054107 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -29,7 +29,7 @@ const _global = (window /* browser */ || global) /* node */ as any; @observer export class CollectionDockingView extends CollectionSubView() { - @observable public static Instance: CollectionDockingView; + @observable public static Instance: CollectionDockingView | undefined; public static makeDocumentConfig(document: Doc, panelName?: string, width?: number) { return { type: 'react-component', @@ -103,12 +103,14 @@ export class CollectionDockingView extends CollectionSubView() { @undoBatch public static CloseSplit(document: Opt<Doc>, panelName?: string): boolean { - const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find(tab => (panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document)); - if (tab) { - const j = tab.header.parent.contentItems.indexOf(tab.contentItem); - if (j !== -1) { - tab.header.parent.contentItems[j].remove(); - return CollectionDockingView.Instance.layoutChanged(); + if (CollectionDockingView.Instance) { + const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find(tab => (panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document)); + if (tab) { + const j = tab.header.parent.contentItems.indexOf(tab.contentItem); + if (j !== -1) { + tab.header.parent.contentItems[j].remove(); + return CollectionDockingView.Instance.layoutChanged(); + } } } @@ -119,19 +121,21 @@ export class CollectionDockingView extends CollectionSubView() { public static OpenFullScreen(doc: Doc) { SelectionManager.DeselectAll(); const instance = CollectionDockingView.Instance; - if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === 'layout') { - return DashboardView.openDashboard(doc); + if (instance) { + if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === 'layout') { + return DashboardView.openDashboard(doc); + } + const newItemStackConfig = { + type: 'stack', + content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc))], + }; + const docconfig = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); + instance._goldenLayout.root.contentItems[0].addChild(docconfig); + docconfig.callDownwards('_$init'); + instance._goldenLayout._$maximiseItem(docconfig); + instance._goldenLayout.emit('stateChanged'); + instance.stateChanged(); } - const newItemStackConfig = { - type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc))], - }; - const docconfig = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); - instance._goldenLayout.root.contentItems[0].addChild(docconfig); - docconfig.callDownwards('_$init'); - instance._goldenLayout._$maximiseItem(docconfig); - instance._goldenLayout.emit('stateChanged'); - instance.stateChanged(); return true; } @@ -146,21 +150,23 @@ export class CollectionDockingView extends CollectionSubView() { const newContentItem = stack.layoutManager.createContentItem(newConfig, instance._goldenLayout); stack.addChild(newContentItem.contentItems[0], undefined); stack.contentItems[activeContentItemIndex].remove(); - return CollectionDockingView.Instance.layoutChanged(); + return instance.layoutChanged(); } - const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find(tab => tab.contentItem.config.props.panelName === panelName); + const tab = Array.from(instance.tabMap.keys()).find(tab => tab.contentItem.config.props.panelName === panelName); if (tab) { tab.header.parent.addChild(newConfig, undefined); const j = tab.header.parent.contentItems.indexOf(tab.contentItem); !addToSplit && j !== -1 && tab.header.parent.contentItems[j].remove(); - return CollectionDockingView.Instance.layoutChanged(); + return instance.layoutChanged(); } return CollectionDockingView.AddSplit(document, panelName, stack, panelName); } @undoBatch public static ToggleSplit(doc: Doc, location: string, stack?: any, panelName?: string) { - return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex(tab => tab.DashDoc === doc) !== -1 ? CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, location, stack, panelName); + return CollectionDockingView.Instance && Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex(tab => tab.DashDoc === doc) !== -1 + ? CollectionDockingView.CloseSplit(doc) + : CollectionDockingView.AddSplit(doc, location, stack, panelName); } // @@ -170,7 +176,7 @@ export class CollectionDockingView extends CollectionSubView() { @action public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) { if (document._viewType === CollectionViewType.Docking) return DashboardView.openDashboard(document); - + if (!CollectionDockingView.Instance) return false; const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document); if (tab) { tab.header.parent.setActiveContentItem(tab.contentItem); @@ -453,13 +459,15 @@ export class CollectionDockingView extends CollectionSubView() { Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc); Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true); } - const dview = CollectionDockingView.Instance.props.Document; - const fieldKey = CollectionDockingView.Instance.props.fieldKey; - Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc); - this.tabMap.delete(tab); - tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); - tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele)); - this.stateChanged(); + if (CollectionDockingView.Instance) { + const dview = CollectionDockingView.Instance.props.Document; + const fieldKey = CollectionDockingView.Instance.props.fieldKey; + Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc); + this.tabMap.delete(tab); + tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); + tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele)); + this.stateChanged(); + } }; tabCreated = (tab: any) => { this.tabMap.add(tab); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 2ab5f6247..f0cb23eab 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -225,7 +225,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab }, }) ); - DocListCast(Cast(Doc.UserDoc()['clickFuncs-child'], Doc, null).data).forEach(childClick => + DocListCast(Cast(Doc.UserDoc()['clickFuncs-child'], Doc, null)?.data).forEach(childClick => onClicks.push({ description: `Set child ${childClick.title}`, icon: 'edit', diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index b8aaea622..2d08b1c09 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -302,6 +302,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array }); if ( + CollectionDockingView.Instance && !Array.from(CollectionDockingView.Instance.tabMap) .map(d => d.DashDoc) .includes(curPres) @@ -420,7 +421,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { ScreenToLocalTransform = () => { this._forceInvalidateScreenToLocal; const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0] as HTMLElement); - return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY); + return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY) ?? Transform.Identity(); }; PanelWidth = () => this._panelWidth; PanelHeight = () => this._panelHeight; @@ -449,9 +450,9 @@ export class TabDocView extends React.Component<TabDocViewProps> { PanelWidth={this.PanelWidth} PanelHeight={this.PanelHeight} styleProvider={DefaultStyleProvider} - docFilters={CollectionDockingView.Instance.childDocFilters} - docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters} - searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} + docFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist} + docRangeFilters={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist} + searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist} addDocument={undefined} removeDocument={this.remDocTab} addDocTab={this.addDocTab} @@ -624,9 +625,9 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> { styleProvider={TabMinimapView.miniStyleProvider} addDocTab={this.props.addDocTab} pinToPres={TabDocView.PinDoc} - docFilters={CollectionDockingView.Instance.childDocFilters} - docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters} - searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} + docFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist} + docRangeFilters={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist} + searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist} fitContentsToBox={returnTrue} /> <div className="miniOverlay" onPointerDown={this.miniDown}> diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index bf4c029b2..c4b42301e 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -1,6 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api'; -import { action, computed, IReactionDisposer, observable, ObservableMap } from 'mobx'; +import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc'; @@ -12,6 +12,7 @@ import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Uti import { Docs } from '../../../documents/Documents'; import { DragManager } from '../../../util/DragManager'; import { SnappingManager } from '../../../util/SnappingManager'; +import { UndoManager } from '../../../util/UndoManager'; import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; @@ -368,23 +369,27 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps setupMoveUpEvents( this, e, - (e, down, delta) => { - const localDelta = this.props - .ScreenToLocalTransform() - .scale(this.props.scaling?.() || 1) - .transformDirection(delta[0], delta[1]); - const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']); - const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); - const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth; - if (ratio >= 1) { - this.layoutDoc.nativeWidth = nativeWidth * ratio; - this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]; - this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; - } - return false; - }, + (e, down, delta) => + runInAction(() => { + const localDelta = this.props + .ScreenToLocalTransform() + .scale(this.props.scaling?.() || 1) + .transformDirection(delta[0], delta[1]); + const fullWidth = this.layoutDoc[WidthSym](); + const mapWidth = fullWidth - this.sidebarWidth(); + if (this.sidebarWidth() + localDelta[0] > 0) { + this._showSidebar = true; + this.layoutDoc._width = fullWidth + localDelta[0]; + this.layoutDoc._sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%'; + } else { + this._showSidebar = false; + this.layoutDoc._width = mapWidth; + this.layoutDoc._sidebarWidthPercent = '0%'; + } + return false; + }), emptyFunction, - this.toggleSidebar + () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map') ); }; @@ -470,13 +475,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps // TODO: Adding highlight box layer to Maps @action toggleSidebar = () => { + //1.2 * w * ? = .2 * w .2/1.2 const prevWidth = this.sidebarWidth(); - this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%'; - this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); + this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%'; + this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); }; sidebarDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), false); + setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true); }; sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => { const bounds = this._ref.current!.getBoundingClientRect(); @@ -605,7 +611,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps {this.annotationLayer} <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}> <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}> - <input className="mapBox-input" ref={this.inputRef} type="text" placeholder="Enter location" /> + <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" /> </Autocomplete> {this.renderMarkers()} diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 5b9d90780..f41f6a1ad 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -13,20 +13,19 @@ import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { KeyCodes } from '../../util/KeyCodes'; import { undoBatch, UndoManager } from '../../util/UndoManager'; +import { CollectionFreeFormView } from '../collections/collectionFreeForm'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; import { Colors } from '../global/globalEnums'; import { CreateImage } from '../nodes/WebBoxRenderer'; -import { AnchorMenu } from '../pdf/AnchorMenu'; import { PDFViewer } from '../pdf/PDFViewer'; import { SidebarAnnos } from '../SidebarAnnos'; import { FieldView, FieldViewProps } from './FieldView'; import { ImageBox } from './ImageBox'; -import './PDFBox.scss'; import { VideoBox } from './VideoBox'; +import './PDFBox.scss'; import React = require('react'); -import { CollectionFreeFormView } from '../collections/collectionFreeForm'; @observer export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index cfaa428f9..e945920da 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -640,7 +640,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); }; sidebarDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), false); + setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true); }; sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => { const bounds = this._ref.current!.getBoundingClientRect(); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 292c187e4..6e5eb3300 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -399,7 +399,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { const presCollection = Cast(this.layoutDoc.presCollection, Doc, null); const collectionDocView = presCollection ? DocumentManager.Instance.getDocumentView(presCollection) : undefined; const includesDoc: boolean = DocListCast(presCollection?.data).includes(targetDoc); - const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === srcContext); + const tab = CollectionDockingView.Instance && Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === srcContext); this.turnOffEdit(); // Handles the setting of presCollection if (includesDoc) { diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 0bf1575fb..3fc0a237e 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -26,7 +26,7 @@ import './TopBar.scss'; @observer export class TopBar extends React.Component { navigateToHome = () => { - CollectionDockingView.Instance.CaptureThumbnail()?.then(() => { + CollectionDockingView.Instance?.CaptureThumbnail()?.then(() => { Doc.ActivePage = 'home'; DashboardView.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use }); |
