aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/CurrentUserUtils.ts349
-rw-r--r--src/client/util/DocumentManager.ts184
-rw-r--r--src/client/util/History.ts45
-rw-r--r--src/client/util/LinkFollower.ts112
-rw-r--r--src/client/util/LinkManager.ts259
-rw-r--r--src/client/util/SelectionManager.ts29
-rw-r--r--src/client/util/SettingsManager.tsx471
-rw-r--r--src/client/util/SharingManager.tsx11
8 files changed, 685 insertions, 775 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 84efcb966..02d43088d 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -2,7 +2,6 @@ import { computed, observable, reaction } from "mobx";
import * as rp from 'request-promise';
import { DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
import { Id } from "../../fields/FieldSymbols";
-import { InkTool } from "../../fields/InkField";
import { List } from "../../fields/List";
import { PrefetchProxy } from "../../fields/Proxy";
import { RichTextField } from "../../fields/RichTextField";
@@ -14,11 +13,12 @@ import { SharingPermissions } from "../../fields/util";
import { OmitKeys, Utils } from "../../Utils";
import { DocServer } from "../DocServer";
import { Docs, DocumentOptions, DocUtils, FInfo } from "../documents/Documents";
-import { DocumentType } from "../documents/DocumentTypes";
+import { CollectionViewType, DocumentType } from "../documents/DocumentTypes";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { TreeViewType } from "../views/collections/CollectionTreeView";
-import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
+import { CollectionView } from "../views/collections/CollectionView";
import { TreeView } from "../views/collections/TreeView";
+import { DashboardView } from "../views/DashboardView";
import { Colors } from "../views/global/globalEnums";
import { MainView } from "../views/MainView";
import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
@@ -57,59 +57,6 @@ interface Button {
export let resolvedPorts: { server: number, socket: number };
export class CurrentUserUtils {
- private static curr_id: string;
- //TODO tfs: these should be temporary...
- private static mainDocId: string | undefined;
-
- public static get id() { return this.curr_id; }
- public static get MainDocId() { return this.mainDocId; }
- public static set MainDocId(id: string | undefined) { this.mainDocId = id; }
- @computed public static get UserDocument() { return Doc.UserDoc(); }
-
- @observable public static GuestTarget: Doc | undefined;
- @observable public static GuestDashboard: Doc | undefined;
- @observable public static GuestMobile: Doc | undefined;
- @observable public static propertiesWidth: number = 0;
- @observable public static headerBarHeight: number = 0;
- @observable public static searchPanelWidth: number = 0;
-
- static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, funcs?:{[key:string]: string}) {
- scripts && Object.keys(scripts).map(key => {
- if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) {
- doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name, value:"any", scriptContext: "any", documentView:Doc.name}, {"_readOnly_": true});
- }
- });
- funcs && Object.keys(funcs).map(key => {
- const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
- if (ScriptCast(cfield)?.script.originalScript !== funcs[key] && funcs[key]) {
- doc[key] = ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, {"_readOnly_": true});
- }
- });
- return doc;
- }
- static AssignOpts(doc:Doc|undefined, reqdOpts:DocumentOptions, items?:Doc[]) {
- if (doc) {
- const compareValues = (val1:any, val2:any) => {
- if (val1 instanceof List && val2 instanceof List && val1.length === val2.length) {
- return !val1.some(v => !val2.includes(v)) || !val2.some(v => val1.includes(v));
- }
- return val1 === val2;
- }
- Object.entries(reqdOpts).forEach(pair => {
- const targetDoc = pair[0].startsWith("_") ? doc : Doc.GetProto(doc as Doc);
- if (!Object.getOwnPropertyNames(targetDoc).includes(pair[0].replace(/^_/,"")) ||
- !compareValues(pair[1], targetDoc[pair[0]])) {
- targetDoc[pair[0]] = pair[1];
- }
- });
- items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), "data", item));
- items && DocListCast(doc.data).forEach(item => !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), "data", item));
- }
- return doc;
- }
- static AssignDocField(doc:Doc, field:string, creator:(reqdOpts:DocumentOptions, items?:Doc[]) => Doc, reqdOpts:DocumentOptions, items?: Doc[], scripts?:{[key:string]:string}, funcs?:{[key:string]:string}) {
- return this.AssignScripts(this.AssignOpts(DocCast(doc[field]), reqdOpts, items) ?? (doc[field] = creator(reqdOpts, items)), scripts, funcs);
- }
// initializes experimental advanced template views - slideView, headerView
static setupExperimentalTemplateButtons(doc: Doc, tempDocs?:Doc) {
@@ -139,12 +86,12 @@ export class CurrentUserUtils {
const reqdScripts = { onDragStart: '{ return copyDragFactory(this.dragFactory); }' };
const assignBtnAndTempOpts = (templateBtn:Opt<Doc>, btnOpts:DocumentOptions, templateOptions:DocumentOptions) => {
if (templateBtn) {
- this.AssignOpts(templateBtn,btnOpts);
- this.AssignDocField(templateBtn, "dragFactory", opts => template(opts), templateOptions);
+ DocUtils.AssignOpts(templateBtn,btnOpts);
+ DocUtils.AssignDocField(templateBtn, "dragFactory", opts => template(opts), templateOptions);
}
return templateBtn;
};
- return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? this.createToolButton( {...btnOpts, dragFactory: MakeTemplate(template(templateOpts))}), reqdScripts);
+ return DocUtils.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? this.createToolButton( {...btnOpts, dragFactory: MakeTemplate(template(templateOpts))}), reqdScripts);
});
const reqdOpts:DocumentOptions = {
@@ -154,7 +101,7 @@ export class CurrentUserUtils {
};
const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
const reqdFuncs = { hidden: "IsNoviceMode()" };
- return this.AssignScripts(this.AssignOpts(tempDocs, reqdOpts, requiredTypes) ?? Docs.Create.MasonryDocument(requiredTypes, reqdOpts), reqdScripts, reqdFuncs);
+ return DocUtils.AssignScripts(DocUtils.AssignOpts(tempDocs, reqdOpts, requiredTypes) ?? Docs.Create.MasonryDocument(requiredTypes, reqdOpts), reqdScripts, reqdFuncs);
}
/// Initializes templates that can be applied to notes
@@ -167,16 +114,16 @@ export class CurrentUserUtils {
const reqdNoteList = reqdTempOpts.map(opts => {
const reqdOpts = {...opts, title: "text", system: true};
const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined;
- return this.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note");
+ return DocUtils.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note");
});
const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, system: true };
- return this.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
+ return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
}
/// Initializes collection of templates for notes and click functions
static setupDocTemplates(doc: Doc, field="myTemplates") {
- this.AssignDocField(doc, "presElement", opts => Docs.Create.PresElementBoxDocument(opts), { title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"});
+ DocUtils.AssignDocField(doc, "presElement", opts => Docs.Create.PresElementBoxDocument(opts), { title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"});
const templates = [
DocCast(doc.presElement),
CurrentUserUtils.setupNoteTemplates(doc),
@@ -184,13 +131,13 @@ export class CurrentUserUtils {
];
const reqdOpts = { title: "template layouts", _xMargin: 0, system: true, };
const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
- return this.AssignDocField(doc, field, (opts,items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, templates, reqdScripts);
+ return DocUtils.AssignDocField(doc, field, (opts,items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, templates, reqdScripts);
}
// setup templates for different document types when they are iconified from Document Decorations
static setupDefaultIconTemplates(doc: Doc, field="template-icons") {
const reqdOpts = { title: "icon templates", _height: 75, system: true };
- const templateIconsDoc = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
+ const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
const makeIconTemplate = (type: DocumentType | undefined, templateField: string, opts:DocumentOptions) => {
const iconFieldName = "icon" + (type ? "_" + type : "");
@@ -201,8 +148,8 @@ export class CurrentUserUtils {
case DocumentType.FONTICON: creator = fontBox; break;
}
const allopts = {system: true, ...opts};
- return this.AssignScripts( (curIcon?.iconTemplate === opts.iconTemplate ?
- this.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))),
+ return DocUtils.AssignScripts( (curIcon?.iconTemplate === opts.iconTemplate ?
+ DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))),
{onClick:"deiconifyView(documentView)"});
};
const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({
@@ -224,7 +171,7 @@ export class CurrentUserUtils {
makeIconTemplate("transcription" as any, "transcription", { iconTemplate:DocumentType.LABEL, backgroundColor: "orange" }),
//makeIconTemplate(DocumentType.PDF, "icon", {iconTemplate:DocumentType.IMG}, (opts) => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", opts))
].filter(d => d).map(d => d!);
- this.AssignOpts(DocCast(doc[field]), {}, iconTemplates);
+ DocUtils.AssignOpts(DocCast(doc[field]), {}, iconTemplates);
}
/// initalizes the set of "empty<DocType>" versions of each document type with default fields. e.g.,. emptyNote, emptyPresentation
@@ -297,7 +244,7 @@ export class CurrentUserUtils {
}, funcs: {title: 'self.text?.Text'}},
];
- emptyThings.forEach(thing => this.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, undefined, thing.funcs));
+ emptyThings.forEach(thing => DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, undefined, thing.funcs));
return [
{ toolTip: "Tap or drag to create a note", title: "Note", icon: "sticky-note", dragFactory: doc.emptyNote as Doc, },
@@ -326,7 +273,7 @@ export class CurrentUserUtils {
btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true,
_removeDropProperties: new List<string>(["_stayInCollection"]),
};
- return this.AssignScripts(this.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
+ return DocUtils.AssignScripts(DocUtils.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
});
const reqdOpts:DocumentOptions = {
@@ -335,7 +282,7 @@ export class CurrentUserUtils {
childDocumentsActive: true
};
const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
- return this.AssignScripts(this.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
+ return DocUtils.AssignScripts(DocUtils.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
}
/// returns descriptions needed to buttons for the left sidebar to open up panes displaying different collections of documents
@@ -348,7 +295,7 @@ export class CurrentUserUtils {
{ title: "Tools", target: this.setupToolsBtnPanel(doc, "myTools"), icon: "wrench", funcs: {hidden: "IsNoviceMode()"} },
{ title: "Imports", target: this.setupImportSidebar(doc, "myImports"), icon: "upload", },
{ title: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), icon: "archive", },
- { title: "Shared Docs", target: this.MySharedDocs, icon: "users", funcs:{badgeValue:badgeValue}},
+ { title: "Shared Docs", target: Doc.MySharedDocs, icon: "users", funcs:{badgeValue:badgeValue}},
{ title: "Trails", target: this.setupTrails(doc, "myTrails"), icon: "pres-trail", },
{ title: "User Doc View", target: this.setupUserDocView(doc, "myUserDocView"), icon: "address-card",funcs: {hidden: "IsNoviceMode()"} },
].map(tuple => ({...tuple, scripts:{onClick: 'selectMainMenu(self)'}}));
@@ -356,7 +303,7 @@ export class CurrentUserUtils {
/// the empty panel that is filled with whichever left menu button's panel has been selected
static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") {
- this.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.system = true; return doc;})(new Doc()), {system:true});
+ DocUtils.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.system = true; return doc;})(new Doc()), {system:true});
}
/// Initializes the left sidebar menu buttons and the panels they open up
@@ -370,20 +317,20 @@ export class CurrentUserUtils {
_width: 60, _height: 60, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, _dropAction: "alias",
_removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
};
- return this.AssignScripts(this.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
+ return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
});
const reqdStackOpts:DocumentOptions ={
title: "menuItemPanel", childDropAction: "alias", backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)", dontRegisterView: true, ignoreClick: true,
_chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true
};
- return this.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
+ return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
}
// Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu
static setupActiveMobileMenu(doc: Doc, field="activeMobileMenu") {
const reqdOpts = { _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, system: true, _chromeHidden: true,};
- this.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(this.setupMobileButtons(), opts), reqdOpts);
+ DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(this.setupMobileButtons(), opts), reqdOpts);
}
// Sets up mobile buttons for inside mobile menu
@@ -436,42 +383,6 @@ export class CurrentUserUtils {
}) as any as Doc
- static setupThumbButtons(doc: Doc) {
- const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, clipboard?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
- { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue" },
- { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow" },
- { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, system: true }), backgroundColor: "orange" },
- { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: "orange" },
- { title: "ignore gestures", icon: "signature", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: "green" },
- ];
- return docProtoData.map(data => Docs.Create.FontIconDocument({
- _nativeWidth: 10, _nativeHeight: 10, _width: 10, _height: 10, title: data.title, icon: data.icon,
- _dropAction: data.pointerDown ? "copy" : undefined,
- ignoreClick: data.ignoreClick,
- onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
- clipboard: data.clipboard,
- onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined,
- onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
- backgroundColor: data.backgroundColor,
- _removeDropProperties: new List<string>(["dropAction"]),
- dragFactory: data.dragFactory,
- system: true
- }));
- }
-
- static setupThumbDoc(userDoc: Doc) {
- if (!userDoc.thumbDoc) {
- const thumbDoc = Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), {
- _width: 100, _height: 50, ignoreClick: true, _lockedPosition: true, title: "buttons",
- _autoHeight: true, _yMargin: 5, linearViewIsExpanded: true, backgroundColor: "white", system: true
- });
- thumbDoc.inkToTextDoc = Docs.Create.LinearDocument([], {
- _width: 300, _height: 25, _autoHeight: true, linearViewIsExpanded: true, flexDirection: "column", system: true
- });
- userDoc.thumbDoc = thumbDoc;
- }
- return Cast(userDoc.thumbDoc, Doc);
- }
static setupMobileInkingDoc(userDoc: Doc) {
return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white", system: true });
@@ -492,7 +403,7 @@ export class CurrentUserUtils {
/// Search option on the left side button panel
static setupSearcher(doc: Doc, field:string) {
- return this.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), {
+ return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), {
dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", system: true, childDropAction: "alias",
_lockedPosition: true, _viewType: CollectionViewType.Schema, _searchDoc: true, });
}
@@ -506,7 +417,7 @@ export class CurrentUserUtils {
title: "My Tools", system: true, ignoreClick: true, boxShadow: "0 0",
_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true,
};
- return this.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]);
+ return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]);
}
/// initializes the left sidebar dashboard pane
@@ -517,7 +428,7 @@ export class CurrentUserUtils {
const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true,
title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", system: true };
const reqdBtnScript = {onClick: newDashboard,}
- const newDashboardButton = this.AssignScripts(this.AssignOpts(DocCast(myDashboards?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
+ const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
const reqdOpts:DocumentOptions = {
title: "My Dashboards", childHideLinkButton: true, freezeChildren: "remove|add", treeViewHideTitle: true, boxShadow: "0 0", childDontRegisterViews: true,
@@ -530,7 +441,7 @@ export class CurrentUserUtils {
childContextMenuIcons: new List<string>(["chalkboard", "tv", "camera", "users", "times"]), // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files."
};
- myDashboards = this.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
+ myDashboards = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`;
const contextMenuScripts = [newDashboard];
const childContextMenuScripts = [toggleDarkTheme, `toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(self)`, 'removeDashboard(self)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters
@@ -553,7 +464,7 @@ export class CurrentUserUtils {
const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true,
title: "New trail", toolTip: "Create new trail", btnType: ButtonType.ClickButton, buttonText: "New trail", icon: "plus", system: true };
const reqdBtnScript = {onClick: `createNewPresentation()`};
- const newTrailButton = this.AssignScripts(this.AssignOpts(DocCast(myTrails?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
+ const newTrailButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myTrails?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
const reqdOpts:DocumentOptions = {
title: "My Trails", _showTitle: "title", _height: 100,
@@ -564,7 +475,7 @@ export class CurrentUserUtils {
_lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true,
explainer: "All of the trails that you have created will appear here."
};
- myTrails = this.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
+ myTrails = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
const contextMenuScripts = [reqdBtnScript.onClick];
if (Cast(myTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) {
myTrails.contextMenuScripts = new List<ScriptField>(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
@@ -575,7 +486,7 @@ export class CurrentUserUtils {
/// initializes the left sidebar File system pane
static setupFilesystem(doc: Doc, field:string) {
var myFilesystem = DocCast(doc[field]);
- const myFileOrphans = this.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
+ const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
const newFolder = `makeTopLevelFolder()`;
const newFolderOpts: DocumentOptions = {
@@ -583,7 +494,7 @@ export class CurrentUserUtils {
title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", system: true
};
const newFolderScript = { onClick: newFolder};
- const newFolderButton = this.AssignScripts(this.AssignOpts(DocCast(myFilesystem?.buttonMenuDoc), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
+ const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.buttonMenuDoc), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
const reqdOpts:DocumentOptions = { _showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true,
title: "My Documents", buttonMenu: true, buttonMenuDoc: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", system: true,
@@ -593,7 +504,7 @@ export class CurrentUserUtils {
childContextMenuIcons: new List<string>(["plus"]),
explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
};
- myFilesystem = this.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [myFileOrphans]);
+ myFilesystem = DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [myFileOrphans]);
const childContextMenuScripts = [newFolder];
if (Cast(myFilesystem.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) {
myFilesystem.childContextMenuScripts = new List<ScriptField>(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
@@ -610,13 +521,13 @@ export class CurrentUserUtils {
contextMenuIcons:new List<string>(["trash"]),
explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
};
- const recentlyClosed = this.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
+ const recentlyClosed = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
const clearAll = (target:string) => `getProto(${target}).data = new List([])`;
const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _hideContextMenu: true,
title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton, buttonText: "Empty", icon: "trash", system: true,
toolTip: "Empty recently closed",};
- const clearDocsButton = this.AssignDocField(recentlyClosed, "clearDocsBtn", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
+ const clearDocsButton = DocUtils.AssignDocField(recentlyClosed, "clearDocsBtn", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
if (recentlyClosed.buttonMenuDoc !== clearDocsButton) Doc.GetProto(recentlyClosed).buttonMenuDoc = clearDocsButton;
@@ -626,19 +537,6 @@ export class CurrentUserUtils {
return recentlyClosed;
}
- /// creates a new, empty filter doc
- static createFilterDoc() {
- const clearAll = `getProto(self).data = new List([])`;
- const reqdOpts:DocumentOptions = {
- _lockedPosition: true, _autoHeight: true, _fitWidth: true, _height: 150, _xPadding: 5, _yPadding: 5, _gridGap: 5, _forceActive: true,
- title: "Unnamed Filter", filterBoolean: "AND", boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, system: true,
- childDropAction: "none", treeViewHideTitle: true, treeViewTruncateTitleWidth: 150,
- childContextMenuLabels: new List<string>(["Clear All"]),
- childContextMenuScripts: new List<ScriptField>([ScriptField.MakeFunction(clearAll)!]),
- };
- return Docs.Create.FilterDocument(reqdOpts);
- }
-
/// initializes the left sidebar panel view of the UserDoc
static setupUserDocView(doc: Doc, field:string) {
const reqdOpts:DocumentOptions = {
@@ -646,8 +544,8 @@ export class CurrentUserUtils {
boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, system: true,
treeViewHideTitle: true, treeViewTruncateTitleWidth: 150
};
- if (!doc[field]) this.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" });
- return this.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [doc]);
+ if (!doc[field]) DocUtils.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" });
+ return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [doc]);
}
static linearButtonList = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, {
@@ -666,7 +564,7 @@ export class CurrentUserUtils {
static setupDockedButtons(doc: Doc, field="myDockedBtns") {
const dockedBtns = DocCast(doc[field]);
const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string}) =>
- this.AssignScripts(this.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === opts.title), opts) ??
+ DocUtils.AssignScripts(DocUtils.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === opts.title), opts) ??
CurrentUserUtils.createToolButton(opts), scripts);
const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet
@@ -680,7 +578,7 @@ export class CurrentUserUtils {
};
reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
- return this.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns);
+ return DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns);
}
static textTools():Button[] {
@@ -768,13 +666,13 @@ export class CurrentUserUtils {
...params.funcs,
backgroundColor: params.scripts?.onClick /// a bit hacky. if onClick is set, then we assume it returns a color value when queried with '_readOnly_'. This will be true for toggle buttons, but not generally
}
- return this.AssignScripts(this.AssignOpts(btnDoc, reqdOpts) ?? Docs.Create.FontIconDocument(reqdOpts), params.scripts, reqdFuncs);
+ return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdOpts) ?? Docs.Create.FontIconDocument(reqdOpts), params.scripts, reqdFuncs);
}
/// Initializes all the default buttons for the top bar context menu
static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
const reqdCtxtOpts = { title: "context menu buttons", flexGap: 0, childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
- const ctxtMenuBtnsDoc = this.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
+ const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
if (!params.subMenu) {
@@ -786,20 +684,20 @@ export class CurrentUserUtils {
const items = params.subMenu?.map(sub =>
this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title))
);
- return this.AssignScripts(
- this.AssignDocField(ctxtMenuBtnsDoc, StrCast(params.title), (opts) => this.linearButtonList(opts, items??[]), reqdSubMenuOpts, items), undefined, params.funcs);
+ return DocUtils.AssignScripts(
+ DocUtils.AssignDocField(ctxtMenuBtnsDoc, StrCast(params.title), (opts) => this.linearButtonList(opts, items??[]), reqdSubMenuOpts, items), undefined, params.funcs);
}
});
- return this.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns);
+ return DocUtils.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns);
}
/// collection of documents rendered in the overlay layer above all tabs and other UI
static setupOverlays(doc: Doc, field = "myOverlayDocs") {
- return this.AssignDocField(doc, field, (opts) => Docs.Create.FreeformDocument([], opts), { title: "overlay documents", backgroundColor: "#aca3a6", system: true });
+ return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.FreeformDocument([], opts), { title: "overlay documents", backgroundColor: "#aca3a6", system: true });
}
static setupPublished(doc:Doc, field = "myPublishedDocs") {
- return this.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), { title: "published docs", backgroundColor: "#aca3a6", system: true });
+ return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), { title: "published docs", backgroundColor: "#aca3a6", system: true });
}
/// The database of all links on all documents
@@ -838,7 +736,7 @@ export class CurrentUserUtils {
explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
};
- this.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts);
+ DocUtils.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts);
}
/// Import option on the left side button panel
@@ -849,12 +747,12 @@ export class CurrentUserUtils {
childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go."
};
- const myImports = this.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), reqdOpts);
+ const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), reqdOpts);
const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer",
_width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton,
buttonText: "Import", icon: "upload", system: true };
- this.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" });
+ DocUtils.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" });
return myImports;
}
@@ -909,7 +807,7 @@ export class CurrentUserUtils {
/// written to the server if the code hasn't changed. However, choices need to be made for each Doc/field
/// whether to revert to "default" values, or to leave them as the user/system last set them.
static updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
- this.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {});
+ DocUtils.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {});
reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data-lastModified"]),
async () => {
const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
@@ -948,8 +846,8 @@ export class CurrentUserUtils {
this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
this.setupDocTemplates(doc); // sets up the template menu of templates
this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
- this.AssignDocField(doc, "globalScriptDatabase", (opts) => Docs.Prototypes.MainScriptDocument(), {});
- this.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
+ DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) => Docs.Prototypes.MainScriptDocument(), {});
+ DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
if (doc.activeDashboard instanceof Doc) {
// undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
@@ -962,7 +860,7 @@ export class CurrentUserUtils {
}
static setupFieldInfos(doc:Doc, field="fieldInfos") {
const fieldInfoOpts = { title: "Field Infos", system: true}; // bcz: all possible document options have associated field infos which are stored onn the FieldInfos document **except for title and system which are used as part of the definition of the fieldInfos object
- const infos = this.AssignDocField(doc, field, opts => Doc.assign(new Doc(), opts as any), fieldInfoOpts);
+ const infos = DocUtils.AssignDocField(doc, field, opts => Doc.assign(new Doc(), opts as any), fieldInfoOpts);
const entries = Object.entries(new DocumentOptions());
entries.forEach(pair => {
if (!Array.from(Object.keys(fieldInfoOpts)).includes(pair[0])) {
@@ -974,7 +872,7 @@ export class CurrentUserUtils {
case Doc.name: opts.fieldValues = new List<Doc>(options.values as any); break;
default: opts.fieldValues = new List<string>(options.values as any); break;// string, pointerEvents, dimUnit, dropActionType
}
- this.AssignDocField(infos, pair[0], opts => Doc.assign(new Doc(), OmitKeys(opts,["values"]).omit), opts);
+ DocUtils.AssignDocField(infos, pair[0], opts => Doc.assign(new Doc(), OmitKeys(opts,["values"]).omit), opts);
}
});
}
@@ -995,7 +893,6 @@ export class CurrentUserUtils {
}
public static async loadUserDocument(id: string) {
- this.curr_id = id;
await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => {
const { userDocumentId, sharingDocumentId, linkDatabaseId } = JSON.parse(ids);
if (userDocumentId !== "guest") {
@@ -1012,46 +909,6 @@ export class CurrentUserUtils {
});
}
- public static _urlState: HistoryUtil.DocUrl;
-
- /// opens a dashboard as the ActiveDashboard (and adds the dashboard to the users list of dashboards if it's not already there).
- /// this also sets the readonly state of the dashboard based on the current mode of dash (from its url)
- public static openDashboard = (doc: Doc|undefined, fromHistory = false) => {
- if (!doc) return false;
- CurrentUserUtils.MainDocId = doc[Id];
- Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc);
-
- // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
- Doc.UserDoc() ? (CurrentUserUtils.ActiveDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
-
- const state = CurrentUserUtils._urlState;
- if (state.sharing === true && !Doc.UserDoc()) {
- DocServer.Control.makeReadOnly();
- } else {
- fromHistory || HistoryUtil.pushState({
- type: "doc",
- docId: doc[Id],
- readonly: state.readonly,
- nro: state.nro,
- sharing: false,
- });
- if (state.readonly === true || state.readonly === null) {
- DocServer.Control.makeReadOnly();
- } else if (state.safe) {
- if (!state.nro) {
- DocServer.Control.makeReadOnly();
- }
- CollectionView.SetSafeMode(true);
- } else if (state.nro || state.nro === null || state.readonly === false) {
- } else if (doc.readOnly) {
- DocServer.Control.makeReadOnly();
- } else {
- DocServer.Control.makeEditable();
- }
- }
-
- return true;
- }
public static importDocument = () => {
const input = document.createElement("input");
@@ -1068,7 +925,7 @@ export class CurrentUserUtils {
// setTimeout(() => SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs =>
// docs.docs.forEach(d => LinkManager.Instance.addLink(d))), 2000); // need to give solr some time to update so that this query will find any link docs we've added.
// }
- const list = Cast(CurrentUserUtils.MyImports.data, listSpec(Doc), null);
+ const list = Cast(Doc.MyImports.data, listSpec(Doc), null);
doc instanceof Doc && list?.splice(0, 0, doc);
} else if (input.files && input.files.length !== 0) {
const disposer = OverlayView.ShowSpinner();
@@ -1076,7 +933,7 @@ export class CurrentUserUtils {
if (results.length !== input.files?.length) {
alert("Error uploading files - possibly due to unsupported file types");
}
- const list = Cast(CurrentUserUtils.MyImports.data, listSpec(Doc), null);
+ const list = Cast(Doc.MyImports.data, listSpec(Doc), null);
list?.splice(0, 0, ...results);
disposer();
} else {
@@ -1085,104 +942,20 @@ export class CurrentUserUtils {
};
input.click();
}
-
- public static snapshotDashboard() { return CollectionDockingView.TakeSnapshot(CurrentUserUtils.ActiveDashboard); }
-
- public static closeActiveDashboard = () => { CurrentUserUtils.ActiveDashboard = undefined; }
-
- public static removeDashboard = async (dashboard:Doc) => {
- const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data);
- if (dashboards?.length) {
- if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard));
- Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard);
- if (!dashboards.length) CurrentUserUtils.ActivePage = "home";
- }
- }
- public static createNewDashboard = (id?: string, name?: string) => {
- const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
- const dashboards = CurrentUserUtils.MyDashboards;
- const dashboardCount = DocListCast(dashboards.data).length + 1;
- const freeformOptions: DocumentOptions = {
- x: 0,
- y: 400,
- _width: 1500,
- _height: 1000,
- _fitWidth: true,
- _backgroundGridShow: true,
- title: `Untitled Tab 1`,
- };
- const title = name ? name : `Dashboard ${dashboardCount}`
- const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
- const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, "row");
- freeformDoc.context = dashboardDoc;
-
- // switching the tabs from the datadoc to the regular doc
- const dashboardTabs = DocListCast(dashboardDoc[DataSym].data);
- dashboardDoc.data = new List<Doc>(dashboardTabs);
- dashboardDoc["pane-count"] = 1;
-
- CurrentUserUtils.ActivePresentation = presentation;
-
- Doc.AddDocToList(dashboards, "data", dashboardDoc);
- // open this new dashboard
- CurrentUserUtils.ActiveDashboard = dashboardDoc;
- CurrentUserUtils.ActivePage = "dashboard";
- }
-
- public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) {
- const tbox = Docs.Create.TextDocument("", {
- _xMargin: noMargins ? 0 : undefined, _yMargin: noMargins ? 0 : undefined, annotationOn, docMaxAutoHeight: maxHeight, backgroundColor: backgroundColor,
- _width: width || 200, _height: 35, x: x, y: y, _fitWidth: true, _autoHeight: true, title
- });
- const template = Doc.UserDoc().defaultTextLayout;
- if (template instanceof Doc) {
- tbox._width = NumCast(template._width);
- tbox.layoutKey = "layout_" + StrCast(template.title);
- Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template;
- }
- return tbox;
- }
-
- public static get MyUserDocView() { return DocCast(Doc.UserDoc().myUserDocView); }
- public static get MyDockedBtns() { return DocCast(Doc.UserDoc().myDockedBtns); }
- public static get MySearcher() { return DocCast(Doc.UserDoc().mySearcher); }
- public static get MyFilesystem() { return DocCast(Doc.UserDoc().myFilesystem); }
- public static get MyHeaderBar() { return DocCast(Doc.UserDoc().myHeaderBar); }
- public static get MyTools() { return DocCast(Doc.UserDoc().myTools); }
- public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); }
- public static get MyFileOrphans() { return DocCast(Doc.UserDoc().myFileOrphans); }
- public static get MyTemplates() { return DocCast(Doc.UserDoc().myTemplates); }
- public static get MyLeftSidebarMenu() { return DocCast(Doc.UserDoc().myLeftSidebarMenu); }
- public static get MyLeftSidebarPanel() { return DocCast(Doc.UserDoc().myLeftSidebarPanel); }
- public static get MySharedDocs() { return DocCast(Doc.UserDoc().mySharedDocs); }
- public static get MyTrails() { return DocCast(Doc.UserDoc().myTrails); }
- public static get MyImports() { return DocCast(Doc.UserDoc().myImports); }
- public static get MyContextMenuBtns() { return DocCast(Doc.UserDoc().myContextMenuBtns); }
- public static get MyRecentlyClosed() { return DocCast(Doc.UserDoc().myRecentlyClosed); }
- public static get MyOverlayDocs() { return DocCast(Doc.UserDoc().myOverlayDocs); }
- public static get MyPublishedDocs() { return DocCast(Doc.UserDoc().myPublishedDocs); }
- public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); }
- public static set ActiveDashboard(val:Doc|undefined) { Doc.UserDoc().activeDashboard = val; }
- public static get ActivePresentation() { return DocCast(Doc.UserDoc().activePresentation); }
- public static set ActivePresentation(val) { Doc.UserDoc().activePresentation = val; }
- public static get ActivePage() { return StrCast(Doc.UserDoc().activePage); }
- public static set ActivePage(val) { Doc.UserDoc().activePage = val; }
- public static set ActiveTool(tool: InkTool) { Doc.UserDoc().activeTool = tool; }
- public static get ActiveTool(): InkTool { return StrCast(Doc.UserDoc().activeTool, InkTool.None) as InkTool; }
}
-ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs");
+ScriptingGlobals.add(function MySharedDocs() { return Doc.MySharedDocs; }, "document containing all shared Docs");
ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode");
ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering");
-ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(); }, "creates a snapshot copy of a dashboard");
-ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(); }, "creates a new dashboard when called");
+ScriptingGlobals.add(function snapshotDashboard() { DashboardView.snapshotDashboard(); }, "creates a snapshot copy of a dashboard");
+ScriptingGlobals.add(function createNewDashboard() { return DashboardView.createNewDashboard(); }, "creates a new dashboard when called");
ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, "creates a new presentation when called");
ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called");
ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)");
ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar");
ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { SharingManager.Instance.open(undefined, dashboard); }, "opens sharing dialog for Dashboard");
-ScriptingGlobals.add(function removeDashboard(dashboard: Doc) { CurrentUserUtils.removeDashboard(dashboard); }, "Remove Dashboard from Dashboards");
-ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { CurrentUserUtils.openDashboard( Doc.MakeAlias(dashboard)); }, "adds Dashboard to set of Dashboards");
+ScriptingGlobals.add(function removeDashboard(dashboard: Doc) { DashboardView.removeDashboard(dashboard); }, "Remove Dashboard from Dashboards");
+ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { DashboardView.openDashboard( Doc.MakeAlias(dashboard)); }, "adds Dashboard to set of Dashboards");
ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkContext?: boolean) {
let selected = (sel => checkContext ? DocCast(sel?.context) : sel)(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement());
return docType ? selected?.type === docType : colType ? selected?.viewType === colType : true;
@@ -1190,5 +963,5 @@ ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colTy
ScriptingGlobals.add(function makeTopLevelFolder() {
TreeView._editTitleOnLoad = { id: Utils.GenerateGuid(), parent: undefined };
const opts = { title: "Untitled folder", _stayInCollection: true, isFolder: true };
- return Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id));
+ return Doc.AddDocToList(Doc.MyFilesystem, "data", Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id));
}); \ No newline at end of file
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 8473ce703..d3ac2f03f 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -4,44 +4,43 @@ import { Id } from '../../fields/FieldSymbols';
import { Cast } from '../../fields/Types';
import { returnFalse } from '../../Utils';
import { DocumentType } from '../documents/DocumentTypes';
-import { CollectionDockingView } from '../views/collections/CollectionDockingView';
-import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
-import { CollectionView } from '../views/collections/CollectionView';
import { LightboxView } from '../views/LightboxView';
import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView';
import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
-import { CurrentUserUtils } from './CurrentUserUtils';
+import { CollectionDockingView } from '../views/collections/CollectionDockingView';
+import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
+import { CollectionView } from '../views/collections/CollectionView';
import { ScriptingGlobals } from './ScriptingGlobals';
import { SelectionManager } from './SelectionManager';
export class DocumentManager {
-
//global holds all of the nodes (regardless of which collection they're in)
@observable public DocumentViews = new Set<DocumentView>();
@observable public LinkAnchorBoxViews: DocumentView[] = [];
@observable public RecordingEvent = 0;
- @observable public LinkedDocumentViews: { a: DocumentView, b: DocumentView, l: Doc }[] = [];
+ @observable public LinkedDocumentViews: { a: DocumentView; b: DocumentView; l: Doc }[] = [];
private static _instance: DocumentManager;
- public static get Instance(): DocumentManager { return this._instance || (this._instance = new this()); }
+ public static get Instance(): DocumentManager {
+ return this._instance || (this._instance = new this());
+ }
//private constructor so no other class can create a nodemanager
- private constructor() { }
+ private constructor() {}
@action
public AddView = (view: DocumentView) => {
//console.log("MOUNT " + view.props.Document.title + "/" + view.props.LayoutTemplateString);
if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
- const viewAnchorIndex = view.props.LayoutTemplateString.includes("anchor2") ? "anchor2" : "anchor1";
+ const viewAnchorIndex = view.props.LayoutTemplateString.includes('anchor2') ? 'anchor2' : 'anchor1';
DocListCast(view.rootDoc.links).forEach(link => {
- this.LinkAnchorBoxViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)).
- forEach(otherView => this.LinkedDocumentViews.push(
- {
- a: viewAnchorIndex === "anchor2" ? otherView : view,
- b: viewAnchorIndex === "anchor2" ? view : otherView,
- l: link
- })
- );
+ this.LinkAnchorBoxViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)).forEach(otherView =>
+ this.LinkedDocumentViews.push({
+ a: viewAnchorIndex === 'anchor2' ? otherView : view,
+ b: viewAnchorIndex === 'anchor2' ? view : otherView,
+ l: link,
+ })
+ );
});
this.LinkAnchorBoxViews.push(view);
// this.LinkedDocumentViews.forEach(view => console.log(" LV = " + view.a.props.Document.title + "/" + view.a.props.LayoutTemplateString + " --> " +
@@ -49,14 +48,16 @@ export class DocumentManager {
} else {
this.DocumentViews.add(view);
}
- }
+ };
public RemoveView = action((view: DocumentView) => {
- this.LinkedDocumentViews.slice().forEach(action(pair => {
- if (pair.a === view || pair.b === view) {
- const li = this.LinkedDocumentViews.indexOf(pair);
- li !== -1 && this.LinkedDocumentViews.splice(li, 1);
- }
- }));
+ this.LinkedDocumentViews.slice().forEach(
+ action(pair => {
+ if (pair.a === view || pair.b === view) {
+ const li = this.LinkedDocumentViews.indexOf(pair);
+ li !== -1 && this.LinkedDocumentViews.splice(li, 1);
+ }
+ })
+ );
if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
const index = this.LinkAnchorBoxViews.indexOf(view);
@@ -125,11 +126,11 @@ export class DocumentManager {
const views: DocumentView[] = [];
Array.from(DocumentManager.Instance.DocumentViews).map(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view));
return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined);
- }
+ };
public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
const views = this.getDocumentViews(toFind).filter(view => view.rootDoc !== originatingDoc);
return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined);
- }
+ };
public getDocumentViews(toFind: Doc): DocumentView[] {
const toReturn: DocumentView[] = [];
const docViews = Array.from(DocumentManager.Instance.DocumentViews).filter(view => !LightboxView.IsLightboxDocView(view.docViewPath));
@@ -146,17 +147,16 @@ export class DocumentManager {
return toReturn;
}
-
static addView = (doc: Doc, finished?: () => void) => {
- CollectionDockingView.AddSplit(doc, "right");
+ CollectionDockingView.AddSplit(doc, 'right');
finished?.();
- }
+ };
public jumpToDocument = async (
- targetDoc: Doc, // document to display
- willZoom: boolean, // whether to zoom doc to take up most of screen
+ targetDoc: Doc, // document to display
+ willZoom: boolean, // whether to zoom doc to take up most of screen
createViewFunc = DocumentManager.addView, // how to create a view of the doc if it doesn't exist
- docContext: Doc[], // context to load that should contain the target
- linkDoc?: Doc, // link that's being followed
+ docContext: Doc[], // context to load that should contain the target
+ linkDoc?: Doc, // link that's being followed
closeContextIfNotFound: boolean = false, // after opening a context where the document should be, this determines whether the context should be closed if the Doc isn't actually there
originatingDoc: Opt<Doc> = undefined, // doc that initiated the display of the target odoc
finished?: () => void,
@@ -172,14 +172,15 @@ export class DocumentManager {
var wasHidden = resolvedTarget.hidden;
if (wasHidden) {
runInAction(() => {
- resolvedTarget.hidden = false; // if the target is hidden, un-hide it here.
+ resolvedTarget.hidden = false; // if the target is hidden, un-hide it here.
docView?.props.bringToFront(resolvedTarget);
});
}
const focusAndFinish = (didFocus: boolean) => {
const finalTargetDoc = resolvedTarget;
if (originatingDoc?.isPushpin) {
- if (!didFocus && !wasHidden) { // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal
+ if (!didFocus && !wasHidden) {
+ // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal
finalTargetDoc.hidden = !finalTargetDoc.hidden;
}
} else {
@@ -192,77 +193,92 @@ export class DocumentManager {
const contextDocs = docContext.length ? await DocListCastAsync(docContext[0].data) : undefined;
const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext.lastElement() : undefined;
const targetDocContext = contextDoc || annotatedDoc;
- const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) ||
- (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
+ const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) || (wasHidden && annoContainerView); // if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
if (annoContainerView) {
- if (annoContainerView.props.Document.layoutKey === "layout_icon") {
- annoContainerView.iconify(() => annoContainerView.focus(targetDoc, {
- originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) =>
- new Promise<ViewAdjustment>(res => {
- focusAndFinish(true);
- res(ViewAdjustment.doNothing);
- })
- }));
+ if (annoContainerView.props.Document.layoutKey === 'layout_icon') {
+ annoContainerView.iconify(() =>
+ annoContainerView.focus(targetDoc, {
+ originalTarget,
+ willZoom,
+ scale: presZoom,
+ afterFocus: (didFocus: boolean) =>
+ new Promise<ViewAdjustment>(res => {
+ focusAndFinish(true);
+ res(ViewAdjustment.doNothing);
+ }),
+ })
+ );
return;
} else if (!docView && targetDoc.type !== DocumentType.MARKER) {
- annoContainerView.focus(targetDoc); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
+ annoContainerView.focus(targetDoc); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
}
}
if (focusView) {
!noSelect && Doc.linkFollowHighlight(focusView.rootDoc); //TODO:glr make this a setting in PresBox
- const doFocus = (forceDidFocus: boolean) => focusView.focus(originalTarget ?? targetDoc, {
- originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) =>
- new Promise<ViewAdjustment>(res => {
- focusAndFinish(forceDidFocus || didFocus);
- res(ViewAdjustment.doNothing);
- })
- });
- if (focusView.props.Document.layoutKey === "layout_icon") {
+ const doFocus = (forceDidFocus: boolean) =>
+ focusView.focus(originalTarget ?? targetDoc, {
+ originalTarget,
+ willZoom,
+ scale: presZoom,
+ afterFocus: (didFocus: boolean) =>
+ new Promise<ViewAdjustment>(res => {
+ focusAndFinish(forceDidFocus || didFocus);
+ res(ViewAdjustment.doNothing);
+ }),
+ });
+ if (focusView.props.Document.layoutKey === 'layout_icon') {
focusView.iconify(() => doFocus(true));
} else {
doFocus(false);
}
} else {
- if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default
+ if (!targetDocContext) {
+ // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default
createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc)));
- } else { // otherwise try to get a view of the context of the target
- if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first..
+ } else {
+ // otherwise try to get a view of the context of the target
+ if (targetDocContextView) {
+ // we found a context view and aren't forced to create a new one ... focus on the context first..
wasHidden = wasHidden || targetDocContextView.rootDoc.hidden;
targetDocContextView.rootDoc.hidden = false; // make sure context isn't hidden
- targetDocContext._viewTransition = "transform 500ms";
+ targetDocContext._viewTransition = 'transform 500ms';
targetDocContextView.props.focus(targetDocContextView.rootDoc, {
- willZoom, afterFocus: async () => {
+ willZoom,
+ afterFocus: async () => {
targetDocContext._viewTransition = undefined;
- if (targetDocContext.layoutKey === "layout_icon") {
- targetDocContextView.iconify(() => this.jumpToDocument(
- resolvedTarget ?? targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
- finished, originalTarget, noSelect, presZoom));
+ if (targetDocContext.layoutKey === 'layout_icon') {
+ targetDocContextView.iconify(() => this.jumpToDocument(resolvedTarget ?? targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc, finished, originalTarget, noSelect, presZoom));
}
return ViewAdjustment.doNothing;
- }
+ },
});
// now find the target document within the context
- if (targetDoc._timecodeToShow) { // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode;
+ if (targetDoc._timecodeToShow) {
+ // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode;
targetDocContext._currentTimecode = targetDoc.anchorTimecodeToShow;
finished?.();
- } else { // no timecode means we need to find the context view and focus on our target
+ } else {
+ // no timecode means we need to find the context view and focus on our target
const findView = (delay: number) => {
- const retryDocView = getFirstDocView(resolvedTarget); // test again for the target view snce we presumably created the context above by focusing on it
- if (retryDocView) { // we found the target in the context.
+ const retryDocView = getFirstDocView(resolvedTarget); // test again for the target view snce we presumably created the context above by focusing on it
+ if (retryDocView) {
+ // we found the target in the context.
Doc.linkFollowHighlight(retryDocView.rootDoc);
retryDocView.focus(targetDoc, {
- willZoom, afterFocus: (didFocus: boolean) =>
+ willZoom,
+ afterFocus: (didFocus: boolean) =>
new Promise<ViewAdjustment>(res => {
!noSelect && focusAndFinish(true);
res(ViewAdjustment.doNothing);
- })
+ }),
}); // focus on the target in the context
} else if (delay > 1000) {
// we didn't find the target, so it must have moved out of the context. Go back to just creating it.
if (closeContextIfNotFound) targetDocContextView.props.removeDocument?.(targetDocContextView.rootDoc);
- if (targetDoc.layout) { // there will no layout for a TEXTANCHOR type document
+ if (targetDoc.layout) {
+ // there will no layout for a TEXTANCHOR type document
createViewFunc(Doc.BrushDoc(targetDoc), finished); // create a new view of the target
}
} else {
@@ -272,22 +288,24 @@ export class DocumentManager {
setTimeout(() => findView(0), 0);
}
} else {
- if (docContext.length && docContext[0]?.layoutKey === "layout_icon") {
+ if (docContext.length && docContext[0]?.layoutKey === 'layout_icon') {
const docContextView = this.getFirstDocumentView(docContext[0]);
if (docContextView) {
- return docContextView.iconify(() => this.jumpToDocument(
- targetDoc, willZoom, createViewFunc, docContext.slice(1, docContext.length), linkDoc, closeContextIfNotFound, originatingDoc,
- finished, originalTarget, noSelect, presZoom));
+ return docContextView.iconify(() =>
+ this.jumpToDocument(targetDoc, willZoom, createViewFunc, docContext.slice(1, docContext.length), linkDoc, closeContextIfNotFound, originatingDoc, finished, originalTarget, noSelect, presZoom)
+ );
}
}
// there's no context view so we need to create one first and try again when that finishes
const finishFunc = () => this.jumpToDocument(targetDoc, true, createViewFunc, docContext, linkDoc, true /* if we don't find the target, we want to get rid of the context just created */, undefined, finished, originalTarget);
- createViewFunc(targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target
- finishFunc);
+ createViewFunc(
+ targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target
+ finishFunc
+ );
}
}
}
- }
+ };
}
export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) {
const cv = collectionDoc && DocumentManager.Instance.getDocumentView(collectionDoc);
@@ -295,14 +313,12 @@ export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) {
if (dv && Doc.AreProtosEqual(dv.props.Document, doc)) {
dv.props.focus(dv.props.Document, { willZoom: true });
Doc.linkFollowHighlight(dv?.props.Document, false);
- }
- else {
- const context = doc.context !== CurrentUserUtils.MyFilesystem && Cast(doc.context, Doc, null);
+ } else {
+ const context = doc.context !== Doc.MyFilesystem && Cast(doc.context, Doc, null);
const showDoc = context || doc;
const bestAlias = showDoc === Doc.GetProto(showDoc) ? DocListCast(showDoc.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail) : showDoc;
- CollectionDockingView.AddSplit(bestAlias ? bestAlias : Doc.MakeAlias(showDoc), "right") && context &&
- setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc));
+ CollectionDockingView.AddSplit(bestAlias ? bestAlias : Doc.MakeAlias(showDoc), 'right') && context && setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc));
}
}
-ScriptingGlobals.add(DocFocusOrOpen); \ No newline at end of file
+ScriptingGlobals.add(DocFocusOrOpen);
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 7dcff9c56..18aee6444 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -1,8 +1,8 @@
-import { Doc } from "../../fields/Doc";
-import { DocServer } from "../DocServer";
import * as qs from 'query-string';
-import { Utils, OmitKeys } from "../../Utils";
-import { CurrentUserUtils } from "./CurrentUserUtils";
+import { Doc } from '../../fields/Doc';
+import { OmitKeys, Utils } from '../../Utils';
+import { DocServer } from '../DocServer';
+import { DashboardView } from '../views/DashboardView';
export namespace HistoryUtil {
export interface DocInitializerList {
@@ -10,7 +10,7 @@ export namespace HistoryUtil {
}
export interface DocUrl {
- type: "doc";
+ type: 'doc';
docId: string;
initializers?: {
[docId: string]: DocInitializerList;
@@ -25,11 +25,11 @@ export namespace HistoryUtil {
// const handlers: ((state: ParsedUrl | null) => void)[] = [];
function onHistory(e: PopStateEvent) {
- if (window.location.pathname !== "/home") {
- const url = e.state as ParsedUrl || parseUrl(window.location);
+ if (window.location.pathname !== '/home') {
+ const url = (e.state as ParsedUrl) || parseUrl(window.location);
if (url) {
switch (url.type) {
- case "doc":
+ case 'doc':
onDocUrl(url);
break;
}
@@ -43,13 +43,13 @@ export namespace HistoryUtil {
let _lastStatePush = 0;
export function pushState(state: ParsedUrl) {
if (Date.now() - _lastStatePush > 1000) {
- history.pushState(state, "", createUrl(state));
+ history.pushState(state, '', createUrl(state));
}
_lastStatePush = Date.now();
}
export function replaceState(state: ParsedUrl) {
- history.replaceState(state, "", createUrl(state));
+ history.replaceState(state, '', createUrl(state));
}
function copyState(state: ParsedUrl): ParsedUrl {
@@ -61,7 +61,7 @@ export namespace HistoryUtil {
if (state) {
state.initializers = state.initializers || {};
}
- return state ?? {initializers:{}};
+ return state ?? { initializers: {} };
}
// export function addHandler(handler: (state: ParsedUrl | null) => void) {
@@ -78,10 +78,10 @@ export namespace HistoryUtil {
const parsers: { [type: string]: (pathname: string[], opts: qs.ParsedQuery) => ParsedUrl | undefined } = {};
const stringifiers: { [type: string]: (state: ParsedUrl) => string } = {};
- type ParserValue = true | "none" | "json" | ((value: string) => any);
+ type ParserValue = true | 'none' | 'json' | ((value: string) => any);
type Parser = {
- [key: string]: ParserValue
+ [key: string]: ParserValue;
};
function addParser(type: string, requiredFields: Parser, optionalFields: Parser, customParser?: (pathname: string[], opts: qs.ParsedQuery, current: ParsedUrl) => ParsedUrl | null | undefined) {
@@ -90,9 +90,9 @@ export namespace HistoryUtil {
return value;
}
if (Array.isArray(value)) {
- } else if (parser === true || parser === "json") {
+ } else if (parser === true || parser === 'json') {
value = JSON.parse(value);
- } else if (parser === "none") {
+ } else if (parser === 'none') {
} else {
value = parser(value);
}
@@ -142,29 +142,28 @@ export namespace HistoryUtil {
}
const queryObj = OmitKeys(state, keys).extract;
const query: any = {};
- Object.keys(queryObj).forEach(key => query[key] = queryObj[key] === null ? null : JSON.stringify(queryObj[key]));
+ Object.keys(queryObj).forEach(key => (query[key] = queryObj[key] === null ? null : JSON.stringify(queryObj[key])));
const queryString = qs.stringify(query);
- return path + (queryString ? `?${queryString}` : "");
+ return path + (queryString ? `?${queryString}` : '');
};
}
- addParser("doc", {}, { readonly: true, initializers: true, nro: true, sharing: true }, (pathname, opts, current) => {
+ addParser('doc', {}, { readonly: true, initializers: true, nro: true, sharing: true }, (pathname, opts, current) => {
if (pathname.length !== 2) return undefined;
current.initializers = current.initializers || {};
const docId = pathname[1];
current.docId = docId;
});
- addStringifier("doc", ["initializers", "readonly", "nro"], (state, current) => {
+ addStringifier('doc', ['initializers', 'readonly', 'nro'], (state, current) => {
return `${current}/${state.docId}`;
});
-
export function parseUrl(location: Location | URL): ParsedUrl | undefined {
const pathname = location.pathname.substring(1);
const search = location.search;
const opts = search.length ? qs.parse(search, { sort: false }) : {};
- const pathnameSplit = pathname.split("/");
+ const pathnameSplit = pathname.split('/');
const type = pathnameSplit[0];
@@ -179,7 +178,7 @@ export namespace HistoryUtil {
if (params.type in stringifiers) {
return stringifiers[params.type](params);
}
- return "";
+ return '';
}
export async function initDoc(id: string, initializer: DocInitializerList) {
@@ -197,7 +196,7 @@ export namespace HistoryUtil {
await Promise.all(Object.keys(init).map(id => initDoc(id, init[id])));
}
if (field instanceof Doc) {
- CurrentUserUtils.openDashboard(field, true);
+ DashboardView.openDashboard(field, true);
}
}
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
new file mode 100644
index 000000000..f75ac24f5
--- /dev/null
+++ b/src/client/util/LinkFollower.ts
@@ -0,0 +1,112 @@
+import { action, observable, observe } from 'mobx';
+import { computedFn } from 'mobx-utils';
+import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { ProxyField } from '../../fields/Proxy';
+import { BoolCast, Cast, StrCast } from '../../fields/Types';
+import { LightboxView } from '../views/LightboxView';
+import { DocumentViewSharedProps, ViewAdjustment } from '../views/nodes/DocumentView';
+import { DocumentManager } from './DocumentManager';
+import { UndoManager } from './UndoManager';
+
+type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
+/*
+ * link doc:
+ * - anchor1: doc
+ * - anchor2: doc
+ *
+ * group doc:
+ * - type: string representing the group type/name/category
+ * - metadata: doc representing the metadata kvps
+ *
+ * metadata doc:
+ * - user defined kvps
+ */
+export class LinkFollower {
+ // follows a link - if the target is on screen, it highlights/pans to it.
+ // if the target isn't onscreen, then it will open up the target in the lightbox, or in place
+ // depending on the followLinkLocation property of the source (or the link itself as a fallback);
+ public static FollowLink = (linkDoc: Opt<Doc>, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean, zoom: boolean = false) => {
+ const batch = UndoManager.StartBatch('follow link click');
+ // open up target if it's not already in view ...
+ const createViewFunc = (doc: Doc, followLoc: string, finished?: Opt<() => void>) => {
+ const createTabForTarget = (didFocus: boolean) =>
+ new Promise<ViewAdjustment>(res => {
+ const where = LightboxView.LightboxDoc ? 'lightbox' : StrCast(sourceDoc.followLinkLocation, followLoc);
+ docViewProps.addDocTab(doc, where);
+ setTimeout(() => {
+ const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
+ const targDocView = getFirstDocView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise.
+ if (targDocView) {
+ targDocView.props.focus(doc, {
+ willZoom: BoolCast(sourceDoc.followLinkZoom, false),
+ afterFocus: (didFocus: boolean) => {
+ finished?.();
+ res(ViewAdjustment.resetView);
+ return new Promise<ViewAdjustment>(res2 => res2(ViewAdjustment.doNothing));
+ },
+ });
+ } else {
+ res(where !== 'inPlace' ? ViewAdjustment.resetView : ViewAdjustment.doNothing); // for 'inPlace' resetting the initial focus&zoom would negate the zoom into the target
+ }
+ });
+ });
+
+ if (!sourceDoc.followLinkZoom) {
+ createTabForTarget(false);
+ } else {
+ // first focus & zoom onto this (the clicked document). Then execute the function to focus on the target
+ docViewProps.focus(sourceDoc, { willZoom: BoolCast(sourceDoc.followLinkZoom, true), scale: 1, afterFocus: createTabForTarget });
+ }
+ };
+ LinkFollower.traverseLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, zoom), docViewProps.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
+ };
+
+ public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
+ const linkDocs = link ? [link] : DocListCast(sourceDoc.links);
+ const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1
+ const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2
+ const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
+ const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
+ const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
+ const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs;
+ const followLinks = sourceDoc.isPushpin ? linkDocList : linkDocList.slice(0, 1);
+ var count = 0;
+ const allFinished = () => ++count === followLinks.length && finished?.();
+ followLinks.forEach(async linkDoc => {
+ if (linkDoc) {
+ const target = (
+ sourceDoc === linkDoc.anchor1
+ ? linkDoc.anchor2
+ : sourceDoc === linkDoc.anchor2
+ ? linkDoc.anchor1
+ : Doc.AreProtosEqual(sourceDoc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)
+ ? linkDoc.anchor2
+ : linkDoc.anchor1
+ ) as Doc;
+ if (target) {
+ if (target.TourMap) {
+ const fieldKey = Doc.LayoutFieldKey(target);
+ const tour = DocListCast(target[fieldKey]).reverse();
+ LightboxView.SetLightboxDoc(currentContext, undefined, tour);
+ setTimeout(LightboxView.Next);
+ allFinished();
+ } else {
+ const containerAnnoDoc = Cast(target.annotationOn, Doc, null);
+ const containerDoc = containerAnnoDoc || target;
+ var containerDocContext = containerDoc?.context ? [Cast(containerDoc?.context, Doc, null)] : ([] as Doc[]);
+ while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
+ containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
+ }
+ const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]].filter(a => a) : containerDocContext;
+ DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, 'lightbox'), finished), targetContexts, linkDoc, undefined, sourceDoc, allFinished);
+ }
+ } else {
+ allFinished();
+ }
+ } else {
+ allFinished();
+ }
+ });
+ }
+}
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index d51cd350d..7a12a8580 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,35 +1,30 @@
-import { action, observable, observe } from "mobx";
-import { computedFn } from "mobx-utils";
-import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from "../../fields/Doc";
-import { List } from "../../fields/List";
-import { ProxyField } from "../../fields/Proxy";
-import { BoolCast, Cast, StrCast } from "../../fields/Types";
-import { LightboxView } from "../views/LightboxView";
-import { DocumentViewSharedProps, ViewAdjustment } from "../views/nodes/DocumentView";
-import { DocumentManager } from "./DocumentManager";
-import { UndoManager } from "./UndoManager";
-
-type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
-/*
- * link doc:
- * - anchor1: doc
+import { action, observable, observe } from 'mobx';
+import { computedFn } from 'mobx-utils';
+import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { ProxyField } from '../../fields/Proxy';
+import { Cast, StrCast } from '../../fields/Types';
+/*
+ * link doc:
+ * - anchor1: doc
* - anchor2: doc
- *
+ *
* group doc:
* - type: string representing the group type/name/category
* - metadata: doc representing the metadata kvps
- *
+ *
* metadata doc:
- * - user defined kvps
+ * - user defined kvps
*/
export class LinkManager {
-
@observable static _instance: LinkManager;
@observable static userLinkDBs: Doc[] = [];
public static currentLink: Opt<Doc>;
- public static get Instance() { return LinkManager._instance; }
+ public static get Instance() {
+ return LinkManager._instance;
+ }
public static addLinkDB = (linkDb: any) => LinkManager.userLinkDBs.push(linkDb);
- public static AutoKeywords = "keywords:Usages";
+ public static AutoKeywords = 'keywords:Usages';
static links: Doc[] = [];
constructor() {
LinkManager._instance = this;
@@ -38,50 +33,61 @@ export class LinkManager {
const addLinkToDoc = (link: Doc) => {
const a1Prom = link?.anchor1;
const a2Prom = link?.anchor2;
- Promise.all([a1Prom, a2Prom]).then((all) => {
+ Promise.all([a1Prom, a2Prom]).then(all => {
const a1 = all[0];
const a2 = all[1];
const a1ProtoProm = (link?.anchor1 as Doc)?.proto;
const a2ProtoProm = (link?.anchor2 as Doc)?.proto;
- Promise.all([a1ProtoProm, a2ProtoProm]).then(action((all) => {
- if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
- Doc.GetProto(a1)[DirectLinksSym].add(link);
- Doc.GetProto(a2)[DirectLinksSym].add(link);
- Doc.GetProto(link)[DirectLinksSym].add(link);
- }
- }));
+ Promise.all([a1ProtoProm, a2ProtoProm]).then(
+ action(all => {
+ if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
+ Doc.GetProto(a1)[DirectLinksSym].add(link);
+ Doc.GetProto(a2)[DirectLinksSym].add(link);
+ Doc.GetProto(link)[DirectLinksSym].add(link);
+ }
+ })
+ );
});
};
const remLinkFromDoc = (link: Doc) => {
const a1 = link?.anchor1;
const a2 = link?.anchor2;
- Promise.all([a1, a2]).then(action(() => {
- if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
- Doc.GetProto(a1)[DirectLinksSym].delete(link);
- Doc.GetProto(a2)[DirectLinksSym].delete(link);
- Doc.GetProto(link)[DirectLinksSym].delete(link);
- }
- }));
+ Promise.all([a1, a2]).then(
+ action(() => {
+ if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
+ Doc.GetProto(a1)[DirectLinksSym].delete(link);
+ Doc.GetProto(a2)[DirectLinksSym].delete(link);
+ Doc.GetProto(link)[DirectLinksSym].delete(link);
+ }
+ })
+ );
};
const watchUserLinkDB = (userLinkDBDoc: Doc) => {
LinkManager.links.push(...DocListCast(userLinkDBDoc.data));
- const toRealField = (field: Field) => field instanceof ProxyField ? field.value() : field; // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields
- observe(userLinkDBDoc.data as Doc, change => { // observe pushes/splices on a user link DB 'data' field (should only happen for local changes)
- switch (change.type as any) {
- case "splice":
- (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link)));
- (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link)));
- break;
- case "update": //let oldValue = change.oldValue;
- }
- }, true);
- observe(userLinkDBDoc, "data", // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link)
+ const toRealField = (field: Field) => (field instanceof ProxyField ? field.value() : field); // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields
+ observe(
+ userLinkDBDoc.data as Doc,
change => {
+ // observe pushes/splices on a user link DB 'data' field (should only happen for local changes)
switch (change.type as any) {
- case "update":
- Promise.all([...(change.oldValue as any as Doc[] || []), ...(change.newValue as any as Doc[] || [])]).then(doclist => {
- const oldDocs = doclist.slice(0, (change.oldValue as any as Doc[] || []).length);
- const newDocs = doclist.slice((change.oldValue as any as Doc[] || []).length, doclist.length);
+ case 'splice':
+ (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link)));
+ (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link)));
+ break;
+ case 'update': //let oldValue = change.oldValue;
+ }
+ },
+ true
+ );
+ observe(
+ userLinkDBDoc,
+ 'data', // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link)
+ change => {
+ switch (change.type as any) {
+ case 'update':
+ Promise.all([...((change.oldValue as any as Doc[]) || []), ...((change.newValue as any as Doc[]) || [])]).then(doclist => {
+ const oldDocs = doclist.slice(0, ((change.oldValue as any as Doc[]) || []).length);
+ const newDocs = doclist.slice(((change.oldValue as any as Doc[]) || []).length, doclist.length);
const added = newDocs?.filter(link => !(oldDocs || []).includes(link));
const removed = oldDocs?.filter(link => !(newDocs || []).includes(link));
@@ -89,38 +95,54 @@ export class LinkManager {
removed?.forEach((link: any) => remLinkFromDoc(toRealField(link)));
});
}
- }, true);
+ },
+ true
+ );
};
- observe(LinkManager.userLinkDBs, change => {
- switch (change.type as any) {
- case "splice": (change as any).added.forEach(watchUserLinkDB); break;
- case "update": //let oldValue = change.oldValue;
- }
- }, true);
+ observe(
+ LinkManager.userLinkDBs,
+ change => {
+ switch (change.type as any) {
+ case 'splice':
+ (change as any).added.forEach(watchUserLinkDB);
+ break;
+ case 'update': //let oldValue = change.oldValue;
+ }
+ },
+ true
+ );
LinkManager.addLinkDB(Doc.LinkDBDoc());
- DocListCastAsync(Doc.LinkDBDoc()?.data).then(dblist => dblist?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
- const a1 = await Cast(link?.anchor1, Doc, null);
- const a2 = await Cast(link?.anchor2, Doc, null);
- }));
+ DocListCastAsync(Doc.LinkDBDoc()?.data).then(dblist =>
+ dblist?.forEach(async link => {
+ // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
+ const a1 = await Cast(link?.anchor1, Doc, null);
+ const a2 = await Cast(link?.anchor2, Doc, null);
+ })
+ );
}
-
public createLinkrelationshipLists = () => {
//create new lists for link relations and their associated colors if the lists don't already exist
!Doc.UserDoc().linkRelationshipList && (Doc.UserDoc().linkRelationshipList = new List<string>());
!Doc.UserDoc().linkColorList && (Doc.UserDoc().linkColorList = new List<string>());
!Doc.UserDoc().linkRelationshipSizes && (Doc.UserDoc().linkRelationshipSizes = new List<number>());
- }
+ };
public addLink(linkDoc: Doc, checkExists = false) {
if (!checkExists || !DocListCast(Doc.LinkDBDoc().data).includes(linkDoc)) {
- Doc.AddDocToList(Doc.LinkDBDoc(), "data", linkDoc);
+ Doc.AddDocToList(Doc.LinkDBDoc(), 'data', linkDoc);
}
}
- public deleteLink(linkDoc: Doc) { return Doc.RemoveDocFromList(Doc.LinkDBDoc(), "data", linkDoc); }
- public deleteAllLinksOnAnchor(anchor: Doc) { LinkManager.Instance.relatedLinker(anchor).forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc)); }
+ public deleteLink(linkDoc: Doc) {
+ return Doc.RemoveDocFromList(Doc.LinkDBDoc(), 'data', linkDoc);
+ }
+ public deleteAllLinksOnAnchor(anchor: Doc) {
+ LinkManager.Instance.relatedLinker(anchor).forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc));
+ }
- public getAllRelatedLinks(anchor: Doc) { return this.relatedLinker(anchor); } // finds all links that contain the given anchor
+ public getAllRelatedLinks(anchor: Doc) {
+ return this.relatedLinker(anchor);
+ } // finds all links that contain the given anchor
public getAllDirectLinks(anchor: Doc): Doc[] {
// FIXME:glr Why is Doc undefined?
if (Doc.GetProto(anchor)[DirectLinksSym]) {
@@ -133,18 +155,16 @@ export class LinkManager {
relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
const lfield = Doc.LayoutFieldKey(anchor);
if (!anchor || anchor instanceof Promise || Doc.GetProto(anchor) instanceof Promise) {
- console.log("WAITING FOR DOC/PROTO IN LINKMANAGER");
+ console.log('WAITING FOR DOC/PROTO IN LINKMANAGER');
return [];
}
const dirLinks = Doc.GetProto(anchor)[DirectLinksSym];
- const annos = DocListCast(anchor[lfield + "-annotations"]);
- const timelineAnnos = DocListCast(anchor[lfield + "-annotations-timeline"]);
+ const annos = DocListCast(anchor[lfield + '-annotations']);
+ const timelineAnnos = DocListCast(anchor[lfield + '-annotations-timeline']);
if (!annos || !timelineAnnos) {
debugger;
}
- const related = [...annos, ...timelineAnnos].reduce((list, anno) =>
- [...list, ...LinkManager.Instance.relatedLinker(anno)],
- Array.from(dirLinks).slice());
+ const related = [...annos, ...timelineAnnos].reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], Array.from(dirLinks).slice());
return related;
}, true);
@@ -152,15 +172,15 @@ export class LinkManager {
public getRelatedGroupedLinks(anchor: Doc): Map<string, Array<Doc>> {
const anchorGroups = new Map<string, Array<Doc>>();
this.relatedLinker(anchor).forEach(link => {
- if (link.linkRelationship && link.linkRelationship !== "-ungrouped-") {
+ if (link.linkRelationship && link.linkRelationship !== '-ungrouped-') {
const relation = StrCast(link.linkRelationship);
- const anchorRelation = relation.indexOf(":") !== -1 ? relation.split(":")[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation;
+ const anchorRelation = relation.indexOf(':') !== -1 ? relation.split(':')[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation;
const group = anchorGroups.get(anchorRelation);
anchorGroups.set(anchorRelation, group ? [...group, link] : [link]);
} else {
// if link is in no groups then put it in default group
- const group = anchorGroups.get("*");
- anchorGroups.set("*", group ? [...group, link] : [link]);
+ const group = anchorGroups.get('*');
+ anchorGroups.set('*', group ? [...group, link] : [link]);
}
});
return anchorGroups;
@@ -178,85 +198,4 @@ export class LinkManager {
if (Doc.AreProtosEqual(anchor, a2.annotationOn as Doc)) return a1;
if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc;
}
-
-
- // follows a link - if the target is on screen, it highlights/pans to it.
- // if the target isn't onscreen, then it will open up the target in the lightbox, or in place
- // depending on the followLinkLocation property of the source (or the link itself as a fallback);
- public static FollowLink = (linkDoc: Opt<Doc>, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean, zoom: boolean = false) => {
- const batch = UndoManager.StartBatch("follow link click");
- // open up target if it's not already in view ...
- const createViewFunc = (doc: Doc, followLoc: string, finished?: Opt<() => void>) => {
- const createTabForTarget = (didFocus: boolean) => new Promise<ViewAdjustment>(res => {
- const where = LightboxView.LightboxDoc ? "lightbox" : StrCast(sourceDoc.followLinkLocation, followLoc);
- docViewProps.addDocTab(doc, where);
- setTimeout(() => {
- const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
- const targDocView = getFirstDocView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise.
- if (targDocView) {
- targDocView.props.focus(doc, {
- willZoom: BoolCast(sourceDoc.followLinkZoom, false),
- afterFocus: (didFocus: boolean) => {
- finished?.();
- res(ViewAdjustment.resetView);
- return new Promise<ViewAdjustment>(res2 => res2(ViewAdjustment.doNothing));
- }
- });
- } else {
- res(where !== "inPlace" ? ViewAdjustment.resetView : ViewAdjustment.doNothing); // for 'inPlace' resetting the initial focus&zoom would negate the zoom into the target
- }
- });
- });
-
- if (!sourceDoc.followLinkZoom) {
- createTabForTarget(false);
- } else {
- // first focus & zoom onto this (the clicked document). Then execute the function to focus on the target
- docViewProps.focus(sourceDoc, { willZoom: BoolCast(sourceDoc.followLinkZoom, true), scale: 1, afterFocus: createTabForTarget });
- }
- };
- LinkManager.traverseLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, zoom), docViewProps.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
- }
-
- public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
- const linkDocs = link ? [link] : DocListCast(sourceDoc.links);
- const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1
- const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2
- const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
- const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
- const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
- const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : (traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs);
- const followLinks = sourceDoc.isPushpin ? linkDocList : linkDocList.slice(0, 1);
- var count = 0;
- const allFinished = () => ++count === followLinks.length && finished?.();
- followLinks.forEach(async linkDoc => {
- if (linkDoc) {
- const target = (sourceDoc === linkDoc.anchor1 ? linkDoc.anchor2 : sourceDoc === linkDoc.anchor2 ? linkDoc.anchor1 :
- (Doc.AreProtosEqual(sourceDoc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc;
- if (target) {
- if (target.TourMap) {
- const fieldKey = Doc.LayoutFieldKey(target);
- const tour = DocListCast(target[fieldKey]).reverse();
- LightboxView.SetLightboxDoc(currentContext, undefined, tour);
- setTimeout(LightboxView.Next);
- allFinished();
- } else {
- const containerAnnoDoc = Cast(target.annotationOn, Doc, null);
- const containerDoc = containerAnnoDoc || target;
- var containerDocContext = containerDoc?.context ? [Cast(containerDoc?.context, Doc, null)] : [] as Doc[];
- while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
- containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
- }
- const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]].filter(a => a) : containerDocContext;
- DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetContexts, linkDoc, undefined, sourceDoc, allFinished);
- }
- } else {
- allFinished();
- }
- } else {
- allFinished();
- }
- });
- }
-
-} \ No newline at end of file
+}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index b71086561..d67ce6f6a 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,15 +1,11 @@
-import { action, observable, ObservableMap } from "mobx";
-import { computedFn } from "mobx-utils";
-import { Doc, Opt } from "../../fields/Doc";
-import { DocumentType } from "../documents/DocumentTypes";
-import { CollectionViewType } from "../views/collections/CollectionView";
-import { DocumentView } from "../views/nodes/DocumentView";
-import { CurrentUserUtils } from "./CurrentUserUtils";
+import { action, observable, ObservableMap } from 'mobx';
+import { computedFn } from 'mobx-utils';
+import { Doc, Opt } from '../../fields/Doc';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
+import { DocumentView } from '../views/nodes/DocumentView';
export namespace SelectionManager {
-
class Manager {
-
@observable IsDragging: boolean = false;
SelectedViews: ObservableMap<DocumentView, Doc> = new ObservableMap();
@observable SelectedSchemaDocument: Doc | undefined;
@@ -63,16 +59,21 @@ export namespace SelectionManager {
manager.SelectSchemaViewDoc(document);
}
- const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) { // wrapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed
+ const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) {
+ // wrapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed
return manager.SelectedViews.get(doc) ? true : false;
});
// computed functions, such as used in IsSelected generate errors if they're called outside of a
// reaction context. Specifying the context with 'outsideReaction' allows an efficiency feature
// to avoid unnecessary mobx invalidations when running inside a reaction.
export function IsSelected(doc: DocumentView | undefined, outsideReaction?: boolean): boolean {
- return !doc ? false : outsideReaction ?
- manager.SelectedViews.get(doc) ? true : false : // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get()
- IsSelectedCache(doc);
+ return !doc
+ ? false
+ : outsideReaction
+ ? manager.SelectedViews.get(doc)
+ ? true
+ : false // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get()
+ : IsSelectedCache(doc);
}
export function DeselectAll(except?: Doc): void {
@@ -96,4 +97,4 @@ export namespace SelectionManager {
export function Docs(): Doc[] {
return Array.from(manager.SelectedViews.values()).filter(doc => doc?._viewType !== CollectionViewType.Docking);
}
-} \ No newline at end of file
+}
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 080237649..12d1793af 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -1,29 +1,28 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { ColorState, SketchPicker } from "react-color";
-import { Doc } from "../../fields/Doc";
-import { BoolCast, StrCast, Cast } from "../../fields/Types";
-import { addStyleSheet, addStyleSheetRule, Utils } from "../../Utils";
-import { GoogleAuthenticationManager } from "../apis/GoogleAuthenticationManager";
-import { DocServer } from "../DocServer";
-import { Networking } from "../Network";
-import { MainViewModal } from "../views/MainViewModal";
-import { FontIconBox } from "../views/nodes/button/FontIconBox";
-import { CurrentUserUtils } from "./CurrentUserUtils";
-import { DragManager } from "./DragManager";
-import { GroupManager } from "./GroupManager";
-import "./SettingsManager.scss";
-import { undoBatch } from "./UndoManager";
-const higflyout = require("@hig/flyout");
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { ColorState, SketchPicker } from 'react-color';
+import { Doc } from '../../fields/Doc';
+import { BoolCast, Cast, StrCast } from '../../fields/Types';
+import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
+import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
+import { DocServer } from '../DocServer';
+import { Networking } from '../Network';
+import { MainViewModal } from '../views/MainViewModal';
+import { FontIconBox } from '../views/nodes/button/FontIconBox';
+import { DragManager } from './DragManager';
+import { GroupManager } from './GroupManager';
+import './SettingsManager.scss';
+import { undoBatch } from './UndoManager';
+const higflyout = require('@hig/flyout');
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
export enum ColorScheme {
- Dark = "-Dark",
- Light = "-Light",
- System = "-MatchSystem"
+ Dark = '-Dark',
+ Light = '-Light',
+ System = '-MatchSystem',
}
@observer
@@ -31,67 +30,77 @@ export class SettingsManager extends React.Component<{}> {
public static Instance: SettingsManager;
static _settingsStyle = addStyleSheet();
@observable private isOpen = false;
- @observable private passwordResultText = "";
+ @observable private passwordResultText = '';
@observable private playgroundMode = false;
- @observable private curr_password = "";
- @observable private new_password = "";
- @observable private new_confirm = "";
- @observable activeTab = "Accounts";
+ @observable private curr_password = '';
+ @observable private new_password = '';
+ @observable private new_confirm = '';
+ @observable activeTab = 'Accounts';
- @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; }
- @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme; }
+ @observable public static propertiesWidth: number = 0;
+ @observable public static headerBarHeight: number = 0;
+
+ @computed get backgroundColor() {
+ return Doc.UserDoc().activeCollectionBackground;
+ }
+ @computed get colorScheme() {
+ return Doc.ActiveDashboard?.colorScheme;
+ }
constructor(props: {}) {
super(props);
SettingsManager.Instance = this;
}
- public close = action(() => this.isOpen = false);
- public open = action(() => this.isOpen = true);
+ public close = action(() => (this.isOpen = false));
+ public open = action(() => (this.isOpen = true));
private googleAuthorize = action(() => GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true));
private changePassword = async () => {
if (!(this.curr_password && this.new_password && this.new_confirm)) {
- runInAction(() => this.passwordResultText = "Error: Hey, we're missing some fields!");
+ runInAction(() => (this.passwordResultText = "Error: Hey, we're missing some fields!"));
} else {
const passwordBundle = { curr_pass: this.curr_password, new_pass: this.new_password, new_confirm: this.new_confirm };
const { error } = await Networking.PostToServer('/internalResetPassword', passwordBundle);
- runInAction(() => this.passwordResultText = error ? "Error: " + error[0].msg + "..." : "Password successfully updated!");
+ runInAction(() => (this.passwordResultText = error ? 'Error: ' + error[0].msg + '...' : 'Password successfully updated!'));
}
- }
-
- @undoBatch selectUserMode = action((e: React.ChangeEvent) => Doc.noviceMode = (e.currentTarget as any)?.value === "Novice");
- @undoBatch changeShowTitle = action((e: React.ChangeEvent) => Doc.UserDoc().showTitle = (e.currentTarget as any).value ? "title" : undefined);
- @undoBatch changeFontFamily = action((e: React.ChangeEvent) => Doc.UserDoc().fontFamily = (e.currentTarget as any).value);
- @undoBatch changeFontSize = action((e: React.ChangeEvent) => Doc.UserDoc().fontSize = (e.currentTarget as any).value);
- @undoBatch switchActiveBackgroundColor = action((color: ColorState) => Doc.UserDoc().activeCollectionBackground = String(color.hex));
- @undoBatch switchUserColor = action((color: ColorState) => { Doc.SharingDoc().userColor = undefined; Doc.GetProto(Doc.SharingDoc()).userColor = String(color.hex); });
+ };
+
+ @undoBatch selectUserMode = action((e: React.ChangeEvent) => (Doc.noviceMode = (e.currentTarget as any)?.value === 'Novice'));
+ @undoBatch changeShowTitle = action((e: React.ChangeEvent) => (Doc.UserDoc().showTitle = (e.currentTarget as any).value ? 'title' : undefined));
+ @undoBatch changeFontFamily = action((e: React.ChangeEvent) => (Doc.UserDoc().fontFamily = (e.currentTarget as any).value));
+ @undoBatch changeFontSize = action((e: React.ChangeEvent) => (Doc.UserDoc().fontSize = (e.currentTarget as any).value));
+ @undoBatch switchActiveBackgroundColor = action((color: ColorState) => (Doc.UserDoc().activeCollectionBackground = String(color.hex)));
+ @undoBatch switchUserColor = action((color: ColorState) => {
+ Doc.SharingDoc().userColor = undefined;
+ Doc.GetProto(Doc.SharingDoc()).userColor = String(color.hex);
+ });
@undoBatch playgroundModeToggle = action(() => {
this.playgroundMode = !this.playgroundMode;
if (this.playgroundMode) {
DocServer.Control.makeReadOnly();
- addStyleSheetRule(SettingsManager._settingsStyle, "topbar-inner-container", { background: "red !important" });
- }
- else DocServer.Control.makeEditable();
+ addStyleSheetRule(SettingsManager._settingsStyle, 'topbar-inner-container', { background: 'red !important' });
+ } else DocServer.Control.makeEditable();
});
@undoBatch
@action
changeColorScheme = action((e: React.ChangeEvent) => {
- const activeDashboard= CurrentUserUtils.ActiveDashboard;
+ const activeDashboard = Doc.ActiveDashboard;
if (!activeDashboard) return;
const scheme: ColorScheme = (e.currentTarget as any).value;
switch (scheme) {
case ColorScheme.Light:
activeDashboard.colorScheme = undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
- addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "#d3d3d3 !important" });
+ addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: '#d3d3d3 !important' });
break;
case ColorScheme.Dark:
activeDashboard.colorScheme = ColorScheme.Dark;
- addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "black !important" });
+ addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: 'black !important' });
break;
- case ColorScheme.System: default:
+ case ColorScheme.System:
+ default:
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
activeDashboard.colorScheme = e.matches ? ColorScheme.Dark : undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
});
@@ -99,223 +108,285 @@ export class SettingsManager extends React.Component<{}> {
}
});
-
@computed get colorsContent() {
- const colorBox = (func: (color: ColorState) => void) => <SketchPicker onChange={func} color={StrCast(this.backgroundColor)}
- presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
- '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
- '#FFFFFF', '#f1efeb', 'transparent']} />;
-
- const colorFlyout = <div className="colorFlyout">
- <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={colorBox(this.switchActiveBackgroundColor)}>
- <div className="colorFlyout-button" style={{ backgroundColor: StrCast(this.backgroundColor) }} onPointerDown={e => e.stopPropagation()} >
- <FontAwesomeIcon icon="palette" size="sm" color={StrCast(this.backgroundColor)} />
- </div>
- </Flyout>
- </div>;
- const userColorFlyout = <div className="colorFlyout">
- <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={colorBox(this.switchUserColor)}>
- <div className="colorFlyout-button" style={{ backgroundColor: StrCast(this.backgroundColor) }} onPointerDown={e => e.stopPropagation()} >
- <FontAwesomeIcon icon="palette" size="sm" color={StrCast(this.backgroundColor)} />
- </div>
- </Flyout>
- </div>;
+ const colorBox = (func: (color: ColorState) => void) => (
+ <SketchPicker
+ onChange={func}
+ color={StrCast(this.backgroundColor)}
+ presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
+ />
+ );
+
+ const colorFlyout = (
+ <div className="colorFlyout">
+ <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={colorBox(this.switchActiveBackgroundColor)}>
+ <div className="colorFlyout-button" style={{ backgroundColor: StrCast(this.backgroundColor) }} onPointerDown={e => e.stopPropagation()}>
+ <FontAwesomeIcon icon="palette" size="sm" color={StrCast(this.backgroundColor)} />
+ </div>
+ </Flyout>
+ </div>
+ );
+ const userColorFlyout = (
+ <div className="colorFlyout">
+ <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={colorBox(this.switchUserColor)}>
+ <div className="colorFlyout-button" style={{ backgroundColor: StrCast(this.backgroundColor) }} onPointerDown={e => e.stopPropagation()}>
+ <FontAwesomeIcon icon="palette" size="sm" color={StrCast(this.backgroundColor)} />
+ </div>
+ </Flyout>
+ </div>
+ );
const colorSchemes = [ColorScheme.Light, ColorScheme.Dark, ColorScheme.System];
- const schemeMap = ["Light", "Dark", "Match system"];
+ const schemeMap = ['Light', 'Dark', 'Match system'];
- return <div className="colors-content">
- <div className="preferences-color">
- <div className="preferences-color-text">Background Color</div>
- {colorFlyout}
- </div>
- <div className="preferences-color">
- <div className="preferences-color-text">Border/Header Color</div>
- {userColorFlyout}
- </div>
- <div className="preferences-colorScheme">
- <div className="preferences-color-text">Color Scheme</div>
- <div className="preferences-color-controls">
- <select className="scheme-select" onChange={this.changeColorScheme} defaultValue={StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme)}>
- {colorSchemes.map((scheme, i) => <option key={scheme} value={scheme}> {schemeMap[i]} </option>)}
- </select>
+ return (
+ <div className="colors-content">
+ <div className="preferences-color">
+ <div className="preferences-color-text">Background Color</div>
+ {colorFlyout}
+ </div>
+ <div className="preferences-color">
+ <div className="preferences-color-text">Border/Header Color</div>
+ {userColorFlyout}
+ </div>
+ <div className="preferences-colorScheme">
+ <div className="preferences-color-text">Color Scheme</div>
+ <div className="preferences-color-controls">
+ <select className="scheme-select" onChange={this.changeColorScheme} defaultValue={StrCast(Doc.ActiveDashboard?.colorScheme)}>
+ {colorSchemes.map((scheme, i) => (
+ <option key={scheme} value={scheme}>
+ {' '}
+ {schemeMap[i]}{' '}
+ </option>
+ ))}
+ </select>
+ </div>
</div>
</div>
- </div>;
+ );
}
@computed get formatsContent() {
- return <div className="prefs-content">
- <div>
- <input type="checkbox" onChange={e => Doc.UserDoc().showTitle = Doc.UserDoc().showTitle ? undefined : "creationDate"} checked={Doc.UserDoc().showTitle !== undefined} />
- <div className="preferences-check">Show doc header</div>
- </div>
- <div>
- <input type="checkbox" onChange={e => Doc.UserDoc()["documentLinksButton-fullMenu"] = !Doc.UserDoc()["documentLinksButton-fullMenu"]}
- checked={BoolCast(Doc.UserDoc()["documentLinksButton-fullMenu"])} />
- <div className="preferences-check">Show full toolbar</div>
- </div>
- <div>
- <input type="checkbox" onChange={e => DragManager.SetRaiseWhenDragged(!DragManager.GetRaiseWhenDragged())}
- checked={DragManager.GetRaiseWhenDragged()} />
- <div className="preferences-check">Raise on drag</div>
- </div>
- <div>
- <input type="checkbox" onChange={e => FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())}
- checked={FontIconBox.GetShowLabels()} />
- <div className="preferences-check">Show button labels</div>
+ return (
+ <div className="prefs-content">
+ <div>
+ <input type="checkbox" onChange={e => (Doc.UserDoc().showTitle = Doc.UserDoc().showTitle ? undefined : 'creationDate')} checked={Doc.UserDoc().showTitle !== undefined} />
+ <div className="preferences-check">Show doc header</div>
+ </div>
+ <div>
+ <input type="checkbox" onChange={e => (Doc.UserDoc()['documentLinksButton-fullMenu'] = !Doc.UserDoc()['documentLinksButton-fullMenu'])} checked={BoolCast(Doc.UserDoc()['documentLinksButton-fullMenu'])} />
+ <div className="preferences-check">Show full toolbar</div>
+ </div>
+ <div>
+ <input type="checkbox" onChange={e => DragManager.SetRaiseWhenDragged(!DragManager.GetRaiseWhenDragged())} checked={DragManager.GetRaiseWhenDragged()} />
+ <div className="preferences-check">Raise on drag</div>
+ </div>
+ <div>
+ <input type="checkbox" onChange={e => FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())} checked={FontIconBox.GetShowLabels()} />
+ <div className="preferences-check">Show button labels</div>
+ </div>
</div>
- </div>;
+ );
}
@computed get appearanceContent() {
-
- return <div className="tab-content appearances-content">
- <div className="tab-column">
- <div className="tab-column-title">Colors</div>
- <div className="tab-column-content">{this.colorsContent}</div>
- </div>
- <div className="tab-column">
- <div className="tab-column-title">Formats</div>
- <div className="tab-column-content">{this.formatsContent}</div>
+ return (
+ <div className="tab-content appearances-content">
+ <div className="tab-column">
+ <div className="tab-column-title">Colors</div>
+ <div className="tab-column-content">{this.colorsContent}</div>
+ </div>
+ <div className="tab-column">
+ <div className="tab-column-title">Formats</div>
+ <div className="tab-column-content">{this.formatsContent}</div>
+ </div>
</div>
- </div>;
+ );
}
@computed get textContent() {
-
- const fontFamilies = ["Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text", "Roboto"];
- const fontSizes = ["7px", "8px", "9px", "10px", "12px", "14px", "16px", "18px", "20px", "24px", "32px", "48px", "72px"];
+ const fontFamilies = ['Times New Roman', 'Arial', 'Georgia', 'Comic Sans MS', 'Tahoma', 'Impact', 'Crimson Text', 'Roboto'];
+ const fontSizes = ['7px', '8px', '9px', '10px', '12px', '14px', '16px', '18px', '20px', '24px', '32px', '48px', '72px'];
return (
<div className="tab-content appearances-content">
<div className="preferences-font">
<div className="preferences-font-text">Default Font</div>
<div className="preferences-font-controls">
- <select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7px")}>
- {fontSizes.map(size => <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}> {size} </option>)}
+ <select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, '7px')}>
+ {fontSizes.map(size => (
+ <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}>
+ {' '}
+ {size}{' '}
+ </option>
+ ))}
</select>
- <select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, "Times New Roman")} >
- {fontFamilies.map(font => <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}> {font} </option>)}
+ <select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, 'Times New Roman')}>
+ {fontFamilies.map(font => (
+ <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}>
+ {' '}
+ {font}{' '}
+ </option>
+ ))}
</select>
</div>
</div>
- </div>);
+ </div>
+ );
}
@action
changeVal = (e: React.ChangeEvent, pass: string) => {
const value = (e.target as any).value;
switch (pass) {
- case "curr": this.curr_password = value; break;
- case "new": this.new_password = value; break;
- case "conf": this.new_confirm = value; break;
+ case 'curr':
+ this.curr_password = value;
+ break;
+ case 'new':
+ this.new_password = value;
+ break;
+ case 'conf':
+ this.new_confirm = value;
+ break;
}
- }
+ };
@computed get passwordContent() {
- return <div className="password-content">
- <div className="password-content-inputs">
- <input className="password-inputs" type="password" placeholder="current password" onChange={e => this.changeVal(e, "curr")} />
- <input className="password-inputs" type="password" placeholder="new password" onChange={e => this.changeVal(e, "new")} />
- <input className="password-inputs" type="password" placeholder="confirm new password" onChange={e => this.changeVal(e, "conf")} />
- </div>
- <div className="password-content-buttons">
- {!this.passwordResultText ? (null) : <div className={`${this.passwordResultText.startsWith("Error") ? "error" : "success"}-text`}>{this.passwordResultText}</div>}
- <a className="password-forgot" href="/forgotPassword">forgot password?</a>
- <button className="password-submit" onClick={this.changePassword}>submit</button>
+ return (
+ <div className="password-content">
+ <div className="password-content-inputs">
+ <input className="password-inputs" type="password" placeholder="current password" onChange={e => this.changeVal(e, 'curr')} />
+ <input className="password-inputs" type="password" placeholder="new password" onChange={e => this.changeVal(e, 'new')} />
+ <input className="password-inputs" type="password" placeholder="confirm new password" onChange={e => this.changeVal(e, 'conf')} />
+ </div>
+ <div className="password-content-buttons">
+ {!this.passwordResultText ? null : <div className={`${this.passwordResultText.startsWith('Error') ? 'error' : 'success'}-text`}>{this.passwordResultText}</div>}
+ <a className="password-forgot" href="/forgotPassword">
+ forgot password?
+ </a>
+ <button className="password-submit" onClick={this.changePassword}>
+ submit
+ </button>
+ </div>
</div>
- </div>;
+ );
}
@computed get accountOthersContent() {
- return <div className="account-others-content">
- <button onClick={this.googleAuthorize} value="data">Authorize Google Acc</button>
- </div>;
+ return (
+ <div className="account-others-content">
+ <button onClick={this.googleAuthorize} value="data">
+ Authorize Google Acc
+ </button>
+ </div>
+ );
}
@computed get accountsContent() {
- return <div className="tab-content accounts-content">
- <div className="tab-column">
- <div className="tab-column-title">Password</div>
- <div className="tab-column-content">{this.passwordContent}</div>
- </div>
- <div className="tab-column">
- <div className="tab-column-title">Others</div>
- <div className="tab-column-content">{this.accountOthersContent}</div>
+ return (
+ <div className="tab-content accounts-content">
+ <div className="tab-column">
+ <div className="tab-column-title">Password</div>
+ <div className="tab-column-content">{this.passwordContent}</div>
+ </div>
+ <div className="tab-column">
+ <div className="tab-column-title">Others</div>
+ <div className="tab-column-content">{this.accountOthersContent}</div>
+ </div>
</div>
- </div>;
+ );
}
@computed get modesContent() {
- return <div className="tab-content modes-content">
- <div className="tab-column">
- <div className="tab-column-title">Modes</div>
- <div className="tab-column-content">
- <select className="modes-select" onChange={this.selectUserMode} defaultValue={Doc.noviceMode ? "Novice" : "Developer"}>
- <option key={"Novice"} value={"Novice"}> Novice </option>
- <option key={"Developer"} value={"Developer"}> Developer</option>
- </select>
- <div className="modes-playground">
- <input className="playground-check" type="checkbox" checked={this.playgroundMode} onChange={this.playgroundModeToggle} />
- <div className="playground-text">Playground Mode</div>
+ return (
+ <div className="tab-content modes-content">
+ <div className="tab-column">
+ <div className="tab-column-title">Modes</div>
+ <div className="tab-column-content">
+ <select className="modes-select" onChange={this.selectUserMode} defaultValue={Doc.noviceMode ? 'Novice' : 'Developer'}>
+ <option key={'Novice'} value={'Novice'}>
+ {' '}
+ Novice{' '}
+ </option>
+ <option key={'Developer'} value={'Developer'}>
+ {' '}
+ Developer
+ </option>
+ </select>
+ <div className="modes-playground">
+ <input className="playground-check" type="checkbox" checked={this.playgroundMode} onChange={this.playgroundModeToggle} />
+ <div className="playground-text">Playground Mode</div>
+ </div>
</div>
</div>
- </div>
- <div className="tab-column">
- <div className="tab-column-title">Permissions</div>
- <div className="tab-column-content">
- <button onClick={() => GroupManager.Instance?.open()}>Manage groups</button>
- <div className="default-acl">
- <input className="acl-check" type="checkbox" checked={BoolCast(Doc.defaultAclPrivate)}
- onChange={action(() => Doc.defaultAclPrivate = !Doc.defaultAclPrivate)} />
- <div className="acl-text">Default access private</div>
+ <div className="tab-column">
+ <div className="tab-column-title">Permissions</div>
+ <div className="tab-column-content">
+ <button onClick={() => GroupManager.Instance?.open()}>Manage groups</button>
+ <div className="default-acl">
+ <input className="acl-check" type="checkbox" checked={BoolCast(Doc.defaultAclPrivate)} onChange={action(() => (Doc.defaultAclPrivate = !Doc.defaultAclPrivate))} />
+ <div className="acl-text">Default access private</div>
+ </div>
</div>
</div>
</div>
-
- </div>;
+ );
}
-
private get settingsInterface() {
// const pairs = [{ title: "Password", ele: this.passwordContent }, { title: "Modes", ele: this.modesContent },
// { title: "Accounts", ele: this.accountsContent }, { title: "Preferences", ele: this.preferencesContent }];
- const tabs = [{ title: "Accounts", ele: this.accountsContent }, { title: "Modes", ele: this.modesContent },
- { title: "Appearance", ele: this.appearanceContent }, { title: "Text", ele: this.textContent }];
+ const tabs = [
+ { title: 'Accounts', ele: this.accountsContent },
+ { title: 'Modes', ele: this.modesContent },
+ { title: 'Appearance', ele: this.appearanceContent },
+ { title: 'Text', ele: this.textContent },
+ ];
- return <div className="settings-interface">
- <div className="settings-panel">
- <div className="settings-tabs">
- {tabs.map(tab => <div key={tab.title} className={"tab-control " + (this.activeTab === tab.title ? "active" : "inactive")} onClick={action(() => this.activeTab = tab.title)}>{tab.title}</div>)}
- </div>
+ return (
+ <div className="settings-interface">
+ <div className="settings-panel">
+ <div className="settings-tabs">
+ {tabs.map(tab => (
+ <div key={tab.title} className={'tab-control ' + (this.activeTab === tab.title ? 'active' : 'inactive')} onClick={action(() => (this.activeTab = tab.title))}>
+ {tab.title}
+ </div>
+ ))}
+ </div>
- <div className="settings-user">
- <div className="settings-username">{Doc.CurrentUserEmail}</div>
- <button className="logout-button" onClick={() => window.location.assign(Utils.prepend("/logout"))} >
- {CurrentUserUtils.GuestDashboard ? "Exit" : "Log Out"}
- </button>
+ <div className="settings-user">
+ <div className="settings-username">{Doc.CurrentUserEmail}</div>
+ <button className="logout-button" onClick={() => window.location.assign(Utils.prepend('/logout'))}>
+ {Doc.GuestDashboard ? 'Exit' : 'Log Out'}
+ </button>
+ </div>
</div>
- </div>
- <div className="close-button" onClick={this.close}>
- <FontAwesomeIcon icon={"times"} color="black" size={"lg"} />
- </div>
+ <div className="close-button" onClick={this.close}>
+ <FontAwesomeIcon icon={'times'} color="black" size={'lg'} />
+ </div>
- <div className="settings-content">
- {tabs.map(tab => <div key={tab.title} className={"tab-section " + (this.activeTab === tab.title ? "active" : "inactive")}>{tab.ele}</div>)}
+ <div className="settings-content">
+ {tabs.map(tab => (
+ <div key={tab.title} className={'tab-section ' + (this.activeTab === tab.title ? 'active' : 'inactive')}>
+ {tab.ele}
+ </div>
+ ))}
+ </div>
</div>
- </div>;
-
+ );
}
render() {
- return <MainViewModal
- contents={this.settingsInterface}
- isDisplayed={this.isOpen}
- interactive={true}
- closeOnExternalClick={this.close}
- dialogueBoxStyle={{ width: "500px", height: "300px", background: Cast(Doc.SharingDoc().userColor, "string", null) }} />;
+ return (
+ <MainViewModal
+ contents={this.settingsInterface}
+ isDisplayed={this.isOpen}
+ interactive={true}
+ closeOnExternalClick={this.close}
+ dialogueBoxStyle={{ width: '500px', height: '300px', background: Cast(Doc.SharingDoc().userColor, 'string', null) }}
+ />
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 1acb3ab38..793027ea1 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -17,7 +17,6 @@ import { MainViewModal } from '../views/MainViewModal';
import { DocumentView } from '../views/nodes/DocumentView';
import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
import { SearchBox } from '../views/search/SearchBox';
-import { CurrentUserUtils } from './CurrentUserUtils';
import { DocumentManager } from './DocumentManager';
import { GroupManager, UserOptions } from './GroupManager';
import { GroupMemberView } from './GroupMemberView';
@@ -180,7 +179,7 @@ export class SharingManager extends React.Component<{}> {
const target = targetDoc || this.targetDoc!;
const acl = `acl-${normalizeEmail(user.email)}`;
const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
- const isDashboard = DocListCast(CurrentUserUtils.MyDashboards.data).indexOf(target) !== -1;
+ const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
return !docs
@@ -211,7 +210,7 @@ export class SharingManager extends React.Component<{}> {
const target = targetDoc || this.targetDoc!;
const key = normalizeEmail(StrCast(group.title));
const acl = `acl-${key}`;
- const isDashboard = DocListCast(CurrentUserUtils.MyDashboards.data).indexOf(target) !== -1;
+ const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
@@ -279,7 +278,7 @@ export class SharingManager extends React.Component<{}> {
else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc);
});
} else {
- const dashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+ const dashboards = DocListCast(Doc.MyDashboards.data);
docs.forEach(doc => {
const isDashboard = dashboards.indexOf(doc) !== -1;
if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
@@ -291,7 +290,7 @@ export class SharingManager extends React.Component<{}> {
* Sets the background of the Dashboard if it has been shared as a visual indicator
*/
setDashboardBackground = async (doc: Doc, permission: SharingPermissions) => {
- if (Doc.IndexOf(doc, DocListCast(CurrentUserUtils.MyDashboards.data)) !== -1) {
+ if (Doc.IndexOf(doc, DocListCast(Doc.MyDashboards.data)) !== -1) {
if (permission !== SharingPermissions.None) {
doc.isShared = true;
doc.backgroundColor = 'green';
@@ -329,7 +328,7 @@ export class SharingManager extends React.Component<{}> {
*/
removeGroup = (group: Doc) => {
if (group.docsShared) {
- const dashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+ const dashboards = DocListCast(Doc.MyDashboards.data);
DocListCast(group.docsShared).forEach(doc => {
const acl = `acl-${StrCast(group.title)}`;
const isDashboard = dashboards.indexOf(doc) !== -1;