aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/DocServer.ts4
-rw-r--r--src/client/util/CurrentUserUtils.ts18
-rw-r--r--src/client/util/GroupManager.tsx9
-rw-r--r--src/client/util/SettingsManager.tsx3
-rw-r--r--src/client/util/SharingManager.tsx3
-rw-r--r--src/client/util/TrackMovements.ts141
-rw-r--r--src/client/views/ContextMenu.scss22
-rw-r--r--src/client/views/ContextMenuItem.tsx84
-rw-r--r--src/client/views/DashboardView.tsx2
-rw-r--r--src/client/views/GlobalKeyHandler.ts4
-rw-r--r--src/client/views/Main.tsx51
-rw-r--r--src/client/views/MainView.tsx53
-rw-r--r--src/client/views/PropertiesDocContextSelector.tsx2
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx70
-rw-r--r--src/client/views/collections/CollectionView.tsx2
-rw-r--r--src/client/views/collections/TabDocView.tsx15
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx48
-rw-r--r--src/client/views/nodes/PDFBox.tsx5
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx2
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx2
-rw-r--r--src/client/views/topbar/TopBar.tsx2
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
});