From 526fb50ba814d91cf9c69d75cb5b95a91c2dd924 Mon Sep 17 00:00:00 2001 From: Jenny Yu Date: Mon, 13 Jun 2022 21:27:33 -0700 Subject: feat: pop up modal for naming dashboard --- src/client/util/CurrentUserUtils.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 60bfc165b..aa7c2a39b 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1222,7 +1222,8 @@ export class CurrentUserUtils { Doc.UserDoc().activeDashboard = undefined; } - public static createNewDashboard = async (userDoc: Doc, id?: string) => { + public static createNewDashboard = async (userDoc: Doc, id?: string, name?: string) => { + console.log(name) const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true); const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc; const dashboardCount = DocListCast(dashboards.data).length + 1; @@ -1235,8 +1236,9 @@ export class CurrentUserUtils { _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: `Dashboard ${dashboardCount}` }, id, "row"); + 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 @@ -1247,7 +1249,8 @@ export class CurrentUserUtils { userDoc.activePresentation = presentation; Doc.AddDocToList(dashboards, "data", dashboardDoc); - // CurrentUserUtils.openDashboard(userDoc, dashboardDoc); + // open this new dashboard + // Doc.UserDoc().activeDashboard = dashboardDoc; } public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) { -- cgit v1.2.3-70-g09d2 From fbdddaeba99f5dbffffa4668e53b27006b7895f6 Mon Sep 17 00:00:00 2001 From: Jenny Yu Date: Mon, 13 Jun 2022 22:09:38 -0700 Subject: feat: automatically open dashboard on create --- src/client/util/CurrentUserUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index aa7c2a39b..1538062bb 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1250,7 +1250,8 @@ export class CurrentUserUtils { Doc.AddDocToList(dashboards, "data", dashboardDoc); // open this new dashboard - // Doc.UserDoc().activeDashboard = dashboardDoc; + Doc.UserDoc().activeDashboard = dashboardDoc; + Doc.UserDoc().activePage = "dashboard"; } public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) { -- cgit v1.2.3-70-g09d2 From da6283875ec7f59af602e14ed19fcab93533f377 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 14 Jun 2022 13:00:27 -0400 Subject: made shared with me doc count decrement when a document is viewed (double-clicked, dragged out) --- src/client/documents/Documents.ts | 4 ++-- src/client/util/CurrentUserUtils.ts | 12 ++++++---- src/client/views/collections/TreeView.tsx | 2 +- src/client/views/nodes/button/FontIconBadge.tsx | 30 ++++++++++++------------- src/client/views/nodes/button/FontIconBox.tsx | 2 +- src/fields/Doc.ts | 1 + 6 files changed, 28 insertions(+), 23 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b290b2d58..d2937b83f 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -198,7 +198,6 @@ export class DocumentOptions { hideAllLinks?: boolean; // whether all individual blue anchor dots should be hidden isTemplateForField?: string; // the field key for which the containing document is a rendering template isTemplateDoc?: boolean; - watchedDocuments?: Doc; // list of documents an icon doc monitors in order to display a badge count targetScriptKey?: string; // where to write a template script (used by collections with click templates which need to target onClick, onDoubleClick, etc) templates?: List; hero?: ImageField; // primary image that best represents a compound document (e.g., for a buxton device document that has multiple images) @@ -251,6 +250,7 @@ export class DocumentOptions { numBtnMax?: number; numBtnMin?: number; switchToggle?: boolean; + badgeValue?: ScriptField; //LINEAR VIEW linearViewIsExpanded?: boolean; // is linear view expanded @@ -294,7 +294,7 @@ export class DocumentOptions { treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items. treeViewGrowsHorizontally?: boolean; // whether an embedded tree view of the document can grow horizontally without growing vertically - + treeViewChildDoubleClick?: ScriptField; // // Action Button buttonMenu?: boolean; // whether a action button should be displayed buttonMenuDoc?: Doc; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index aa7c2a39b..bb78fe4e7 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -23,6 +23,7 @@ import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView"; +import { DocumentView } from "../views/nodes/DocumentView"; import { OverlayView } from "../views/OverlayView"; import { DocumentManager } from "./DocumentManager"; import { DragManager } from "./DragManager"; @@ -354,6 +355,7 @@ export class CurrentUserUtils { } static menuBtnDescriptions(doc: Doc) { + const badgeScript = ScriptField.MakeFunction("((len) => len ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length)") return [ { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' }, { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' }, @@ -361,7 +363,7 @@ export class CurrentUserUtils { { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" }, { title: "Imports", target: Cast(doc.myImportDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' }, { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' }, - { title: "Shared with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc }, + { title: "Shared with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', badgeValue: badgeScript}, { title: "Trails", target: Cast(doc.myTrails, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' }, { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" }, ]; @@ -370,7 +372,7 @@ export class CurrentUserUtils { static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { if (doc.menuStack === undefined) { await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId); // sets up the right sidebar collection for mobile upload documents and sharing - const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, watchedDocuments, hidden }) => + const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, badgeValue, hidden }) => Docs.Create.FontIconDocument({ icon, btnType: ButtonType.MenuButton, @@ -387,7 +389,7 @@ export class CurrentUserUtils { _removeDropProperties: new List(["dropAction", "_stayInCollection"]), _width: 60, _height: 60, - watchedDocuments, + badgeValue, onClick: ScriptField.MakeScript(click, { scriptContext: "any" }) }) ); @@ -918,11 +920,12 @@ export class CurrentUserUtils { // TODO:glr NOTE: treeViewHideTitle & _showTitle may be confusing, treeViewHideTitle is for the editable title (just for tree view), _showTitle is to show the Document title for any document if (doc.mySharedDocs === undefined) { let sharedDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(sharingDocumentId + "outer"); + const dblClkScript = ScriptField.MakeScript("{scriptContext.openLevel(documentView); addDocToList(scriptContext.props.treeView.props.Document, 'viewed', documentView.rootDoc);}", {scriptContext:"any", documentView:Doc.name}) if (!sharedDocs) { sharedDocs = Docs.Create.TreeDocument([], { title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, - _chromeHidden: true, boxShadow: "0 0", + _chromeHidden: true, boxShadow: "0 0", treeViewChildDoubleClick: dblClkScript, dontRegisterView: true, 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'" }, sharingDocumentId + "outer", sharingDocumentId); (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment; @@ -1069,6 +1072,7 @@ export class CurrentUserUtils { // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme; } + return doc; } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 8824750a3..59dc5671b 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -613,7 +613,7 @@ export class TreeView extends React.Component { return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!); } - onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); + onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick,(!this.props.treeView.outlineMode ? this._openScript?.():null)); refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document); ignoreEvent = (e: any) => { diff --git a/src/client/views/nodes/button/FontIconBadge.tsx b/src/client/views/nodes/button/FontIconBadge.tsx index cf86b5e07..3b5aac221 100644 --- a/src/client/views/nodes/button/FontIconBadge.tsx +++ b/src/client/views/nodes/button/FontIconBadge.tsx @@ -7,30 +7,30 @@ import { DragManager } from "../../../util/DragManager"; import "./FontIconBadge.scss"; interface FontIconBadgeProps { - collection: Doc | undefined; + value: string | undefined; } @observer export class FontIconBadge extends React.Component { _notifsRef = React.createRef(); - onPointerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, - (e: PointerEvent) => { - const dragData = new DragManager.DocumentDragData([this.props.collection!]); - DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y); - return true; - }, - returnFalse, emptyFunction, false); - } + // onPointerDown = (e: React.PointerEvent) => { + // setupMoveUpEvents(this, e, + // (e: PointerEvent) => { + // const dragData = new DragManager.DocumentDragData([this.props.collection!]); + // DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y); + // return true; + // }, + // returnFalse, emptyFunction, false); + // } render() { - if (!(this.props.collection instanceof Doc)) return (null); - const length = DocListCast(this.props.collection.data).filter(d => GetEffectiveAcl(d) !== AclPrivate).length; // Object.keys(d).length).length; // filter out any documents that we can't read + if (this.props.value === undefined) return (null); return
-
0 ? { "display": "initial" } : { "display": "none" }} - onPointerDown={this.onPointerDown} > - {length} +
+ {this.props.value}
; } diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index a1b9023f3..97e6eddfe 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -521,7 +521,7 @@ export class FontIconBox extends DocComponent() {
{this.icon === "pres-trail" ? trailsIcon : } {menuLabel} - +
); break; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 6abc27b23..7b72787d1 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1436,6 +1436,7 @@ ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return Doc.cop ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return Doc.delegateDragFactory(dragFactory); }); ScriptingGlobals.add(function copyField(field: any) { return Field.Copy(field); }); ScriptingGlobals.add(function docList(field: any) { return DocListCast(field); }); +ScriptingGlobals.add(function addDocToList(doc: Doc, field: string, added:Doc) { return Doc.AddDocToList(doc,field, added); }); ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); }); ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); }); ScriptingGlobals.add(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); }); -- cgit v1.2.3-70-g09d2 From 6dc7a0d37f055c57135b91d4d54174ee156b61f5 Mon Sep 17 00:00:00 2001 From: Jenny Yu Date: Wed, 15 Jun 2022 19:46:10 -0700 Subject: share and remove dashboard from context menu --- src/client/util/CurrentUserUtils.ts | 7 +++++++ src/client/views/DashboardView.tsx | 19 ++++++++----------- src/client/views/topbar/TopBar.tsx | 3 --- 3 files changed, 15 insertions(+), 14 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 199ef701d..c0318e86b 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1226,6 +1226,13 @@ export class CurrentUserUtils { Doc.UserDoc().activeDashboard = undefined; } + public static async removeDashboard(dashboard: Doc) { + const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data); + if (dashboards && dashboards.length > 0) { + Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard); + } + } + public static createNewDashboard = async (userDoc: Doc, id?: string, name?: string) => { console.log(name) const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true); diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 716653ea5..37a70f165 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -14,6 +14,7 @@ import { DocumentManager } from "../util/DocumentManager"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { ContextMenuProps } from "./ContextMenuItem"; import { simulateMouseClick } from "../../Utils"; +import { SharingManager } from "../util/SharingManager"; enum DashboardGroup { MyDashboards, SharedDashboards @@ -60,12 +61,6 @@ export class DashboardView extends React.Component { this.abortCreateNewDashboard(); } - showContextMenu = (e: React.MouseEvent) => { - // DocumentViewInternal.SelectAfterContextMenu = false; - // simulateMouseClick(this._docRef?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); - // DocumentViewInternal.SelectAfterContextMenu = true; - } - @computed get namingInterface() { return
@@ -78,7 +73,7 @@ export class DashboardView extends React.Component { _downX: number = 0; _downY: number = 0; @action - onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => { + onContextMenu = (dashboard: Doc, e?: React.MouseEvent, pageX?: number, pageY?: number) => { // the touch onContextMenu is button 0, the pointer onContextMenu is button 2 if (e) { e.preventDefault(); @@ -91,12 +86,12 @@ export class DashboardView extends React.Component { const cm = ContextMenu.Instance; cm.addItem({ description: "Share Dashboard", event: async () => { - // ... + SharingManager.Instance.open(undefined, dashboard) }, icon: "edit" }); cm.addItem({ description: "Delete Dashboard", event: async () => { - // ... + CurrentUserUtils.removeDashboard(dashboard) }, icon: "trash" }); cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15); @@ -116,7 +111,7 @@ export class DashboardView extends React.Component { {this.getDashboards().map((dashboard) => { const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href; return
{this.onContextMenu(dashboard, e)}} onClick={e => this.clickDashboard(e, dashboard)}>
@@ -124,7 +119,9 @@ export class DashboardView extends React.Component {
{ this._downX = e.clientX; this._downY = e.clientY; - }} onClick={this.onContextMenu}> + }} + onContextMenu={(e) => {this.onContextMenu(dashboard, e)}} + >
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 0665e79ed..e4f48adce 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -76,9 +76,6 @@ export class TopBar extends React.Component {
{CurrentUserUtils.ActiveDashboard ?
{ SharingManager.Instance.open(undefined, activeDashboard) }}> - {/* TODO: if I have edit access to the dashboard, display share - if this is a shared dashboard, display "view original or view annotated" */} - {/* { Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author === Doc.CurrentUserEmail ? "Share": "view original" } */} {GetEffectiveAcl(Doc.GetProto(CurrentUserUtils.ActiveDashboard)) === AclAdmin ? "Share" : "view original"}
: (null)}
window.open( -- cgit v1.2.3-70-g09d2 From 950440f9c73746867454e20f0e5c3a38297728b9 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 16 Jun 2022 09:28:00 -0400 Subject: added unviewed shared dashboards to dashboard view --- src/client/util/CurrentUserUtils.ts | 1 + src/client/views/DashboardView.tsx | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c0318e86b..3085e7e72 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1286,6 +1286,7 @@ export class CurrentUserUtils { public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); } public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); } public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); } + public static get MySharedDocs() { return Cast(Doc.UserDoc().mySharedDocs, Doc, null); } public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); } public static get OverlayDocs() { return DocListCast((Doc.UserDoc().myOverlayDocs as Doc)?.data); } public static set SelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; } diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 9f497dcbf..a105e0790 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -15,6 +15,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { ContextMenuProps } from "./ContextMenuItem"; import { simulateMouseClick } from "../../Utils"; import { SharingManager } from "../util/SharingManager"; +import { CollectionViewType } from "./collections/CollectionView"; enum DashboardGroup { MyDashboards, SharedDashboards @@ -54,6 +55,11 @@ export class DashboardView extends React.Component { } } + getSharedDashboards = () => { + const sharedDashs = DocListCast(CurrentUserUtils.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking); + return sharedDashs.filter((dashboard) => !DocListCast(CurrentUserUtils.MySharedDocs.viewed).includes(dashboard)) + } + createNewDashboard = async (name: string) => { const batch = UndoManager.StartBatch("new dash"); await CurrentUserUtils.createNewDashboard(Doc.UserDoc(), undefined, name); @@ -129,6 +135,31 @@ export class DashboardView extends React.Component {
})} +
+ +
UNVIEWED SHARED DASHBOARDS
+
+ {this.getSharedDashboards().map((dashboard) => { + const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href; + return
{this.onContextMenu(dashboard, e)}} + onClick={e => this.clickDashboard(e, dashboard)}> + +
+
{StrCast(dashboard.title)}
+
{ + this._downX = e.clientX; + this._downY = e.clientY; + }} + onClick={(e) => {this.onContextMenu(dashboard, e)}} + > + +
+
+ +
+ + })}
-- cgit v1.2.3-70-g09d2 From 9c1f0a7d394affe955fb3f422647784b5bdd32b6 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 24 Jun 2022 09:30:40 -0400 Subject: fixed highlighting readonly mode. simplified away some async stuff on load. --- src/client/DocServer.ts | 2 +- src/client/util/CurrentUserUtils.ts | 38 +++++++++++++++++-------------------- src/client/util/SettingsManager.tsx | 5 ++--- 3 files changed, 20 insertions(+), 25 deletions(-) (limited to 'src/client/util') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index dbc4783d8..c9a30b8e3 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -180,7 +180,7 @@ export namespace DocServer { _isReadOnly = true; _CreateField = field => _cache[field[Id]] = field; _UpdateField = emptyFunction; - _RespondToUpdate = emptyFunction; + _RespondToUpdate = emptyFunction; // bcz: option: don't clear RespondToUpdate to continue to receive updates as others change the DB } } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 28d20c56b..e5e12a7e5 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -807,16 +807,13 @@ export class CurrentUserUtils { } /// The database of all links on all documents - static async setupLinkDocs(doc: Doc, linkDatabaseId: string) { - if (doc.myLinkDatabase === undefined) { - let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId); - if (!linkDocs) { - linkDocs = new Doc(linkDatabaseId, true); - (linkDocs as Doc).title = "LINK DATABASE: " + Doc.CurrentUserEmail; - (linkDocs as Doc).author = Doc.CurrentUserEmail; - (linkDocs as Doc).data = new List([]); - (linkDocs as Doc)["acl-Public"] = SharingPermissions.Augment; - } + static setupLinkDocs(doc: Doc, linkDatabaseId: string) { + if (!(Docs.newAccount ? undefined : DocCast(doc.myLinkDatabase))) { + const linkDocs = new Doc(linkDatabaseId, true); + linkDocs.title = "LINK DATABASE: " + Doc.CurrentUserEmail; + linkDocs.author = Doc.CurrentUserEmail; + linkDocs.data = new List([]); + linkDocs["acl-Public"] = SharingPermissions.Augment; doc.myLinkDatabase = new PrefetchProxy(linkDocs); } } @@ -825,14 +822,12 @@ export class CurrentUserUtils { // A user's sharing document is where all documents that are shared to that user are placed. // When the user views one of these documents, it will be added to the sharing documents 'viewed' list field // The sharing document also stores the user's color value which helps distinguish shared documents from personal documents - static async setupSharedDocs(doc: Doc, sharingDocumentId: string) { + static setupSharedDocs(doc: Doc, sharingDocumentId: string) { const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`); const dashboardFilter = ScriptField.MakeFunction(`doc._viewType === '${CollectionViewType.Docking}'`, { doc: Doc.name }); const dblClkScript = "{scriptContext.openLevel(documentView); addDocToList(scriptContext.props.treeView.props.Document, 'viewed', documentView.rootDoc);}"; - const sharedScripts = { - treeViewChildDoubleClick: dblClkScript, - } + const sharedScripts = { treeViewChildDoubleClick: dblClkScript, } const sharedDocOpts:DocumentOptions = { title: "My Shared Docs", userColor: "rgb(202, 202, 202)", @@ -847,8 +842,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'" }; - await DocServer.GetRefField(sharingDocumentId + "outer"); - return this.AssignDocField(doc, "mySharedDocs", (opts) => Docs.Create.TreeDocument([], opts, sharingDocumentId + "outer", sharingDocumentId), sharedDocOpts, undefined, sharedScripts); + this.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts); } /// Import option on the left side button panel @@ -914,16 +908,18 @@ export class CurrentUserUtils { return doc.clickFuncs as Doc; } - static async updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { + + /// Updates the UserDoc to have all required fields, docs, etc. No changes should need to be + /// 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(), {}); - await DocListCastAsync(DocCast(doc.globalGroupDatabase).data); reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data-lastModified"]), async () => { const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data); 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 ?? (doc.system = true); doc.title ?? (doc.title = Doc.CurrentUserEmail); Doc.noviceMode ?? (Doc.noviceMode = true); @@ -945,8 +941,8 @@ export class CurrentUserUtils { doc.savedFilters ?? (doc.savedFilters = new List()); doc.filterDocCount = 0; doc.freezeChildren = "remove|add"; - await this.setupLinkDocs(doc, linkDatabaseId); - await this.setupSharedDocs(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing + this.setupLinkDocs(doc, linkDatabaseId); + this.setupSharedDocs(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile this.setupOverlays(doc); // sets up the overlay panel where documents and other widgets can be added to float over the rest of the dashboard diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 22e33ab1e..080237649 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -67,12 +67,11 @@ export class SettingsManager extends React.Component<{}> { @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(() => { + @undoBatch playgroundModeToggle = action(() => { this.playgroundMode = !this.playgroundMode; if (this.playgroundMode) { DocServer.Control.makeReadOnly(); - addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "pink !important" }); + addStyleSheetRule(SettingsManager._settingsStyle, "topbar-inner-container", { background: "red !important" }); } else DocServer.Control.makeEditable(); }); -- cgit v1.2.3-70-g09d2 From dccf5909f4a4bec35559b23a2f355ab4c7a94086 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 24 Jun 2022 19:03:00 -0400 Subject: fixed myImports pane to show imports. fixed saving/loading zip files of a collection. fixed errors related to NumT casting to number --- src/client/util/CurrentUserUtils.ts | 38 ++++++++++------------ src/client/views/collections/CollectionSubView.tsx | 6 ++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 32 +++++------------- src/fields/Doc.ts | 17 +++++++++- 4 files changed, 45 insertions(+), 48 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index e5e12a7e5..d085c5f5e 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -858,7 +858,8 @@ export class CurrentUserUtils { 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 }; - return this.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" }); + this.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" }); + return myImports; } static setupClickEditorTemplates(doc: Doc) { @@ -1065,29 +1066,24 @@ export class CurrentUserUtils { input.onchange = async _e => { const upload = Utils.prepend("/uploadDoc"); const formData = new FormData(); - const file = input.files && input.files[0]; + const file = input.files?.[0]; if (file?.type === 'application/zip') { - formData.append('file', file); - formData.append('remap', "true"); - const response = await fetch(upload, { method: "POST", body: formData }); - const json = await response.json(); - if (json !== "error") { - const doc = Docs.newAccount ? undefined : await DocServer.GetRefField(json); - if (doc instanceof Doc) { - setTimeout(() => SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => - docs.docs.forEach(d => LinkManager.Instance.addLink(d))), 2000); // need to give solr some time to update so that this query will find any link docs we've added. - } - } + const doc = await Doc.importDocument(file); + // NOT USING SOLR, so need to replace this with something else // if (doc instanceof Doc) { + // setTimeout(() => SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => + // docs.docs.forEach(d => LinkManager.Instance.addLink(d))), 2000); // need to give solr some time to update so that this query will find any link docs we've added. + // } + const list = Cast(CurrentUserUtils.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(); - DocListCastAsync(CurrentUserUtils.MyImports.data).then(async list => { - const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {}); - if (results.length !== input.files?.length) { - alert("Error uploading files - possibly due to unsupported file types"); - } - list?.splice(0, 0, ...results); - disposer(); - }); + const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {}); + 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); + list?.splice(0, 0, ...results); + disposer(); } else { console.log("No file selected"); } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 17fdba764..03450b798 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -274,7 +274,7 @@ export function CollectionSubView(moreProps?: X) { if (docid) { // prosemirror text containing link to dash document DocServer.GetRefField(docid).then(f => { if (f instanceof Doc) { - if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView + if (options.x || options.y) { f.x = options.x as number; f.y = options.y as number; } // should be in CollectionFreeFormView (f instanceof Doc) && addDocument(f); } }); @@ -311,7 +311,7 @@ export function CollectionSubView(moreProps?: X) { const docid = text.replace(Doc.globalServerPath(), "").split("?")[0]; DocServer.GetRefField(docid).then(f => { if (f instanceof Doc) { - if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView + if (options.x || options.y) { f.x = options.x as number; f.y = options.y as number; } // should be in CollectionFreeFormView (f instanceof Doc) && addDocument(f); } }); @@ -445,7 +445,7 @@ export function CollectionSubView(moreProps?: X) { if (completed) completed(set); else { if (isFreeformView && generatedDocuments.length > 1) { - addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!,); + addDocument(DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!,); } else { generatedDocuments.forEach(addDocument); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 542b1fce1..3c2047db7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1660,8 +1660,9 @@ export class CollectionFreeFormView extends CollectionSubView Doc.Zip(this.props.Document) }); - moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(x, y) }); + moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(e.clientX, e.clientY) }); } !mores && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "eye" }); } @@ -1670,28 +1671,13 @@ export class CollectionFreeFormView extends CollectionSubView { - const upload = Utils.prepend("/uploadDoc"); - const formData = new FormData(); - const file = input.files && input.files[0]; - if (file) { - formData.append('file', file); - formData.append('remap', "true"); - const response = await fetch(upload, { method: "POST", body: formData }); - const json = await response.json(); - if (json !== "error") { - const doc = await DocServer.GetRefField(json); - if (doc instanceof Doc) { - const [xx, yy] = this.props.ScreenToLocalTransform().transformPoint(x, y); - doc.x = xx, doc.y = yy; - this.props.addDocument?.(doc); - setTimeout(() => - SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => { - docs.docs.forEach(d => LinkManager.Instance.addLink(d)); - }), 2000); // need to give solr some time to update so that this query will find any link docs we've added. - } - } - } + input.onchange = _e => { + input.files && Doc.importDocument(input.files[0]).then(doc => { + if (doc instanceof Doc) { + const [xx, yy] = this.getTransform().transformPoint(x, y); + doc.x = xx, doc.y = yy; + this.props.addDocument?.(doc);} + }); }; input.click(); } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 981514b25..4fe6eb1e7 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -658,7 +658,7 @@ export namespace Doc { const zip = new JSZip(); - zip.file(doc.title + ".json", docString); + zip.file("doc.json", docString); // // Generate a directory within the Zip file structure // var img = zip.folder("images"); @@ -1289,6 +1289,21 @@ export namespace Doc { } } + export async function importDocument(file:File) { + const upload = Utils.prepend("/uploadDoc"); + const formData = new FormData(); + if (file) { + formData.append('file', file); + formData.append('remap', "true"); + const response = await fetch(upload, { method: "POST", body: formData }); + const json = await response.json(); + if (json !== "error") { + const doc = await DocServer.GetRefField(json); + return doc; + } + } + return undefined; + } export namespace Get { -- cgit v1.2.3-70-g09d2 From 550b5e93097e0708199a9cf88714754d197b3b97 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 27 Jun 2022 12:48:54 -0400 Subject: fixed initialization issues with creating user documents unnecessarily. fixed updating doc icon templates to apply to documents already created. --- src/client/documents/Documents.ts | 10 +- src/client/util/CurrentUserUtils.ts | 64 ++++++----- src/client/util/LinkManager.ts | 123 +++++++++++---------- src/client/views/Main.tsx | 3 +- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/fields/Doc.ts | 2 +- 6 files changed, 104 insertions(+), 100 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 84e6258ac..3780df5b9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -105,7 +105,6 @@ export class DocumentOptions { userColor?: STRt = new StrInfo("color associated with a Dash user (seen in header fields of shared documents)"); color?: STRt = new StrInfo("foreground color data doc"); backgroundColor?: STRt = new StrInfo("background color for data doc"); - _backgroundColor?: STRt = new StrInfo("background color for each template layout doc (overrides backgroundColor)"); _autoHeight?: BOOLt = new BoolInfo("whether document automatically resizes vertically to display contents"); _headerHeight?: NUMt = new NumInfo("height of document header used for displaying title"); _headerFontSize?: NUMt = new NumInfo("font size of header of custom notes"); @@ -328,6 +327,7 @@ export class DocumentOptions { text?: string; textTransform?: string; // is linear view expanded letterSpacing?: string; // is linear view expanded + iconTemplate?: string; // name of icon template style selectedIndex?: number; // which item in a linear view has been selected using the "thumb doc" ui clipboard?: Doc; searchQuery?: string; // for quersyBox @@ -1288,9 +1288,7 @@ export namespace DocUtils { const batch = UndoManager.StartBatch("makeCustomViewClicked"); runInAction(() => { doc.layoutKey = "layout_" + templateSignature; - if (doc[doc.layoutKey] === undefined) { - createCustomView(doc, creator, templateSignature, docLayoutTemplate); - } + createCustomView(doc, creator, templateSignature, docLayoutTemplate); }); batch.end(); return doc; @@ -1318,7 +1316,9 @@ export namespace DocUtils { const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false }; if (docLayoutTemplate) { - Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined); + if (docLayoutTemplate !== doc[customName]) { + Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined); + } } else { let fieldTemplate: Opt; if (doc.data instanceof RichTextField || typeof (doc.data) === "string") { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 5c77041e0..f585277d8 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -205,29 +205,37 @@ export class CurrentUserUtils { const reqdOpts = { title: "icon templates", _height: 75, system: true }; const templateIconsDoc = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts)); - const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: (opts:DocumentOptions) => Doc) => { + const makeIconTemplate = (type: DocumentType | undefined, templateField: string, opts:DocumentOptions) => { const iconFieldName = "icon" + (type ? "_" + type : ""); - return DocCast(templateIconsDoc[iconFieldName] ?? (templateIconsDoc[iconFieldName] = MakeTemplate(iconTemplate({onClick:deiconifyScript(), system: true}), true, iconFieldName, templateField))) ; + const curIcon = DocCast(templateIconsDoc[iconFieldName]); + let creator = labelBox; + switch (opts.iconTemplate) { + case DocumentType.IMG : creator = imageBox; break; + 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))), + {onClick:"deiconifyView(documentView)"}); }; - const deiconifyScript = () => ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }); - const labelBox = (opts: object) => Docs.Create.LabelDocument({ + const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({ textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts }); - const imageBox = (url: Opt, opts: object) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, _width: 360 / 4, _height: 270 / 4, _showTitle: "title", ...opts }); - const fontBox = (opts:DocumentOptions) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts }); + const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _showTitle: "title", ...opts }); + const fontBox = (opts:DocumentOptions, data?:string) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts }); const iconTemplates = [ - makeIconTemplate(undefined, "title", (opts) => labelBox({ ...opts, _backgroundColor: "dimgray" })), - makeIconTemplate(DocumentType.AUDIO, "title", (opts) => labelBox({ ...opts, _backgroundColor: "lightgreen" })), - makeIconTemplate(DocumentType.PDF, "title", (opts) => labelBox({ ...opts, _backgroundColor: "pink" })), - makeIconTemplate(DocumentType.WEB, "title", (opts) => labelBox({...opts, _backgroundColor: "brown" })), - makeIconTemplate(DocumentType.RTF, "text", (opts) => labelBox({ ...opts, _showTitle: "creationDate" })), - makeIconTemplate(DocumentType.IMG, "data", (opts) => imageBox("", { ...opts, _height: undefined, })), - makeIconTemplate(DocumentType.COL, "icon", (opts) => imageBox(undefined, opts)), - makeIconTemplate(DocumentType.VID, "icon", (opts) => imageBox(undefined, opts)), - makeIconTemplate(DocumentType.BUTTON, "data", (opts) => fontBox(opts)), + makeIconTemplate(undefined, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "dimgray"}), + makeIconTemplate(DocumentType.AUDIO, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "lightgreen"}), + makeIconTemplate(DocumentType.PDF, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "pink"}), + makeIconTemplate(DocumentType.WEB, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "brown"}), + makeIconTemplate(DocumentType.RTF, "text", { iconTemplate:DocumentType.LABEL, _showTitle: "creationDate"}), + makeIconTemplate(DocumentType.IMG, "data", { iconTemplate:DocumentType.IMG, _height: undefined}), + makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}), + makeIconTemplate(DocumentType.VID, "icon", { iconTemplate:DocumentType.IMG}), + makeIconTemplate(DocumentType.BUTTON,"data", { iconTemplate:DocumentType.FONTICON}), //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now - makeIconTemplate("transcription" as any, "transcription", (opts) => labelBox({ ...opts, _backgroundColor: "orange" })), - // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})) + 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); } @@ -779,23 +787,23 @@ export class CurrentUserUtils { /// Initializes all the default buttons for the top bar context menu static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") { - const ctxtMenuBtnsDoc = DocCast(doc[field]); + 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 ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => { const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title); if (!params.subMenu) { return this.setupContextMenuButton(params, menuBtnDoc); } else { - const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, title:"submenu", + const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: true, linearViewSubMenu: true, linearViewExpandable: true, }; return this.AssignScripts(this.AssignOpts(menuBtnDoc, reqdSubMenuOpts) ?? - this.linearButtonList(reqdSubMenuOpts, params.subMenu.map(sub => + (ctxtMenuBtnsDoc[StrCast(params.title)]= this.linearButtonList(reqdSubMenuOpts, params.subMenu.map(sub => this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title)) - )), undefined, params.funcs); + ))), undefined, params.funcs); } }); - const reqdCtxtOpts = { title: "context menu buttons", flexGap: 0, childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }; - return this.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, ctxtMenuBtns); + return this.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns); } /// collection of documents rendered in the overlay layer above all tabs and other UI @@ -957,12 +965,13 @@ export class CurrentUserUtils { 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 - setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); 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) doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme; } + new LinkManager(); + DocServer.UPDATE_SERVER_CACHE(); return doc; } static setupFieldInfos(doc:Doc, field="fieldInfos") { @@ -1009,12 +1018,7 @@ export class CurrentUserUtils { await Docs.Prototypes.initialize(); const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc; Docs.newAccount &&(userDoc.activePage = "home"); - const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId); - (await DocListCastAsync(Doc.LinkDBDoc()?.data))?.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); - }); - return updated; + return this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId); }); } else { throw new Error("There should be a user id! Why does Dash think there isn't one?"); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 2100b1195..d51cd350d 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,7 +1,6 @@ -import { validationResult } from "express-validator/check"; import { action, observable, observe } from "mobx"; import { computedFn } from "mobx-utils"; -import { DirectLinksSym, Doc, DocListCast, Field, Opt } from "../../fields/Doc"; +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"; @@ -35,72 +34,74 @@ export class LinkManager { constructor() { LinkManager._instance = this; this.createLinkrelationshipLists(); - setTimeout(() => { - LinkManager.userLinkDBs = []; - const addLinkToDoc = (link: Doc) => { - const a1Prom = link?.anchor1; - const a2Prom = link?.anchor2; - 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); - } - })); - }); - }; - const remLinkFromDoc = (link: Doc) => { - const a1 = link?.anchor1; - const a2 = link?.anchor2; - Promise.all([a1, a2]).then(action(() => { + LinkManager.userLinkDBs = []; + const addLinkToDoc = (link: Doc) => { + const a1Prom = link?.anchor1; + const a2Prom = link?.anchor2; + 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].delete(link); - Doc.GetProto(a2)[DirectLinksSym].delete(link); - Doc.GetProto(link)[DirectLinksSym].delete(link); + Doc.GetProto(a1)[DirectLinksSym].add(link); + Doc.GetProto(a2)[DirectLinksSym].add(link); + Doc.GetProto(link)[DirectLinksSym].add(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) - 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)); - added?.forEach((link: any) => addLinkToDoc(toRealField(link))); - removed?.forEach((link: any) => remLinkFromDoc(toRealField(link))); - }); - } - }, true); - }; - observe(LinkManager.userLinkDBs, change => { + }); + }; + 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); + } + })); + }; + 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(watchUserLinkDB); break; + 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); - LinkManager.addLinkDB(Doc.LinkDBDoc()); - }); + 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)); + added?.forEach((link: any) => addLinkToDoc(toRealField(link))); + removed?.forEach((link: any) => remLinkFromDoc(toRealField(link))); + }); + } + }, 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); + })); } diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 49c2dcf34..17841c055 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -10,7 +10,6 @@ import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { LinkManager } from "../util/LinkManager"; import { RecordingApi } from "../util/RecordingApi"; import { CollectionView } from "./collections/CollectionView"; -import { DashboardView } from './DashboardView'; import { MainView } from "./MainView"; AssignAllExtensions(); @@ -24,6 +23,7 @@ AssignAllExtensions(); await CurrentUserUtils.loadUserDocument(info.id); } else { await Docs.Prototypes.initialize(); + new LinkManager(); } document.getElementById('root')!.addEventListener('wheel', event => { if (event.ctrlKey) { @@ -37,7 +37,6 @@ AssignAllExtensions(); d.setTime(d.getTime() + (100 * 24 * 60 * 60 * 1000)); const expires = "expires=" + d.toUTCString(); document.cookie = `loadtime=${loading};${expires};path=/`; - new LinkManager(); new RecordingApi; ReactDOM.render(, document.getElementById('root')); })(); \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 081a1a924..17f1106d0 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -517,7 +517,7 @@ export class MarqueeView extends React.Component Date: Mon, 27 Jun 2022 12:56:18 -0400 Subject: restored removeDashboard --- src/client/util/CurrentUserUtils.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index f585277d8..d98eddb2e 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1132,6 +1132,13 @@ export class CurrentUserUtils { CurrentUserUtils.ActiveDashboard = undefined; } + public static removeDashboard = async (dashboard:Doc) => { + const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data); + if (dashboards && dashboards.length > 1) { + if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard)!); + Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard); + } + } public static createNewDashboard = (id?: string, name?: string) => { const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true); const dashboards = CurrentUserUtils.MyDashboards; @@ -1223,14 +1230,7 @@ ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.creat 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(async function removeDashboard(dashboard: Doc) { - const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data); - if (dashboards && dashboards.length > 1) { - if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard)!); - Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard); - } -}, - "Remove Dashboard from Dashboards"); +ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) { CurrentUserUtils.removeDashboard(dashboard); }, "Remove Dashboard from Dashboards"); ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { const dashboardAlias = Doc.MakeAlias(dashboard); Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias); -- cgit v1.2.3-70-g09d2 From 0b9f5fb60d5ee3b589e2e4628c5f1a737757492d Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 27 Jun 2022 13:04:01 -0400 Subject: from last --- src/client/util/CurrentUserUtils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index d98eddb2e..6e115ea3c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1029,6 +1029,7 @@ export class CurrentUserUtils { public static _urlState: HistoryUtil.DocUrl; public static openDashboard = (doc: Doc, fromHistory = false) => { + if (!doc) return; CurrentUserUtils.MainDocId = doc[Id]; Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc); @@ -1134,9 +1135,10 @@ export class CurrentUserUtils { public static removeDashboard = async (dashboard:Doc) => { const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data); - if (dashboards && dashboards.length > 1) { + 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) => { -- cgit v1.2.3-70-g09d2 From b1eb13fc6264b1272daba824ff0e4cd2bca8a6fa Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 28 Jun 2022 12:23:44 -0400 Subject: cleaned up some more currentUserUtils - fixed some issues with prev/next key frame appearing. --- src/client/documents/Documents.ts | 29 +++++- src/client/util/CurrentUserUtils.ts | 111 ++++----------------- .../views/collections/CollectionDockingView.tsx | 37 +++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 19 +++- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/topbar/TopBar.tsx | 3 +- src/fields/Doc.ts | 25 +---- 7 files changed, 97 insertions(+), 129 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3780df5b9..4690d856d 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -13,7 +13,7 @@ import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../fields/Types"; import { AudioField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from "../../fields/URLField"; -import { SharingPermissions } from "../../fields/util"; +import { inheritParentAcls, SharingPermissions } from "../../fields/util"; import { Upload } from "../../server/SharedMediaTypes"; import { aggregateBounds, OmitKeys, Utils } from "../../Utils"; import { YoutubeBox } from "../apis/youtube/YoutubeBox"; @@ -1266,7 +1266,7 @@ export namespace DocUtils { const documentList: ContextMenuProps[] = DocListCast(DocListCast(CurrentUserUtils.MyTools?.data)[0]?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ description: ":" + StrCast(dragDoc.title).replace("Untitled ",""), event: undoBatch((args: { x: number, y: number }) => { - const newDoc = Doc.copyDragFactory(dragDoc); + const newDoc = DocUtils.copyDragFactory(dragDoc); if (newDoc) { newDoc.author = Doc.CurrentUserEmail; newDoc.x = x; @@ -1479,9 +1479,34 @@ export namespace DocUtils { } return generatedDocuments; } + + // copies the specified drag factory document + export function copyDragFactory(dragFactory: Doc) { + if (!dragFactory) return undefined; + const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); + ndoc && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, "data", Doc.GetProto(ndoc)); + if (ndoc && dragFactory["dragFactory-count"] !== undefined) { + dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; + Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true); + } + + if (ndoc && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc); + + return ndoc; + } + export function delegateDragFactory(dragFactory: Doc) { + const ndoc = Doc.MakeDelegateWithProto(dragFactory); + if (ndoc && dragFactory["dragFactory-count"] !== undefined) { + dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; + Doc.GetProto(ndoc).title = ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(); + } + return ndoc; + } } ScriptingGlobals.add("Docs", Docs); +ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return DocUtils.copyDragFactory(dragFactory); }); +ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return DocUtils.delegateDragFactory(dragFactory); }); ScriptingGlobals.add(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; }); ScriptingGlobals.add(function generateLinkTitle(self: Doc) { const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null).title : ""; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 6e115ea3c..2a0702a58 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -755,8 +755,8 @@ export class CurrentUserUtils { CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, CollectionViewType.Grid]), title: "Perspective", toolTip: "View", width: 100,btnType: ButtonType.DropdownList,ignoreClick: true, scripts: { script: 'setView(value, _readOnly_)'}}, - { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}}, - { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}}, + { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode() || !selectedDocumentType(undefined, "freeform")'}}, + { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode() || !selectedDocumentType(undefined, "freeform")'}}, { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, scripts: { script: 'setBackgroundColor(value, _readOnly_)'},funcs: {hidden: '!selectedDocumentType()'}}, // Only when a document is selected { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, scripts: { script: 'setHeaderColor(value, _readOnly_)'}, funcs: {hidden: '!selectedDocumentType()'}}, { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, scripts: { onClick: 'toggleOverlay(_readOnly_)'}, funcs: {hidden: '!selectedDocumentType(undefined, "freeform", true)'}}, // Only when floating document is selected in freeform @@ -1028,8 +1028,10 @@ export class CurrentUserUtils { public static _urlState: HistoryUtil.DocUrl; - public static openDashboard = (doc: Doc, fromHistory = false) => { - if (!doc) return; + /// 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); @@ -1097,46 +1099,15 @@ export class CurrentUserUtils { }; input.click(); } + + public static snapshotDashboard() { return CollectionDockingView.TakeSnapshot(CurrentUserUtils.ActiveDashboard); } - public static CaptureDashboardThumbnail() { - const activeDashboard = CurrentUserUtils.ActiveDashboard; - const docView = CollectionDockingView.Instance.props.DocumentView?.(); - const content = docView?.ContentDiv; - if (docView && content && activeDashboard) { - const _width = Number(getComputedStyle(content).width.replace("px","")); - const _height = Number(getComputedStyle(content).height.replace("px","")); - return CollectionFreeFormView.UpdateIcon( - docView.layoutDoc[Id] + "-icon" + (new Date()).getTime(), - content, - _width, _height, - _width, _height, 0, 1, true, docView.layoutDoc[Id] + "-icon", - (iconFile, _nativeWidth, _nativeHeight) => { - const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: docView.rootDoc.title+"-icon", _width, _height, _nativeWidth, _nativeHeight}); - const proto = Cast(img.proto, Doc, null)!; - proto["data-nativeWidth"] = _width; - proto["data-nativeHeight"] = _height; - Doc.GetProto(activeDashboard).thumb = img; - }); - } - - } - - public static async snapshotDashboard() { - if (CurrentUserUtils.ActiveDashboard) { - const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); - Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy); - CurrentUserUtils.openDashboard(copy); - } - } - - public static closeActiveDashboard = () => { - CurrentUserUtils.ActiveDashboard = undefined; - } + 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)!); + if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard)); Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard); if (!dashboards.length) CurrentUserUtils.ActivePage = "home"; } @@ -1214,14 +1185,6 @@ export class CurrentUserUtils { public static get ActiveTool(): InkTool { return StrCast(Doc.UserDoc().activeTool, InkTool.None) as InkTool; } } -ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) { - const copy = Doc.copyDragFactory(dragFactory); - if (copy) { - CollectionDockingView.AddSplit(copy, "right"); - const view = DocumentManager.Instance.getFirstDocumentView(copy); - view && SelectionManager.SelectView(view, false); - } -}); ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.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"); @@ -1232,52 +1195,14 @@ ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.creat 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(async function removeDashboard(dashboard: Doc) { CurrentUserUtils.removeDashboard(dashboard); }, "Remove Dashboard from Dashboards"); -ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { - const dashboardAlias = Doc.MakeAlias(dashboard); - Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias); - CurrentUserUtils.openDashboard(dashboardAlias); -}, - "adds Dashboard to set of Dashboards"); - -ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) { - let selected = SelectionManager.Docs().length ? SelectionManager.Docs()[0] : undefined; - if (selected && checkParent) { - const parentDoc: Doc = Cast(selected.context, Doc, null); - selected = parentDoc; - } - if (selected && docType && selected.type === docType) return true; - else if (selected && colType && selected.viewType === colType) return true; - else if (selected && !colType && !docType) return true; - else return false; +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 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; }); ScriptingGlobals.add(function makeTopLevelFolder() { - const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true }); - TreeView._editTitleOnLoad = { id: folder[Id], parent: undefined }; - return Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", folder); -}); -ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { - if (readOnly) return; - const sel = SelectionManager.Views()[0]; - const col = (sel.ComponentView as CollectionFreeFormView); - const currentFrame = Cast(sel.props.Document._currentFrame, "number", null); - if (currentFrame === undefined) { - sel.props.Document._currentFrame = 0; - CollectionFreeFormDocumentView.setupKeyframes(col.childDocs, 0); - } - CollectionFreeFormDocumentView.updateKeyframe(col.childDocs, currentFrame || 0); - sel.rootDoc._currentFrame = Math.max(0, (currentFrame || 0) + 1); - sel.rootDoc.lastFrame = Math.max(NumCast(sel.rootDoc._currentFrame), NumCast(sel.rootDoc.lastFrame)); -}); -ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) { - if (readOnly) return; - const sel = SelectionManager.Views()[0]; - const col = (sel.ComponentView as CollectionFreeFormView); - const currentFrame = Cast(sel.props.Document._currentFrame, "number", null); - if (currentFrame === undefined) { - sel.props.Document._currentFrame = 0; - CollectionFreeFormDocumentView.setupKeyframes(col.childDocs, 0); - } - CollectionFreeFormDocumentView.gotoKeyframe(col.childDocs.slice()); - sel.rootDoc._currentFrame = Math.max(0, (currentFrame || 0) - 1); + 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)); }); \ No newline at end of file diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 07fcd6a7d..0830b6fdf 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -4,12 +4,12 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from "mo import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import * as GoldenLayout from "../../../client/goldenLayout"; -import { DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc"; +import { Doc, DocListCast, Opt } from "../../../fields/Doc"; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; -import { listSpec } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from "../../../fields/Types"; +import { ImageField } from '../../../fields/URLField'; import { inheritParentAcls } from '../../../fields/util'; import { emptyFunction, incrementTitleCopy } from '../../../Utils'; import { DocServer } from "../../DocServer"; @@ -19,14 +19,15 @@ import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { DragManager } from "../../util/DragManager"; import { InteractionUtils } from '../../util/InteractionUtils'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; +import { SelectionManager } from '../../util/SelectionManager'; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { LightboxView } from '../LightboxView'; import "./CollectionDockingView.scss"; +import { CollectionFreeFormView } from './collectionFreeForm'; import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; import { CollectionViewType } from './CollectionView'; import { TabDocView } from './TabDocView'; import React = require("react"); -import { SelectionManager } from '../../util/SelectionManager'; const _global = (window /* browser */ || global /* node */) as any; @observer @@ -373,13 +374,34 @@ export class CollectionDockingView extends CollectionSubView() { } } - public static async Copy(doc: Doc, clone = false) { + public CaptureThumbnail() { + const content = this.props.DocumentView?.()?.ContentDiv; + if (content) { + const _width = Number(getComputedStyle(content).width.replace("px","")); + const _height = Number(getComputedStyle(content).height.replace("px","")); + return CollectionFreeFormView.UpdateIcon( + this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), + content, + _width, _height, + _width, _height, 0, 1, true, this.layoutDoc[Id] + "-icon", + (iconFile, _nativeWidth, _nativeHeight) => { + const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: this.rootDoc.title+"-icon", _width, _height, _nativeWidth, _nativeHeight}); + const proto = Cast(img.proto, Doc, null)!; + proto["data-nativeWidth"] = _width; + proto["data-nativeHeight"] = _height; + this.dataDoc.thumb = img; + }); + } + + } + public static async TakeSnapshot(doc: Doc|undefined, clone = false) { + if (!doc) return undefined; let json = StrCast(doc.dockingConfig); if (clone) { - const cloned = (await Doc.MakeClone(doc)); + const cloned = await Doc.MakeClone(doc); Array.from(cloned.map.entries()).map(entry => json = json.replace(entry[0], entry[1][Id])); Doc.GetProto(cloned.clone).dockingConfig = json; - return cloned.clone; + return CurrentUserUtils.openDashboard(cloned.clone); } const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); const origtabids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) || []; @@ -395,7 +417,8 @@ export class CollectionDockingView extends CollectionSubView() { json = json.replace(origtab[Id], newtab[Id]); return newtab; }); - return Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) }); + const copy = Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) }); + return CurrentUserUtils.openDashboard(await copy); } @action diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 3c2047db7..b9da4faa4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -153,6 +153,21 @@ export class CollectionFreeFormView extends CollectionSubView { + const currentFrame = Cast(this.Document._currentFrame, "number", null); + if (currentFrame === undefined) { + this.Document._currentFrame = 0; + CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0); + } + if (back) { + CollectionFreeFormDocumentView.gotoKeyframe(this.childDocs.slice()); + this.Document._currentFrame = Math.max(0, (currentFrame || 0) - 1); + } else { + CollectionFreeFormDocumentView.updateKeyframe(this.childDocs, currentFrame || 0); + this.Document._currentFrame = Math.max(0, (currentFrame || 0) + 1); + this.Document.lastFrame = Math.max(NumCast(this.Document._currentFrame), NumCast(this.Document.lastFrame)); + } + } @action setKeyFrameEditing = (set: boolean) => this._keyframeEditing = set; getKeyFrameEditing = () => this._keyframeEditing; onBrowseClickHandler = () => this.props.onBrowseClick?.() || ScriptCast(this.layoutDoc.onBrowseClick); @@ -2086,4 +2101,6 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY }); Doc.linkFollowHighlight(dv?.props.Document, false); } -ScriptingGlobals.add(CollectionBrowseClick); \ No newline at end of file +ScriptingGlobals.add(CollectionBrowseClick); +ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(); }); +ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) { !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true); }); \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 17f1106d0..ab8a34d5a 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -156,7 +156,7 @@ export class MarqueeView extends React.Component { - CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => { + CollectionDockingView.Instance.CaptureThumbnail()?.then(() => { CurrentUserUtils.ActivePage = "home"; CurrentUserUtils.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use }); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index a5d952176..b30ca644d 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -23,7 +23,7 @@ import { listSpec } from "./Schema"; import { ComputedField, ScriptField } from "./ScriptField"; import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types"; import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from "./URLField"; -import { deleteProperty, GetEffectiveAcl, getField, getter, inheritParentAcls, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util"; +import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util"; import JSZip = require("jszip"); export namespace Field { @@ -1242,27 +1242,6 @@ export namespace Doc { return !curPres ? false : DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1; } - export function copyDragFactory(dragFactory: Doc) { - const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); - ndoc && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, "data", Doc.GetProto(ndoc)); - if (ndoc && dragFactory["dragFactory-count"] !== undefined) { - dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; - Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true); - } - - if (ndoc && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc); - - return ndoc; - } - export function delegateDragFactory(dragFactory: Doc) { - const ndoc = Doc.MakeDelegateWithProto(dragFactory); - if (ndoc && dragFactory["dragFactory-count"] !== undefined) { - dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; - Doc.GetProto(ndoc).title = ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(); - } - return ndoc; - } - export function toIcon(doc?: Doc, isOpen?: boolean) { switch (StrCast(doc?.type)) { case DocumentType.IMG: return "image"; @@ -1454,8 +1433,6 @@ ScriptingGlobals.add(function getProto(doc: any) { return Doc.GetProto(doc); }); ScriptingGlobals.add(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); }); ScriptingGlobals.add(function getAlias(doc: any) { return Doc.MakeAlias(doc); }); ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); }); -ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return Doc.copyDragFactory(dragFactory); }); -ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return Doc.delegateDragFactory(dragFactory); }); ScriptingGlobals.add(function copyField(field: any) { return Field.Copy(field); }); ScriptingGlobals.add(function docList(field: any) { return DocListCast(field); }); ScriptingGlobals.add(function addDocToList(doc: Doc, field: string, added:Doc) { return Doc.AddDocToList(doc,field, added); }); -- cgit v1.2.3-70-g09d2 From aa979bcd302e48982707fb6f12403a48a721f147 Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Wed, 29 Jun 2022 19:17:00 -0400 Subject: got basic file upload to work --- src/client/documents/Documents.ts | 12 +++++++++--- src/client/util/CurrentUserUtils.ts | 2 +- src/fields/URLField.ts | 1 + src/server/ApiManagers/UploadManager.ts | 1 + src/server/DashUploadUtils.ts | 18 +++++++++++++++++- src/server/DataVizUtils.ts | 13 +++++++++++++ 6 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 src/server/DataVizUtils.ts (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 59a1d41a8..b9d879c55 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -12,7 +12,7 @@ import { RichTextField } from "../../fields/RichTextField"; import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../fields/Types"; -import { AudioField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from "../../fields/URLField"; +import { AudioField, CsvField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { Upload } from "../../server/SharedMediaTypes"; import { aggregateBounds, OmitKeys, Utils } from "../../Utils"; @@ -940,8 +940,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.PRESELEMENT), undefined, { ...(options || {}) }); } - export function DataVizDocument(options?: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), undefined, { title: "Data Viz", ...options }); + export function DataVizDocument(url: string, options?: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: "Data Viz", ...options }); } export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { @@ -1253,6 +1253,11 @@ export namespace DocUtils { if (!options._width) options._width = 400; if (!options._height) options._height = (options._width as number) * 1200 / 927; } + if (type.indexOf("csv") !== -1) { + ctor = Docs.Create.DataVizDocument; + if (!options._width) options._width = 400; + if (!options._height) options._height = (options._width as number) * 1200 / 927; + } //TODO:al+glr // if (type.indexOf("map") !== -1) { // ctor = Docs.Create.MapDocument; @@ -1278,6 +1283,7 @@ export namespace DocUtils { ctor = Docs.Create.WebDocument; options = { ...options, _width: 400, _height: 512, title: path, }; } + return ctor ? ctor(path, options) : undefined; } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 52fbda9a9..40268693e 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -294,7 +294,7 @@ export class CurrentUserUtils { {key: "WebCam", creator: opts => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, title: "recording", recording:true, system: true, cloneFieldFilter: new List(["system"]) }}, {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, }}, {key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }}, - {key: "DataViz", creator: opts => Docs.Create.DataVizDocument(opts), opts: { _width: 300, _height: 300 }}, + // {key: "DataViz", creator: opts => Docs.Create.DataVizDocument(opts), opts: { _width: 300, _height: 300 }}, {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true,}}, {key: "Presentation",creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 500, _viewType: CollectionViewType.Stacking, targetDropAction: "alias" as any, _chromeHidden: true, boxShadow: "0 0" }}, {key: "Tab", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 500, _height: 800, _backgroundGridShow: true, }}, diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts index 3e7542e46..36dd56a1a 100644 --- a/src/fields/URLField.ts +++ b/src/fields/URLField.ts @@ -59,6 +59,7 @@ export const nullAudio = "https://actions.google.com/sounds/v1/alarms/beep_short @scriptingGlobal @Deserializable("pdf") export class PdfField extends URLField { } @scriptingGlobal @Deserializable("web") export class WebField extends URLField { } @scriptingGlobal @Deserializable("map") export class MapField extends URLField { } +@scriptingGlobal @Deserializable("csv") export class CsvField extends URLField { } @scriptingGlobal @Deserializable("youtube") export class YoutubeField extends URLField { } @scriptingGlobal @Deserializable("webcam") export class WebCamField extends URLField { } diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index e7b7056a1..04a11f410 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -24,6 +24,7 @@ export enum Directory { text = "text", pdf_thumbnails = "pdf_thumbnails", audio = "audio", + csv = "csv", } export function serverPathToFile(directory: Directory, filename: string) { diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index 552ab57a5..5f46bcc88 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -17,6 +17,7 @@ import { resolvedServerUrl } from "./server_Initialization"; import { AcceptableMedia, Upload } from './SharedMediaTypes'; import request = require('request-promise'); import formidable = require('formidable'); +import { csvParser } from './DataVizUtils'; const { exec } = require("child_process"); const parse = require('pdf-parse'); const ffmpeg = require("fluent-ffmpeg"); @@ -85,7 +86,7 @@ export namespace DashUploadUtils { const category = types[0]; let format = `.${types[1]}`; console.log(green(`Processing upload of file (${name}) and format (${format}) with upload type (${type}) in category (${category}).`)); - + switch (category) { case "image": if (imageFormats.includes(format)) { @@ -116,6 +117,11 @@ export namespace DashUploadUtils { if (audioFormats.includes(format)) { return UploadAudio(file, format); } + case "text": + if (types[1] == "csv") { + return UploadCsv(file); + } + } console.log(red(`Ignoring unsupported file (${name}) with upload type (${type}).`)); @@ -135,6 +141,16 @@ export namespace DashUploadUtils { return MoveParsedFile(file, Directory.pdfs, undefined, result.text); } + async function UploadCsv(file: File) { + const { path: sourcePath } = file; + // read the file as a string + const data = readFileSync(sourcePath, 'utf8'); + // split the string into an array of lines + return MoveParsedFile(file, Directory.csv, undefined, data); + // console.log(csvParser(data)); + + } + const manualSuffixes = [".webm"]; async function UploadAudio(file: File, format: string) { diff --git a/src/server/DataVizUtils.ts b/src/server/DataVizUtils.ts new file mode 100644 index 000000000..4fd0ca6ff --- /dev/null +++ b/src/server/DataVizUtils.ts @@ -0,0 +1,13 @@ +export function csvParser(csv: string) { + const lines = csv.split("\n"); + const headers = lines[0].split(","); + const data = lines.slice(1).map(line => { + const values = line.split(","); + const obj: any = {}; + for (let i = 0; i < headers.length; i++) { + obj[headers[i]] = values[i]; + } + return obj; + }); + return data; +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From ea6e63648b21c46672b1b7cb1da0cbaa6857d0c1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 30 Jun 2022 11:01:46 -0400 Subject: fixed up some more initialization --- src/client/util/CurrentUserUtils.ts | 37 +++++++--------------- src/client/util/DropConverter.ts | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 6 ++-- 3 files changed, 16 insertions(+), 29 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 2a0702a58..a39081d10 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -9,30 +9,25 @@ import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { Cast, DateCast, DocCast, FieldValue, NumCast, PromiseValue, ScriptCast, StrCast } from "../../fields/Types"; -import { ImageField, nullAudio } from "../../fields/URLField"; +import { nullAudio } from "../../fields/URLField"; 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 { CollectionDockingView } from "../views/collections/CollectionDockingView"; -import { CollectionFreeFormView } from "../views/collections/collectionFreeForm"; import { TreeViewType } from "../views/collections/CollectionTreeView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; import { TreeView } from "../views/collections/TreeView"; import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; -import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView"; -import { DocumentView } from "../views/nodes/DocumentView"; import { OverlayView } from "../views/OverlayView"; -import { DocumentManager } from "./DocumentManager"; import { DragManager } from "./DragManager"; -import { makeTemplate, MakeTemplate } from "./DropConverter"; +import { MakeTemplate } from "./DropConverter"; import { HistoryUtil } from "./History"; import { LinkManager } from "./LinkManager"; import { ScriptingGlobals } from "./ScriptingGlobals"; -import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; import { ColorScheme } from "./SettingsManager"; import { SharingManager } from "./SharingManager"; @@ -149,11 +144,7 @@ export class CurrentUserUtils { } return templateBtn; }; - const makeTemp = (doc:Doc) => { - doc.isTemplateDoc = makeTemplate(doc); - return doc; - } - return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? this.createToolButton( {...btnOpts, dragFactory: makeTemp(template(templateOpts))}), reqdScripts); + return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? this.createToolButton( {...btnOpts, dragFactory: MakeTemplate(template(templateOpts))}), reqdScripts); }); const reqdOpts:DocumentOptions = { @@ -176,11 +167,7 @@ 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; - const makeTemp = (doc:Doc, noteType?:string) => { - doc.isTemplateDoc = makeTemplate(doc, true, noteType??"Note"); - return doc; - } - return this.AssignOpts(noteType, reqdOpts) ?? makeTemp(Docs.Create.TextDocument("",reqdOpts), opts.noteType); + return this.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note"); }); const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, system: true }; @@ -280,8 +267,7 @@ export class CurrentUserUtils { // " " + // " " + // "
"; - Doc.GetProto(header).isTemplateDoc = makeTemplate(Doc.GetProto(header), true, "headerView"); - Doc.GetProto(header).title = "Untitled Header"; + MakeTemplate(Doc.GetProto(header), true, "Untitled Header"); return header; } const emptyThings:{key:string, // the field name where the empty thing will be stored @@ -705,7 +691,7 @@ export class CurrentUserUtils { { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, scripts: {script: '{ return setFontColor(value, _readOnly_); }'}}, { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", scripts: {onClick: '{ return toggleBold(_readOnly_); }'} }, { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", scripts: {onClick: '{ return toggleItalic(_readOnly_);}'} }, - { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", scripts: {onClick:'{ return toggleUnderline(_readOnly_);}'} }, + { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", scripts: {onClick: '{ return toggleUnderline(_readOnly_);}'} }, { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", scripts: {onClick: '{ return setBulletList("bullet", _readOnly_);}'} }, { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", scripts: {onClick: '{ return setBulletList("decimal", _readOnly_);}'} }, @@ -797,10 +783,11 @@ export class CurrentUserUtils { const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: true, linearViewSubMenu: true, linearViewExpandable: true, }; - return this.AssignScripts(this.AssignOpts(menuBtnDoc, reqdSubMenuOpts) ?? - (ctxtMenuBtnsDoc[StrCast(params.title)]= this.linearButtonList(reqdSubMenuOpts, params.subMenu.map(sub => - this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title)) - ))), undefined, params.funcs); + 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 this.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns); @@ -985,7 +972,7 @@ export class CurrentUserUtils { switch (options.fieldType) { case "boolean": opts.fieldValues = new List(options.values as any); break; case "number": opts.fieldValues = new List(options.values as any); break; - case "Doc": opts.fieldValues = new List(options.values as any); break; + case Doc.name: opts.fieldValues = new List(options.values as any); break; default: opts.fieldValues = new List(options.values as any); break;// string, pointerEvents, dimUnit, dropActionType } this.AssignDocField(infos, pair[0], opts => Doc.assign(new Doc(), OmitKeys(opts,["values"]).omit), opts); diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 076afd3a0..256ab5c44 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -20,7 +20,7 @@ export function MakeTemplate(doc: Doc, first: boolean = true, rename: Opt = undefined): boolean { +function makeTemplate(doc: Doc, first: boolean = true, rename: Opt = undefined): boolean { const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; if (layoutDoc.layout instanceof Doc) { // its already a template return true; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 9ae604e9b..8e1698eba 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -29,7 +29,7 @@ import { CurrentUserUtils } from '../../../util/CurrentUserUtils'; import { DictationManager } from '../../../util/DictationManager'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from "../../../util/DragManager"; -import { makeTemplate } from '../../../util/DropConverter'; +import { MakeTemplate } from '../../../util/DropConverter'; import { LinkManager } from '../../../util/LinkManager'; import { SelectionManager } from "../../../util/SelectionManager"; import { SnappingManager } from '../../../util/SnappingManager'; @@ -682,7 +682,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (!this.layoutDoc.isTemplateDoc) { const title = StrCast(this.rootDoc.title); this.rootDoc.title = "text"; - this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc, true, title); + MakeTemplate(this.rootDoc, true, title); } else if (!this.rootDoc.isTemplateDoc) { const title = StrCast(this.rootDoc.title); this.rootDoc.title = "text"; @@ -691,7 +691,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.rootDoc.isTemplateDoc = false; this.rootDoc.isTemplateForField = ""; this.rootDoc.layoutKey = "layout"; - this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc, true, title); + MakeTemplate(this.rootDoc, true, title); setTimeout(() => { this.rootDoc._autoHeight = this.layoutDoc._autoHeight; // autoHeight, width and height this.rootDoc._width = this.layoutDoc._width || 300; // are stored on the template, since we're getting rid of the old template -- cgit v1.2.3-70-g09d2