diff options
Diffstat (limited to 'src/client/util/CurrentUserUtils.ts')
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 164 |
1 files changed, 125 insertions, 39 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 734b96b1e..9a634017a 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1,14 +1,15 @@ -import { computed, observable, reaction, action } from "mobx"; +import { computed, observable, reaction } from "mobx"; import * as rp from 'request-promise'; -import { DataSym, Doc, DocListCast, DocListCastAsync, AclReadonly } from "../../fields/Doc"; +import { DataSym, Doc, DocListCast, DocListCastAsync } 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"; import { listSpec } from "../../fields/Schema"; import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; -import { BoolCast, Cast, NumCast, PromiseValue, StrCast, DateCast } from "../../fields/Types"; +import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; import { nullAudio } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { Utils } from "../../Utils"; @@ -19,6 +20,7 @@ import { Networking } from "../Network"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; +import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; import { LabelBox } from "../views/nodes/LabelBox"; @@ -31,12 +33,10 @@ import { LinkManager } from "./LinkManager"; import { Scripting } from "./Scripting"; import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; -import { UndoManager } from "./UndoManager"; +import { ColorScheme } from "./SettingsManager"; +import { SharingManager } from "./SharingManager"; import { SnappingManager } from "./SnappingManager"; -import { InkTool } from "../../fields/InkField"; -import { computedFn } from "mobx-utils"; -import { CollectionSchemaBooleanCell } from "../views/collections/CollectionSchemaCells"; -import { Console } from "console"; +import { UndoManager } from "./UndoManager"; export let resolvedPorts: { server: number, socket: number }; @@ -409,12 +409,23 @@ export class CurrentUserUtils { selection: { type: "text", anchor: 1, head: 1 }, storedMarks: [] }; - const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { title: "header", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, cloneFieldFilter: new List<string>(["system"]) }, "header"); // text needs to be a space to allow templateText to be created + const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { + title: "text", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all", + _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, _fitWidth: true, + cloneFieldFilter: new List<string>(["system"]) + }, "header"); + const headerBtnHgt = 10; headerTemplate[DataSym].layout = - "<div style={'height:100%'}>" + - " <FormattedTextBox {...props} fieldKey={'header'} dontSelectOnLoad={'true'} ignoreAutoHeight={'true'} pointerEvents='{this._headerPointerEvents||`none`}' fontSize='{this._headerFontSize}px' height='{this._headerHeight}px' background='{this._headerColor||this.target.mySharedDocs.userColor}' />" + - " <FormattedTextBox {...props} fieldKey={'text'} position='absolute' top='{(this._headerHeight)*scale}px' height='calc({100/scale}% - {this._headerHeight}px)'/>" + - "</div>"; + "<HTMLdiv transformOrigin='top left' width='{100/scale}%' height='{100/scale}%' transform='scale({scale})'>" + + ` <FormattedTextBox {...props} dontScale='true' fieldKey={'text'} height='calc(100% - ${headerBtnHgt}px - {this._headerHeight}px)'/>` + + " <FormattedTextBox {...props} dontScale='true' fieldKey={'header'} dontSelectOnLoad='true' ignoreAutoHeight='true' fontSize='{this._headerFontSize}px' height='{(this._headerHeight||1)}px' background='{this._headerColor ||this.target.mySharedDocs.userColor||`lightGray`}' />" + + ` <HTMLdiv fontSize='${headerBtnHgt - 1}px' height='${headerBtnHgt}px' background='yellow' onClick={‘(this._headerHeight=scale*Math.min(Math.max(1,this._height-30),this._headerHeight===1?50:1)) && (this._autoHeightMargins=this._headerHeight+${headerBtnHgt})’} >Metadata</HTMLdiv>` + + "</HTMLdiv>"; + + // "<div style={'height:100%'}>" + + // " <FormattedTextBox {...props} fieldKey={'header'} dontSelectOnLoad={'true'} ignoreAutoHeight={'true'} pointerEvents='{this._headerPointerEvents||`none`}' fontSize='{this._headerFontSize}px' height='{this._headerHeight}px' background='{this._headerColor||this.target.mySharedDocs.userColor}' />" + + // " <FormattedTextBox {...props} fieldKey={'text'} position='absolute' top='{(this._headerHeight)*scale}px' height='calc({100/scale}% - {this._headerHeight}px)'/>" + + // "</div>"; (headerTemplate.proto as Doc).isTemplateDoc = makeTemplate(headerTemplate.proto as Doc, true, "headerView"); doc.emptyHeader = headerTemplate; ((doc.emptyHeader as Doc).proto as Doc)["dragFactory-count"] = 0; @@ -465,7 +476,7 @@ export class CurrentUserUtils { { toolTip: "Tap to create a videoWall", title: "Wall", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWall as Doc }, { toolTip: "Tap to create an audio recorder in a new pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true }, { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc }, - { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true }, + // { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true }, { toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScript as Doc }, { toolTip: "Tap to create a mobile view in a new pane, drag for a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc }, { toolTip: "Tap to create a custom header note document, drag for a custom header note", title: "Custom", icon: "window-maximize", click: 'openOnRight(delegateDragFactory(this.dragFactory))', drag: 'delegateDragFactory(this.dragFactory)', dragFactory: doc.emptyHeader as Doc }, @@ -526,10 +537,9 @@ export class CurrentUserUtils { { title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' }, { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' }, { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc }, - // { title: "Filter", target: Cast(doc.currentFilter, Doc, null), icon: "filter", click: 'selectMainMenu(self)' }, { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' }, - { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' }, - { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' }, + // { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' }, + // { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' }, { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' }, ]; } @@ -547,7 +557,6 @@ export class CurrentUserUtils { dontUndo: true, title, target, - backgroundColor: "black", _dropAction: "alias", _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]), _width: 60, @@ -575,8 +584,10 @@ export class CurrentUserUtils { title: "menuItemPanel", childDropAction: "alias", _chromeHidden: true, + backgroundColor: Colors.DARK_GRAY, + boxShadow: "rgba(0,0,0,0)", dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), - backgroundColor: "black", ignoreClick: true, + ignoreClick: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true @@ -586,8 +597,6 @@ export class CurrentUserUtils { PromiseValue(Cast(doc.menuStack, Doc)).then(stack => { stack && PromiseValue(stack.data).then(btns => { DocListCastAsync(btns).then(bts => bts?.forEach(btn => { - btn.color = "white"; - btn._backgroundColor = ""; btn.dontUndo = true; btn.system = true; if (btn.title === "Catalog" || btn.title === "My Files") { // migration from Catalog to My Files @@ -746,7 +755,7 @@ export class CurrentUserUtils { if (doc.myTools === undefined) { const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], { title: "My Tools", _width: 500, _yMargin: 20, ignoreClick: true, _lockedPosition: true, _forceActive: true, - system: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, + system: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, boxShadow: "0 0", })) as any as Doc; doc.myTools = toolsStack; @@ -758,10 +767,10 @@ export class CurrentUserUtils { await doc.myDashboards; if (doc.myDashboards === undefined) { doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "My Dashboards", _height: 400, childHideLinkButton: true, + title: "My Dashboards", _showTitle: "title", _height: 400, childHideLinkButton: true, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, ignoreClick: true, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true + _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true })); const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`); (doc.myDashboards as any as Doc).contextMenuScripts = new List<ScriptField>([newDashboard!]); @@ -774,7 +783,7 @@ export class CurrentUserUtils { await doc.myPresentations; if (doc.myPresentations === undefined) { doc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "My Presentations", _height: 100, + title: "My Trails", _showTitle: "title", _height: 100, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true @@ -793,7 +802,7 @@ export class CurrentUserUtils { doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true }); doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true }); doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileRoot as Doc, doc.myFileOrphans as Doc], { - title: "My Documents", _height: 100, + title: "My Documents", _showTitle: "title", _height: 100, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, ignoreClick: true, isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true, @@ -807,7 +816,7 @@ export class CurrentUserUtils { // setup Recently Closed library item if (doc.myRecentlyClosedDocs === undefined) { doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "Recently Closed", treeViewShowClearButton: true, childHideLinkButton: true, + title: "Recently Closed", _showTitle: "title", treeViewShowClearButton: true, childHideLinkButton: true, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true @@ -839,7 +848,7 @@ export class CurrentUserUtils { doc.treeViewOpen = true; doc.treeViewExpandedView = "fields"; doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], { - treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, title: "My UserDoc", + treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, title: "My UserDoc", _showTitle: "title", treeViewTruncateTitleWidth: 150, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true })) as any as Doc; @@ -859,11 +868,11 @@ export class CurrentUserUtils { static async setupSidebarButtons(doc: Doc) { CurrentUserUtils.setupSidebarContainer(doc); await CurrentUserUtils.setupToolsBtnPanel(doc); + CurrentUserUtils.setupImportSidebar(doc); CurrentUserUtils.setupDashboards(doc); CurrentUserUtils.setupPresentations(doc); CurrentUserUtils.setupFilesystem(doc); CurrentUserUtils.setupRecentlyClosedDocs(doc); - // CurrentUserUtils.setupFilterDocs(doc); CurrentUserUtils.setupUserDoc(doc); } @@ -891,6 +900,7 @@ export class CurrentUserUtils { (doc["dockedBtn-undo"] as Doc).dontUndo = true; (doc["dockedBtn-redo"] as Doc).dontUndo = true; } + // sets up the default set of documents to be shown in the Overlay layer static setupOverlays(doc: Doc) { if (doc.myOverlayDocs === undefined) { @@ -922,9 +932,10 @@ export class CurrentUserUtils { if (doc.mySharedDocs === undefined) { let sharedDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(sharingDocumentId + "outer"); if (!sharedDocs) { - sharedDocs = Docs.Create.StackingDocument([], { - title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, - _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Add, "_acl-Public": SharingPermissions.Add, _chromeHidden: true, + sharedDocs = Docs.Create.TreeDocument([], { + title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, + _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Add, "_acl-Public": SharingPermissions.Add, + _chromeHidden: true, boxShadow: "0 0", }, sharingDocumentId + "outer", sharingDocumentId); (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Add; } @@ -939,14 +950,14 @@ export class CurrentUserUtils { static setupImportSidebar(doc: Doc) { if (doc.myImportDocs === undefined) { doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { - title: "My ImportDocuments", _forceActive: true, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, + title: "My ImportDocuments", _forceActive: true, ignoreClick: true, _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, childDropAction: "alias", _autoHeight: true, _yMargin: 50, _gridGap: 15, _lockedPosition: true, system: true, _chromeHidden: true, })); } if (doc.myImportPanel === undefined) { const uploads = Cast(doc.myImportDocs, Doc, null); const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", icon: "upload", system: true }); - doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true })); + doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, _showTitle: "title", ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true, boxShadow: "0 0" })); } } @@ -1017,10 +1028,14 @@ export class CurrentUserUtils { const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || []; SnappingManager.SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]); }, { fireImmediately: true }); + // Document properties on load doc.system = true; + doc.darkScheme = ColorScheme.Dark; doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode; doc.title = Doc.CurrentUserEmail; doc._raiseWhenDragged = true; + doc._showLabel = false; + doc._showMenuLabel = true; doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)"); doc.activeInkWidth = StrCast(doc.activeInkWidth, "1"); doc.activeInkBezier = StrCast(doc.activeInkBezier, "0"); @@ -1209,14 +1224,37 @@ export class CurrentUserUtils { }; const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row"); + freeformDoc.context = dashboardDoc; + + // switching the tabs from the datadoc to the regular doc + const dashboardTabs = dashboardDoc[DataSym].data; + dashboardDoc[DataSym].data = new List<Doc>(); + dashboardDoc.data = dashboardTabs; + + // collating all docs on the dashboard to make a data-all field + const allDocs = new List<Doc>(); + const allDocs2 = new List<Doc>(); // Array.from, spread, splice all cause so stack or acl issues for some reason + DocListCast(dashboardTabs).forEach(doc => { + const tabDocs = DocListCast(doc.data); + allDocs.push(...tabDocs); + allDocs2.push(...tabDocs); + }); + dashboardDoc[DataSym]["data-all"] = allDocs; + dashboardDoc["data-all"] = allDocs2; + DocListCast(dashboardDoc.data).forEach(doc => doc.dashboard = dashboardDoc); + DocListCast(dashboardDoc.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(self.dashboard)`) as any; + Doc.AddDocToList(myPresentations, "data", presentation); userDoc.activePresentation = presentation; - const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`); + const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`); const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`); const createDashboard = ScriptField.MakeScript(`createNewDashboard()`); - dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, snapshotDashboard!, createDashboard!]); - dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard"]); + const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`); + const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`); + const removeDashboard = ScriptField.MakeScript('removeDashboard(self)'); + dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, snapshotDashboard!, createDashboard!, shareDashboard!, addToDashboards!, removeDashboard!]); + dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard", "Share Dashboard", "Add to Dashboards", "Remove Dashboard"]); Doc.AddDocToList(dashboards, "data", dashboardDoc); CurrentUserUtils.openDashboard(userDoc, dashboardDoc); @@ -1268,5 +1306,53 @@ Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Insta "returns all the links to the document or its annotations", "(doc: any)"); Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); -Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, - "toggle between regular rendeing and an informal sketch/comic style"); +Scripting.addGlobal(function shareDashboard(dashboard: Doc) { + SharingManager.Instance.open(undefined, dashboard); +}, + "opens sharing dialog for Dashboard"); +Scripting.addGlobal(async function removeDashboard(dashboard: Doc) { + const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data); + if (dashboards && dashboards.length > 1) { + if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboards.find(doc => doc !== dashboard)!); + Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard); + } +}, + "Remove Dashboard from Dashboards"); +Scripting.addGlobal(async function addToDashboards(dashboard: Doc) { + const dashboardAlias = Doc.MakeAlias(dashboard); + + const allDocs = await DocListCastAsync(dashboard[DataSym]["data-all"]); + + // moves the data-all field from the datadoc to the layoutdoc, necessary for off screen docs tab to function properly + dashboard["data-all"] = new List<Doc>(allDocs); + dashboardAlias["data-all"] = new List<Doc>((allDocs || []).map(doc => Doc.MakeAlias(doc))); + + const dockingConfig = JSON.parse(StrCast(dashboardAlias.dockingConfig)); + dockingConfig.content = []; + dashboardAlias.dockingConfig = JSON.stringify(dockingConfig); + + + dashboardAlias.data = new List<Doc>(DocListCast(dashboard.data).map(tabFolder => Doc.MakeAlias(tabFolder))); + DocListCast(dashboardAlias.data).forEach(doc => doc.dashboard = dashboardAlias); + DocListCast(dashboardAlias.data)[0].data = new List<Doc>(); + DocListCast(dashboardAlias.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(self.dashboard)`) as any; + Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias); + CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboardAlias); +}, + "adds Dashboard to set of Dashboards"); + +/** + * Dynamically computes which docs should be rendered in the off-screen tabs tree of a dashboard. + */ +Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) { + if (dashboard[DataSym] instanceof Doc) { + const allDocs = DocListCast(dashboard["data-all"]); + const onScreenTab = DocListCast(dashboard.data)[0]; + const onScreenDocs = DocListCast(onScreenTab.data); + return new List<Doc>(allDocs.reduce((result: Doc[], doc) => { + !onScreenDocs.includes(doc) && !onScreenDocs.includes(doc.aliasOf as Doc) && (result.push(doc)); + return result; + }, [])); + } + return []; +}); |