From bd6b9c40f150fab76e8907c45e29fa809f9acae0 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Fri, 2 Apr 2021 19:04:09 -0400 Subject: dashboard sharing initial setup, inherits acls from dashboard - looks like it works --- src/client/views/DocComponent.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 90449ab6c..447daeb02 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -7,7 +7,7 @@ import { InteractionUtils } from '../util/InteractionUtils'; import { List } from '../../fields/List'; import { DateField } from '../../fields/DateField'; import { ScriptField } from '../../fields/ScriptField'; -import { GetEffectiveAcl, SharingPermissions, distributeAcls, denormalizeEmail } from '../../fields/util'; +import { GetEffectiveAcl, SharingPermissions, distributeAcls, denormalizeEmail, inheritParentAcls } from '../../fields/util'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; import { DocUtils } from '../documents/Documents'; import { returnFalse } from '../../Utils'; @@ -198,15 +198,15 @@ export function ViewBoxAnnotatableComponent

{ for (const [key, value] of Object.entries(this.props.Document[AclSym])) { - if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true); - //else if (this.props.Document[key] === SharingPermissions.Admin) distributeAcls(key, SharingPermissions.Add, d, true); - // else distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true); + if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d); } }); } if (effectiveAcl === AclAddonly) { added.map(doc => { + + if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); doc.context = this.props.Document; if (annotationKey ?? this._annotationKey) Doc.GetProto(doc).annotationOn = this.props.Document; this.props.layerProvider?.(doc, true); @@ -220,6 +220,8 @@ export function ViewBoxAnnotatableComponent

; if (annoDocs) annoDocs.push(...added); -- cgit v1.2.3-70-g09d2 From d252d6dba8b789215ed8da5b66889a26b06a2a18 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Sat, 3 Apr 2021 20:13:58 -0400 Subject: dashboard sharing works, aliases to go --- src/client/util/CurrentUserUtils.ts | 7 +++---- src/client/util/SharingManager.tsx | 24 +++++++++++++++++++++++- src/client/views/PropertiesView.tsx | 2 +- src/fields/Doc.ts | 2 +- 4 files changed, 28 insertions(+), 7 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index f1357e3d7..fdceb60f3 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1189,7 +1189,7 @@ export class CurrentUserUtils { const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`); const createDashboard = ScriptField.MakeScript(`createNewDashboard()`); - const shareDashboard = ScriptField.MakeScript(`shareDashboard()`); + const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`); const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`); dashboardDoc.contextMenuScripts = new List([toggleTheme!, toggleComic!, snapshotDashboard!, createDashboard!, shareDashboard!, addToDashboards!]); dashboardDoc.contextMenuLabels = new List(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard", "Share Dashboard", "Add to Dashboards"]); @@ -1244,9 +1244,8 @@ Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Insta "returns all the links to the document or its annotations", "(doc: any)"); Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); -Scripting.addGlobal(function shareDashboard() { - CurrentUserUtils.ActiveDashboard.isShared = true; - SharingManager.Instance.open(undefined, CurrentUserUtils.ActiveDashboard); +Scripting.addGlobal(function shareDashboard(dashboard: Doc) { + SharingManager.Instance.open(undefined, dashboard); }, "opens sharing dialog for Dashboard"); Scripting.addGlobal(function addToDashboards(dashboard: Doc) { Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboard); }, diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index ded56d1da..ca14154b2 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -5,7 +5,7 @@ import { observer } from "mobx-react"; import * as React from "react"; import Select from "react-select"; import * as RequestPromise from "request-promise"; -import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc"; +import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc"; import { List } from "../../fields/List"; import { Cast, StrCast } from "../../fields/Types"; import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from "../../fields/util"; @@ -17,6 +17,7 @@ import { MainViewModal } from "../views/MainViewModal"; import { DocumentView } from "../views/nodes/DocumentView"; import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox"; import { SearchBox } from "../views/search/SearchBox"; +import { CurrentUserUtils } from "./CurrentUserUtils"; import { DocumentManager } from "./DocumentManager"; import { GroupManager, UserOptions } from "./GroupManager"; import { GroupMemberView } from "./GroupMemberView"; @@ -170,6 +171,7 @@ export class SharingManager extends React.Component<{}> { doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc); distributeAcls(acl, permission as SharingPermissions, doc); + this.setDashboardBackground(doc, permission as SharingPermissions); if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc || doc)); }).some(success => !success); @@ -192,6 +194,7 @@ export class SharingManager extends React.Component<{}> { return !docs.map(doc => { doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc); distributeAcls(acl, permission as SharingPermissions, doc); + this.setDashboardBackground(doc, permission as SharingPermissions); if (group instanceof Doc) { const members: string[] = JSON.parse(StrCast(group.members)); @@ -246,6 +249,25 @@ export class SharingManager extends React.Component<{}> { } } + /** + * Sets the background of the Dashboard if it has been shared as a visual indicator + */ + setDashboardBackground = async (doc: Doc, permission: SharingPermissions) => { + if (Doc.IndexOf(doc, DocListCast(CurrentUserUtils.MyDashboards.data)) !== -1) { + if (permission !== SharingPermissions.None) { + doc.isShared = true; + doc.backgroundColor = "green"; + } + else { + const acls = doc[DataSym][AclSym]; + if (Object.keys(acls).every(key => key === `acl-${Doc.CurrentUserEmailNormalized}` ? true : [AclUnset, AclPrivate].includes(acls[key]))) { + doc.isShared = undefined; + doc.backgroundColor = undefined; + } + } + } + } + /** * Removes the documents shared with a user through a group when the user is removed from the group. * @param group diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index c8ce8bfeb..0fc6c75d0 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -400,7 +400,7 @@ export class PropertiesView extends React.Component { const showAdmin = effectiveAcls.every(acl => acl === AclAdmin); // users in common between all docs - const commonKeys = intersection(...docs.map(doc => this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym][AclSym] && Object.keys(doc[DataSym][AclSym]))); + const commonKeys: string[] = intersection(...docs.map(doc => this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym][AclSym] && Object.keys(doc[DataSym][AclSym]))); const tableEntries = []; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index b37c2fdfe..1719a6445 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -22,7 +22,7 @@ import { listSpec } from "./Schema"; import { ComputedField, ScriptField } from "./ScriptField"; import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types"; import { AudioField, ImageField, PdfField, VideoField, WebField } from "./URLField"; -import { deleteProperty, getField, getter, inheritParentAcls, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util"; +import { deleteProperty, GetEffectiveAcl, getField, getter, inheritParentAcls, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util"; import JSZip = require("jszip"); import { CurrentUserUtils } from "../client/util/CurrentUserUtils"; -- cgit v1.2.3-70-g09d2 From b901d5b16b6a4d53d7975a318017dc9630966abf Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Sat, 3 Apr 2021 22:24:34 -0400 Subject: made dashboard option visible --- src/client/views/search/SearchBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 5c168d8a9..4cbdd622f 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -530,7 +530,7 @@ export class SearchBox extends ViewBoxBaseComponent

CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}> New -- cgit v1.2.3-70-g09d2 From a80143dca8309463af954d09ec1e6d30aa6e3fd1 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Sat, 3 Apr 2021 22:32:58 -0400 Subject: revised previous --- src/client/views/search/SearchBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 4cbdd622f..d3a023ec9 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -530,7 +530,7 @@ export class SearchBox extends ViewBoxBaseComponent
CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}> New -- cgit v1.2.3-70-g09d2 From 0bc38e1ca78a5a2ee21d99ef511f2f744d8c67bb Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Sun, 4 Apr 2021 01:55:12 -0400 Subject: added datadoc print to console --- src/client/views/nodes/DocumentView.tsx | 1 + 1 file changed, 1 insertion(+) (limited to 'src/client/views') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 26cf52f17..3870cbed0 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -740,6 +740,7 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" }); helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" }); !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" }); + !Doc.UserDoc().novice && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" }); cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" }); if (!this.topMost) e?.stopPropagation(); // DocumentViews should stop propagation of this event -- cgit v1.2.3-70-g09d2 From 2e76877dc1c9c5b1c226f5bd0394d17cabfec0b4 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Mon, 17 May 2021 18:01:44 -0400 Subject: trying dynamic off screen docs --- src/client/documents/Documents.ts | 20 ++++++++-- src/client/util/CurrentUserUtils.ts | 33 ++++++++++++++--- src/client/util/SharingManager.tsx | 40 ++++++++++++-------- .../views/collections/CollectionDockingView.tsx | 43 ++++++++++++++-------- src/client/views/collections/TreeView.tsx | 9 ++++- src/fields/util.ts | 18 +++++---- 6 files changed, 115 insertions(+), 48 deletions(-) (limited to 'src/client/views') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 1e2919bad..906603d78 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -648,8 +648,9 @@ export namespace Docs { * constructor just generates a new GUID. This is currently used * only when creating a DockDocument from the current user's already existing * main document. + * @param layoutData whether the fieldKey field on the layout doc should store the data or the data doc */ - function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data", protoId?: string) { + function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data", protoId?: string, layoutData?: boolean) { const viewKeys = ["x", "y", "system"]; // keys that should be addded to the view document even though they don't begin with an "_" const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, "^_"); @@ -660,7 +661,17 @@ export namespace Docs { dataProps[`${fieldKey}-lastModified`] = new DateField; dataProps["acl-Override"] = "None"; dataProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; - dataProps[fieldKey] = data; + + if (layoutData) { + viewProps[fieldKey] = data; + const list = new List(); + console.log(DocListCast(data)); + DocListCast(data).forEach(doc => { + list.push(...DocListCast(doc.data)); + }); + dataProps[fieldKey + "-all"] = list; + } + else dataProps[fieldKey] = data; // so that the list of annotations is already initialised, prevents issues in addonly. // without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do. dataProps[fieldKey + "-annotations"] = new List(); @@ -891,7 +902,7 @@ export namespace Docs { export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { const tabs = TreeDocument(documents, { title: "On-Screen Tabs", childDontRegisterViews: true, freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", _fitWidth: true, system: true }); const all = TreeDocument([], { title: "Off-Screen Tabs", childDontRegisterViews: true, freezeChildren: "add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", system: true }); - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List([tabs, all]), { freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List([tabs, all]), { freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id, undefined, undefined, true); } export function DirectoryImportDocument(options: DocumentOptions = {}) { @@ -1426,4 +1437,7 @@ Scripting.addGlobal(function generateLinkTitle(self: Doc) { const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null).title : ""; const relation = self.linkRelationship || "to"; return `${anchor1title} (${relation}) ${anchor2title}`; +}); +Scripting.addGlobal(function openTabAlias(tab: Doc) { + CollectionDockingView.AddSplit(Doc.MakeAlias(tab), "right"); }); \ No newline at end of file diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 5dbded00e..6fdf649a4 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -750,7 +750,7 @@ export class CurrentUserUtils { title: "My Dashboards", _height: 400, childHideLinkButton: true, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, ignoreClick: true, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true + _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true })); const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`); (doc.myDashboards as any as Doc).contextMenuScripts = new List([newDashboard!]); @@ -851,7 +851,6 @@ export class CurrentUserUtils { CurrentUserUtils.setupPresentations(doc); CurrentUserUtils.setupFilesystem(doc); CurrentUserUtils.setupRecentlyClosedDocs(doc); - // CurrentUserUtils.setupFilterDocs(doc); CurrentUserUtils.setupUserDoc(doc); } @@ -1186,7 +1185,11 @@ export class CurrentUserUtils { title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`, }; 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: `Dashboard ${dashboardCount}` }, id, "row"); // add isFolder:true here? + freeformDoc.context = dashboardDoc; + + DocListCast(dashboardDoc.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(dashboardDoc)`, { dashboardDoc: Doc.name }, { dashboardDoc }) as any; + Doc.AddDocToList(myPresentations, "data", presentation); userDoc.activePresentation = presentation; const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`); @@ -1252,7 +1255,25 @@ Scripting.addGlobal(function shareDashboard(dashboard: Doc) { SharingManager.Instance.open(undefined, dashboard); }, "opens sharing dialog for Dashboard"); -Scripting.addGlobal(function addToDashboards(dashboard: Doc) { Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboard); }, +Scripting.addGlobal(function addToDashboards(dashboard: Doc) { + const dashboardAlias = Doc.MakeAlias(dashboard); + dashboardAlias.data = new List(DocListCast(dashboard.data).map(tabFolder => Doc.MakeAlias(tabFolder))); + // DocListCast(dashboardAlias.data).forEach(tabFolder => { + // tabFolder.data = new List(DocListCast(tabFolder.data).map(tab => Doc.MakeAlias(tab))); + // }); + Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias); + CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboardAlias); +}, "adds Dashboard to set of Dashboards"); -Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, - "toggle between regular rendeing and an informal sketch/comic style"); + +Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) { + const allDocs = DocListCast(dashboard[DataSym]["data-all"]); + console.log(allDocs); + const onScreenTab = DocListCast(dashboard.data)[0]; + const onScreenDocs = DocListCast(onScreenTab.data); + return allDocs.reduce((result: Doc[], doc) => { + !onScreenDocs.includes(doc) && (result.push(doc)); + // console.log(doc); + return result; + }, []); +}); diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 2f8ecd4ee..18f254cd6 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -173,10 +173,14 @@ export class SharingManager extends React.Component<{}> { const target = targetDoc || this.targetDoc!; const acl = `acl-${normalizeEmail(user.email)}`; const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`; + console.log(DocListCast(CurrentUserUtils.MyDashboards.data)); + console.log(target); + console.log(DocListCast(CurrentUserUtils.MyDashboards.data).indexOf(target)); + const isDashboard = DocListCast(CurrentUserUtils.MyDashboards.data).indexOf(target) !== -1; const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document); return !docs.map(doc => { - doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc); + doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard); if (permission === SharingPermissions.None) { if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 1) - 1; @@ -185,7 +189,7 @@ export class SharingManager extends React.Component<{}> { if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1; } - distributeAcls(acl, permission as SharingPermissions, doc); + distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard); this.setDashboardBackground(doc, permission as SharingPermissions); if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); @@ -203,12 +207,13 @@ export class SharingManager extends React.Component<{}> { const target = targetDoc || this.targetDoc!; const key = normalizeEmail(StrCast(group.title)); const acl = `acl-${key}`; + const isDashboard = DocListCast(CurrentUserUtils.MyDashboards.data).indexOf(target) !== -1; const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document); // ! ensures it returns true if document has been shared successfully, false otherwise return !docs.map(doc => { - doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc); + doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc, undefined, undefined, isDashboard); if (permission === SharingPermissions.None) { if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 1) - 1; @@ -217,7 +222,7 @@ export class SharingManager extends React.Component<{}> { if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 0) + 1; } - distributeAcls(acl, permission as SharingPermissions, doc); + distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard); this.setDashboardBackground(doc, permission as SharingPermissions); if (group instanceof Doc) { @@ -267,8 +272,10 @@ export class SharingManager extends React.Component<{}> { }); } else { + const dashboards = DocListCast(CurrentUserUtils.MyDashboards.data); docs.forEach(doc => { - if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc); + const isDashboard = dashboards.indexOf(doc) !== -1; + if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard); }); } } @@ -316,10 +323,11 @@ export class SharingManager extends React.Component<{}> { */ removeGroup = (group: Doc) => { if (group.docsShared) { + const dashboards = DocListCast(CurrentUserUtils.MyDashboards.data); DocListCast(group.docsShared).forEach(doc => { const acl = `acl-${StrCast(group.title)}`; - - distributeAcls(acl, SharingPermissions.None, doc); + const isDashboard = dashboards.indexOf(doc) !== -1; + distributeAcls(acl, SharingPermissions.None, doc, undefined, undefined, isDashboard); const members: string[] = JSON.parse(StrCast(group.members)); const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); @@ -445,16 +453,16 @@ export class SharingManager extends React.Component<{}> { } } - distributeOverCollection = (targetDoc?: Doc) => { - const target = targetDoc || this.targetDoc!; + // distributeOverCollection = (targetDoc?: Doc) => { + // const target = targetDoc || this.targetDoc!; - const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document); - docs.forEach(doc => { - for (const [key, value] of Object.entries(doc[AclSym])) { - distributeAcls(key, this.AclMap.get(value)! as SharingPermissions, target); - } - }); - } + // const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document); + // docs.forEach(doc => { + // for (const [key, value] of Object.entries(doc[AclSym])) { + // distributeAcls(key, this.AclMap.get(value)! as SharingPermissions, target); + // } + // }); + // } /** * Sorting algorithm to sort users. diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 388f9a909..819667834 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -4,7 +4,7 @@ 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 { Doc, DocListCast, Opt, DocListCastAsync } from "../../../fields/Doc"; +import { Doc, DocListCast, Opt, DocListCastAsync, DataSym } from "../../../fields/Doc"; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; @@ -24,6 +24,7 @@ import React = require("react"); import { DocumentType } from '../../documents/DocumentTypes'; import { listSpec } from '../../../fields/Schema'; import { LightboxView } from '../LightboxView'; +import { inheritParentAcls } from '../../../fields/util'; const _global = (window /* browser */ || global /* node */) as any; @observer @@ -160,6 +161,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { } const instance = CollectionDockingView.Instance; if (!instance) return false; + else Doc.AddDocToList(instance.props.Document[DataSym], "data-all", document); const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName); if (!pullSide && stack) { @@ -381,15 +383,22 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { setTimeout(async () => { const sublists = await DocListCastAsync(this.props.Document[this.props.fieldKey]); const tabs = sublists && Cast(sublists[0], Doc, null); - const other = sublists && Cast(sublists[1], Doc, null); + // const other = sublists && Cast(sublists[1], Doc, null); const tabdocs = await DocListCastAsync(tabs?.data); - const otherdocs = await DocListCastAsync(other?.data); - tabs && (Doc.GetProto(tabs).data = new List(docs)); - const otherSet = new Set(); - otherdocs?.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc)); - tabdocs?.filter(doc => !docs.includes(doc) && doc.type !== DocumentType.KVP).forEach(doc => otherSet.add(doc)); - const vals = Array.from(otherSet.values()).filter(val => val instanceof Doc).map(d => d).filter(d => d.type !== DocumentType.KVP); - other && (Doc.GetProto(other).data = new List(vals)); + // const otherdocs = await DocListCastAsync(other?.data); + if (tabs) { + tabs.data = new List(docs); + // DocListCast(tabs.aliases).forEach(tab => tab !== tabs && (tab.data = new List(docs))); + } + // const otherSet = new Set(); + // otherdocs?.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc)); + // tabdocs?.filter(doc => !docs.includes(doc) && doc.type !== DocumentType.KVP).forEach(doc => otherSet.add(doc)); + // const vals = Array.from(otherSet.values()).filter(val => val instanceof Doc).map(d => d).filter(d => d.type !== DocumentType.KVP); + // this.props.Document[DataSym][this.props.fieldKey + "-all"] = new List([...docs, ...vals]); + // if (other) { + // other.data = new List(vals); + // // DocListCast(other.aliases).forEach(tab => tab !== other && (tab.data = new List(vals))); + // } }, 0); } @@ -399,7 +408,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele)); } tabCreated = (tab: any) => { - tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous abs (ie, when dragging a tab around a new tab is created for the old content) + tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content) } stackCreated = (stack: any) => { @@ -407,9 +416,11 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { if (e.target === stack.header?.element[0] && e.button === 2) { const emptyPane = CurrentUserUtils.EmptyPane; emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; - CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { - _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}` - }), "", stack); + const docToAdd = Docs.Create.FreeformDocument([], { + _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`, + }); + this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd); + CollectionDockingView.AddSplit(docToAdd, "", stack); } }); @@ -430,9 +441,11 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { // stack.config.fixed = !stack.config.fixed; // force the stack to have a fixed size const emptyPane = CurrentUserUtils.EmptyPane; emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; - CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { + const docToAdd = Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}` - }), "", stack); + }); + this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd); + CollectionDockingView.AddSplit(docToAdd, "", stack); })); } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 2e98fb508..ba4af2a6d 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -151,7 +151,10 @@ export class TreeView extends React.Component { this.treeViewOpen = !this.treeViewOpen; } else { // choose an appropriate alias or make one. --- choose the first alias that (1) user owns, (2) has no context field ... otherwise make a new alias + // this.props.addDocTab(CurrentUserUtils.ActiveDashboard.isShared ? Doc.MakeAlias(this.props.document) : this.props.document, "add:right"); + // choose an appropriate alias or make one -- -- choose the first alias that (1) the user owns, (2) has no context field - if I own it and someone else does not have it open,, otherwise create an alias this.props.addDocTab(this.props.document, "add:right"); + } } constructor(props: any) { @@ -507,7 +510,11 @@ export class TreeView extends React.Component { [{ script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" }]; } onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); - onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); + + onChildDoubleClick = () => { + console.log(this.props.document.onChildDoubleClick); + return (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick) + }; refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document); ignoreEvent = (e: any) => { diff --git a/src/fields/util.ts b/src/fields/util.ts index a4c99928a..882c7fee8 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -136,11 +136,9 @@ export function denormalizeEmail(email: string) { * Copies parent's acl fields to the child */ export function inheritParentAcls(parent: Doc, child: Doc) { - if (parent.isShared) { - const dataDoc = parent[DataSym]; - for (const key of Object.keys(dataDoc)) { - key.startsWith("acl") && distributeAcls(key, dataDoc[key], child); - } + const dataDoc = parent[DataSym]; + for (const key of Object.keys(dataDoc)) { + key.startsWith("acl") && distributeAcls(key, dataDoc[key], child); } } @@ -228,7 +226,7 @@ function getEffectiveAcl(target: any, user?: string): symbol { * @param inheritingFromCollection whether the target is being assigned rights after being dragged into a collection (and so is inheriting the acls from the collection) * inheritingFromCollection is not currently being used but could be used if acl assignment defaults change */ -export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean, visited?: Doc[]) { +export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean, visited?: Doc[], isDashboard?: boolean) { if (!visited) visited = [] as Doc[]; if (visited.includes(target)) return; visited.push(target); @@ -249,6 +247,12 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc if (GetEffectiveAcl(target) === AclAdmin && (!inheritingFromCollection || !target[key] || HierarchyMapping.get(StrCast(target[key]))! > HierarchyMapping.get(acl)!)) { target[key] = acl; layoutDocChanged = true; + + if (isDashboard) { + DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(d => { + distributeAcls(key, acl, d, inheritingFromCollection, visited); + }); + } } if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) { @@ -263,7 +267,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc links.forEach(link => distributeAcls(key, acl, link, inheritingFromCollection, visited)); // maps over the children of the document - DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).map(d => { + DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + (isDashboard ? "-all" : "")]).map(d => { // this is now on the layoutdoc instead - figure out the "data-all" approach for the datadoc // if (GetEffectiveAcl(d) === AclAdmin && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) { distributeAcls(key, acl, d, inheritingFromCollection, visited); // } -- cgit v1.2.3-70-g09d2 From 77c16aa9f00a9d1b5e31ddf5a68d56f183f13691 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Thu, 20 May 2021 12:47:46 -0400 Subject: dashboard sharing, aliases now work? needs more testing --- src/client/util/CurrentUserUtils.ts | 19 +++++++++++++------ .../views/collections/CollectionDockingView.tsx | 6 +++++- 2 files changed, 18 insertions(+), 7 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index a8327ae93..e143887fa 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1255,12 +1255,20 @@ Scripting.addGlobal(function shareDashboard(dashboard: Doc) { SharingManager.Instance.open(undefined, dashboard); }, "opens sharing dialog for Dashboard"); -Scripting.addGlobal(function addToDashboards(dashboard: Doc) { +Scripting.addGlobal(async function addToDashboards(dashboard: Doc) { const dashboardAlias = Doc.MakeAlias(dashboard); + const dockingConfig = JSON.parse(StrCast(dashboardAlias.dockingConfig)); + dockingConfig.content = []; + dashboardAlias.dockingConfig = JSON.stringify(dockingConfig); + + const allDocs = await DocListCastAsync(dashboard[DataSym]["data-all"]); + + dashboardAlias["data-all"] = new List((allDocs || []).map(doc => Doc.MakeAlias(doc))); + dashboardAlias.data = new List(DocListCast(dashboard.data).map(tabFolder => Doc.MakeAlias(tabFolder))); - // DocListCast(dashboardAlias.data).forEach(tabFolder => { - // tabFolder.data = new List(DocListCast(tabFolder.data).map(tab => Doc.MakeAlias(tab))); - // }); + DocListCast(dashboardAlias.data).forEach(doc => doc.dashboard = dashboardAlias); + DocListCast(dashboardAlias.data)[0].data = new List(); + DocListCast(dashboardAlias.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(self.dashboard)`) as any; Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias); CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboardAlias); }, @@ -1268,13 +1276,12 @@ Scripting.addGlobal(function addToDashboards(dashboard: Doc) { Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) { if (dashboard[DataSym] instanceof Doc) { - const allDocs = DocListCast(dashboard[DataSym]["data-all"]); + const allDocs = DocListCast(dashboard["data-all"]); console.log(allDocs); const onScreenTab = DocListCast(dashboard.data)[0]; const onScreenDocs = DocListCast(onScreenTab.data); return new List(allDocs.reduce((result: Doc[], doc) => { !onScreenDocs.includes(doc) && (result.push(doc)); - // console.log(doc); return result; }, [])); } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 819667834..235fe8950 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -161,7 +161,11 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { } const instance = CollectionDockingView.Instance; if (!instance) return false; - else Doc.AddDocToList(instance.props.Document[DataSym], "data-all", document); + else { + const docList = DocListCast(instance.props.Document[DataSym]["data-all"]); + !docList.includes(document) && !docList.includes(document.aliasOf as Doc) && Doc.AddDocToList(instance.props.Document[DataSym], "data-all", document); + DocListCast(instance.props.Document[DataSym]["aliases"]).forEach(alias => !alias.aliasOf && alias !== instance.props.Document && Doc.AddDocToList(alias, "data-all", Doc.MakeAlias(document))); + } const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName); if (!pullSide && stack) { -- cgit v1.2.3-70-g09d2 From 64dbd28badac3e0689ea38d92e4f6c660967ccce Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Sun, 30 May 2021 17:27:31 -0400 Subject: cleanup --- src/client/views/collections/TreeView.tsx | 1 - src/fields/util.ts | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index ba4af2a6d..fc4361935 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -512,7 +512,6 @@ export class TreeView extends React.Component { onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); onChildDoubleClick = () => { - console.log(this.props.document.onChildDoubleClick); return (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick) }; diff --git a/src/fields/util.ts b/src/fields/util.ts index 882c7fee8..f0cc20d74 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -267,23 +267,21 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc links.forEach(link => distributeAcls(key, acl, link, inheritingFromCollection, visited)); // maps over the children of the document - DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + (isDashboard ? "-all" : "")]).map(d => { // this is now on the layoutdoc instead - figure out the "data-all" approach for the datadoc - // if (GetEffectiveAcl(d) === AclAdmin && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) { + DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + (isDashboard ? "-all" : "")]).map(d => { distributeAcls(key, acl, d, inheritingFromCollection, visited); // } const data = d[DataSym]; - if (data) {// && GetEffectiveAcl(data) === AclAdmin && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) { + if (data) { distributeAcls(key, acl, data, inheritingFromCollection, visited); } }); // maps over the annotations of the document DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + "-annotations"]).map(d => { - // if (GetEffectiveAcl(d) === AclAdmin && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) { distributeAcls(key, acl, d, inheritingFromCollection, visited); // } const data = d[DataSym]; - if (data) {// && GetEffectiveAcl(data) === AclAdmin && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) { + if (data) { distributeAcls(key, acl, data, inheritingFromCollection, visited); } }); -- cgit v1.2.3-70-g09d2 From b85e7819ae46558a659cac73817be0b812d6abbe Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Mon, 31 May 2021 20:18:19 -0400 Subject: sharing dashboard works? pending fixing remnant on screen tabs --- src/client/documents/Documents.ts | 17 +++--------- src/client/util/CurrentUserUtils.ts | 30 +++++++++++++++++++--- .../views/collections/CollectionDockingView.tsx | 9 ++++++- 3 files changed, 38 insertions(+), 18 deletions(-) (limited to 'src/client/views') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 6fb6f70b3..70541b559 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -648,9 +648,8 @@ export namespace Docs { * constructor just generates a new GUID. This is currently used * only when creating a DockDocument from the current user's already existing * main document. - * @param layoutData whether the fieldKey field on the layout doc should store the data or the data doc */ - function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data", protoId?: string, layoutData?: boolean) { + function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data", protoId?: string) { const viewKeys = ["x", "y", "system"]; // keys that should be addded to the view document even though they don't begin with an "_" const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, "^_"); @@ -662,16 +661,8 @@ export namespace Docs { dataProps["acl-Override"] = "None"; dataProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; - if (layoutData) { - viewProps[fieldKey] = data; - const list = new List(); - console.log(DocListCast(data)); - DocListCast(data).forEach(doc => { - list.push(...DocListCast(doc.data)); - }); - dataProps[fieldKey + "-all"] = list; - } - else dataProps[fieldKey] = data; + dataProps[fieldKey] = data; + // so that the list of annotations is already initialised, prevents issues in addonly. // without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do. dataProps[fieldKey + "-annotations"] = new List(); @@ -902,7 +893,7 @@ export namespace Docs { export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { const tabs = TreeDocument(documents, { title: "On-Screen Tabs", childDontRegisterViews: true, freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", _fitWidth: true, system: true }); const all = TreeDocument([], { title: "Off-Screen Tabs", childDontRegisterViews: true, freezeChildren: "add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", system: true }); - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List([tabs, all]), { freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id, undefined, undefined, true); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List([tabs, all]), { freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); } export function DirectoryImportDocument(options: DocumentOptions = {}) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index e143887fa..90a2378fe 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1187,6 +1187,22 @@ export class CurrentUserUtils { const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row"); // add isFolder:true here? freeformDoc.context = dashboardDoc; + + // switching the tabs from the datadoc to the regular doc + const dashboardTabs = dashboardDoc[DataSym].data; + dashboardDoc[DataSym].data = new List(); + dashboardDoc.data = dashboardTabs; + + // collating all docs on the dashboard to make a data-all field + const allDocs = new List(); + const allDocs2 = new List(); // Array.from, spread, splice all cause so stack or acl issues for some reason + DocListCast(dashboardTabs).forEach(doc => { + const tabDocs = DocListCast(doc.data); + allDocs.push(...tabDocs); + allDocs2.push(...tabDocs); + }); + dashboardDoc[DataSym]["data-all"] = allDocs; + dashboardDoc["data-all"] = allDocs2; DocListCast(dashboardDoc.data).forEach(doc => doc.dashboard = dashboardDoc); DocListCast(dashboardDoc.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(self.dashboard)`) as any; @@ -1257,14 +1273,18 @@ Scripting.addGlobal(function shareDashboard(dashboard: Doc) { "opens sharing dialog for Dashboard"); Scripting.addGlobal(async function addToDashboards(dashboard: Doc) { const dashboardAlias = Doc.MakeAlias(dashboard); - const dockingConfig = JSON.parse(StrCast(dashboardAlias.dockingConfig)); - dockingConfig.content = []; - dashboardAlias.dockingConfig = JSON.stringify(dockingConfig); const allDocs = await DocListCastAsync(dashboard[DataSym]["data-all"]); + // moves the data-all field from the datadoc to the layoutdoc, necessary for off screen docs tab to function properly + dashboard["data-all"] = new List(allDocs); dashboardAlias["data-all"] = new List((allDocs || []).map(doc => Doc.MakeAlias(doc))); + const dockingConfig = JSON.parse(StrCast(dashboardAlias.dockingConfig)); + dockingConfig.content = []; + dashboardAlias.dockingConfig = JSON.stringify(dockingConfig); + + dashboardAlias.data = new List(DocListCast(dashboard.data).map(tabFolder => Doc.MakeAlias(tabFolder))); DocListCast(dashboardAlias.data).forEach(doc => doc.dashboard = dashboardAlias); DocListCast(dashboardAlias.data)[0].data = new List(); @@ -1274,10 +1294,12 @@ Scripting.addGlobal(async function addToDashboards(dashboard: Doc) { }, "adds Dashboard to set of Dashboards"); +/** + * Dynamically computes which docs should be rendered in the off-screen tabs tree of a dashboard. + */ Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) { if (dashboard[DataSym] instanceof Doc) { const allDocs = DocListCast(dashboard["data-all"]); - console.log(allDocs); const onScreenTab = DocListCast(dashboard.data)[0]; const onScreenDocs = DocListCast(onScreenTab.data); return new List(allDocs.reduce((result: Doc[], doc) => { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 235fe8950..2e44b65e6 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -163,8 +163,15 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { if (!instance) return false; else { const docList = DocListCast(instance.props.Document[DataSym]["data-all"]); + // adds the doc of the newly created tab to the data-all field if it doesn't already include that doc or one of its aliases !docList.includes(document) && !docList.includes(document.aliasOf as Doc) && Doc.AddDocToList(instance.props.Document[DataSym], "data-all", document); - DocListCast(instance.props.Document[DataSym]["aliases"]).forEach(alias => !alias.aliasOf && alias !== instance.props.Document && Doc.AddDocToList(alias, "data-all", Doc.MakeAlias(document))); + // adds an alias of the doc to the data-all field of the layoutdocs of the aliases + DocListCast(instance.props.Document[DataSym].aliases).forEach(alias => { + const aliasDocList = DocListCast(alias["data-all"]); + // if aliasDocList contains the alias, don't do anything + // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias + !DocListCast(document.aliases).some(a => aliasDocList.includes(a)) && Doc.AddDocToList(alias, "data-all", alias !== instance.props.Document ? Doc.MakeAlias(document) : document); + }); } const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName); -- cgit v1.2.3-70-g09d2 From 0a0a467df5f87adb1f9872c82cc58bbc6c1046d7 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Sat, 5 Jun 2021 17:44:54 -0400 Subject: cleanup --- src/client/views/collections/TreeView.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index fc4361935..a20d41a8d 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -511,9 +511,7 @@ export class TreeView extends React.Component { } onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); - onChildDoubleClick = () => { - return (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick) - }; + onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document); ignoreEvent = (e: any) => { -- cgit v1.2.3-70-g09d2 From 0d07ddae31c5dae8364160801509e236c7e6e894 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 10 Jun 2021 18:30:29 -0400 Subject: fixed link anchor to not render as a blue box when link trails are turned on. --- src/client/views/StyleProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 9e61351c4..47a4a192c 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -127,7 +127,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt Date: Thu, 10 Jun 2021 18:40:28 -0400 Subject: fixed positioning of blue anchor dot when showing link anchor lines --- src/client/views/nodes/DocumentView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b861669f8..5646a9790 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -13,7 +13,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Ty import { AudioField } from "../../../fields/URLField"; import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; import { MobileInterface } from '../../../mobile/MobileInterface'; -import { emptyFunction, hasDescendantTarget, OmitKeys, returnVal, Utils } from "../../../Utils"; +import { emptyFunction, hasDescendantTarget, OmitKeys, returnVal, Utils, returnTrue } from "../../../Utils"; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentType } from '../../documents/DocumentTypes'; @@ -846,6 +846,7 @@ export class DocumentViewInternal extends DocComponent Date: Thu, 17 Jun 2021 18:26:49 -0400 Subject: fixed drawing on images/videos/photos --- src/client/views/GestureOverlay.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 4 +++- src/client/views/nodes/VideoBox.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 2 +- src/client/views/nodes/formattedText/RichTextMenu.tsx | 2 +- src/pen-gestures/GestureUtils.ts | 4 +--- 6 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 491bf18b2..6a4f55bef 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -634,7 +634,7 @@ export class GestureOverlay extends Touchable { } else { this._points = []; } - CollectionFreeFormViewChrome.Instance.primCreated(); + CollectionFreeFormViewChrome.Instance?.primCreated(); } makePolygon = (shape: string, gesture: boolean) => { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index e2e08a0e6..13dfad0fe 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -26,6 +26,8 @@ import { FaceRectangles } from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; import React = require("react"); +import { InkTool } from '../../../fields/InkField'; +import { CurrentUserUtils } from '../../util/CurrentUserUtils'; const path = require('path'); export const pageSchema = createSchema({ @@ -321,7 +323,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true)) this._marqueeing = [e.clientX, e.clientY]; + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) this._marqueeing = [e.clientX, e.clientY]; } @action finishMarquee = () => { diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 263fd5a19..fc08a2302 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -543,7 +543,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true)) this._marqueeing = [e.clientX, e.clientY]; + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) this._marqueeing = [e.clientX, e.clientY]; }); finishMarquee = action(() => { diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index cb7e58559..bd9e03856 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -395,7 +395,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.isContentActive(true)) { + if (!e.altKey && e.button === 0 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { this._marqueeing = [e.clientX, e.clientY]; this.props.select(false); } diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 071491463..59b2d3753 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -352,7 +352,7 @@ export class RichTextMenu extends AntimodeMenu { function onClick(e: React.PointerEvent) { e.preventDefault(); e.stopPropagation(); - self.TextView.endUndoTypingBatch(); + self.TextView?.endUndoTypingBatch(); UndoManager.RunInBatch(() => { self.view && command && command(self.view.state, self.view.dispatch, self.view); self.view && onclick && onclick(self.view.state, self.view.dispatch, self.view); diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts index e7cc89697..65f2bf80c 100644 --- a/src/pen-gestures/GestureUtils.ts +++ b/src/pen-gestures/GestureUtils.ts @@ -20,9 +20,7 @@ export namespace GestureUtils { ): GestureEventDisposer { const handler = (e: Event) => func(e, (e as CustomEvent).detail); element.addEventListener("dashOnGesture", handler); - return () => { - element.removeEventListener("dashOnGesture", handler); - }; + return () => element.removeEventListener("dashOnGesture", handler); } export enum Gestures { -- cgit v1.2.3-70-g09d2 From 383e98b01fbee9383f1bc738a87d4ebeab1dea0b Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 17 Jun 2021 19:42:28 -0400 Subject: from last --- src/client/views/pdf/PDFViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 85cf5abd7..3c6cad42f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -372,7 +372,7 @@ export class PDFViewer extends React.Component { if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) { this._setPreviewCursor?.(e.clientX, e.clientY, true); } - if (!e.altKey && e.button === 0 && this.props.isContentActive(true)) { + if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { this.props.select(false); this._marqueeing = [e.clientX, e.clientY]; if (e.target && ((e.target as any).className.includes("endOfContent") || ((e.target as any).parentElement.className !== "textLayer"))) { -- cgit v1.2.3-70-g09d2 From 0c6269f2eda7a090ebd7d10298d55b7bc832297b Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Sat, 19 Jun 2021 19:41:12 -0400 Subject: trying to solve sidebar issues --- src/client/views/DocComponent.tsx | 1 - src/client/views/MainView.tsx | 6 +++--- src/fields/util.ts | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index f1042de0f..2baf9fbda 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -205,7 +205,6 @@ export function ViewBoxAnnotatableComponent

{ - if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); doc.context = this.props.Document; if (annotationKey ?? this._annotationKey) Doc.GetProto(doc).annotationOn = this.props.Document; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 4eeb1fc95..9d7999672 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -103,7 +103,7 @@ export class MainView extends React.Component { } new InkStrokeProperties(); this._sidebarContent.proto = undefined; - DocServer.setPlaygroundFields(["x", "y", "dataTransition", "_autoHeight", "_showSidebar", "_sidebarWidthPercent", "_width", "_height", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeHidden"]); // can play with these fields on someone else's + DocServer.setPlaygroundFields(["x", "y", "dataTransition", "_autoHeight", "_showSidebar", "showSidebar", "_sidebarWidthPercent", "_width", "_height", "width", "height", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeHidden", "nativeWidth", "_nativeWidth"]); // can play with these fields on someone else's DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg))); @@ -180,8 +180,8 @@ export class MainView extends React.Component { const targClass = targets[0].className.toString(); if (SearchBox.Instance._searchbarOpen || SearchBox.Instance.open) { const check = targets.some((thing) => - (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" || - thing.className === "collectionSchema-header-menuOptions")); + (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" || + thing.className === "collectionSchema-header-menuOptions")); !check && SearchBox.Instance.resetSearch(true); } !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu(); diff --git a/src/fields/util.ts b/src/fields/util.ts index f0cc20d74..526e5af72 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -249,8 +249,8 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc layoutDocChanged = true; if (isDashboard) { - DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(d => { - distributeAcls(key, acl, d, inheritingFromCollection, visited); + DocListCastAsync(target[Doc.LayoutFieldKey(target)]).then(docs => { + docs?.forEach(d => distributeAcls(key, acl, d, inheritingFromCollection, visited)); }); } } -- cgit v1.2.3-70-g09d2 From db2864dfbe85f411e0a098447a7684e104a093ed Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 21 Jun 2021 13:48:23 -0400 Subject: null tests --- src/client/views/nodes/ScreenshotBox.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 252c029e4..700f8a7d3 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -252,8 +252,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent Date: Tue, 22 Jun 2021 13:39:34 -0400 Subject: added "live" flag to url parsing to override playground fields. --- src/client/views/Main.tsx | 1 + src/client/views/MainView.tsx | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 60327f1bf..7553c8118 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -12,6 +12,7 @@ import { LinkManager } from "../util/LinkManager"; AssignAllExtensions(); (async () => { + MainView.Live = window.location.search.includes("live"); window.location.search.includes("safe") && CollectionView.SetSafeMode(true); const info = await CurrentUserUtils.loadCurrentUser(); if (info.id !== "__guest__") { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 9d7999672..a064f7fe8 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -68,6 +68,7 @@ const _global = (window /* browser */ || global /* node */) as any; @observer export class MainView extends React.Component { public static Instance: MainView; + public static Live: boolean = false; private _docBtnRef = React.createRef(); @observable public LastButton: Opt; @observable private _windowWidth: number = 0; @@ -103,8 +104,9 @@ export class MainView extends React.Component { } new InkStrokeProperties(); this._sidebarContent.proto = undefined; - DocServer.setPlaygroundFields(["x", "y", "dataTransition", "_autoHeight", "_showSidebar", "showSidebar", "_sidebarWidthPercent", "_width", "_height", "width", "height", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeHidden", "nativeWidth", "_nativeWidth"]); // can play with these fields on someone else's - + if (!MainView.Live) { + DocServer.setPlaygroundFields(["x", "y", "dataTransition", "_autoHeight", "_showSidebar", "showSidebar", "_sidebarWidthPercent", "_width", "_height", "width", "height", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeHidden", "nativeWidth", "_nativeWidth"]); // can play with these fields on someone else's + } DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg))); const tag = document.createElement('script'); @@ -180,8 +182,8 @@ export class MainView extends React.Component { const targClass = targets[0].className.toString(); if (SearchBox.Instance._searchbarOpen || SearchBox.Instance.open) { const check = targets.some((thing) => - (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" || - thing.className === "collectionSchema-header-menuOptions")); + (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" || + thing.className === "collectionSchema-header-menuOptions")); !check && SearchBox.Instance.resetSearch(true); } !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu(); -- cgit v1.2.3-70-g09d2 From 886439bdb812b54a756587be2a2f12e8ced67159 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Thu, 24 Jun 2021 18:22:06 -0400 Subject: pre-initialised annos list to prevent addonly privilege issues + some cleanup --- src/client/views/SidebarAnnos.tsx | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 59ff1c340..dcdc4f776 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -31,18 +31,22 @@ interface ExtraProps { } @observer export class SidebarAnnos extends React.Component { + constructor(props: Readonly) { + super(props); + this.props.dataDoc[this.sidebarKey] = new List(); + } _stackRef = React.createRef(); @computed get allHashtags() { const keys = new Set(); - DocListCast(this.props.rootDoc[this.sidebarKey()]).forEach(doc => SearchBox.documentKeys(doc).forEach(key => keys.add(key))); + DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => SearchBox.documentKeys(doc).forEach(key => keys.add(key))); return Array.from(keys.keys()).filter(key => key[0]).filter(key => !key.startsWith("_") && (key[0] === "#" || key[0] === key[0].toUpperCase())).sort(); } @computed get allUsers() { const keys = new Set(); - DocListCast(this.props.rootDoc[this.sidebarKey()]).forEach(doc => keys.add(StrCast(doc.author))); + DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author))); return Array.from(keys.keys()).sort(); } - get filtersKey() { return "_" + this.sidebarKey() + "-docFilters"; } + get filtersKey() { return "_" + this.sidebarKey + "-docFilters"; } anchorMenuClick = (anchor: Doc) => { const startup = StrListCast(this.props.rootDoc.docFilters).map(filter => filter.split(":")[0]).join(" "); @@ -59,7 +63,7 @@ export class SidebarAnnos extends React.Component { this._stackRef.current?.focusDocument(target); } makeDocUnfiltered = (doc: Doc) => { - if (DocListCast(this.props.rootDoc[this.sidebarKey()]).includes(doc)) { + if (DocListCast(this.props.rootDoc[this.sidebarKey]).includes(doc)) { if (this.props.layoutDoc[this.filtersKey]) { this.props.layoutDoc[this.filtersKey] = new List(); return true; @@ -67,14 +71,15 @@ export class SidebarAnnos extends React.Component { } return false; } - sidebarKey = () => this.props.fieldKey + "-sidebar"; + + get sidebarKey() { return this.props.fieldKey + "-sidebar"; } filtersHeight = () => 38; screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(Doc.NativeWidth(this.props.dataDoc), 0).scale(this.props.scaling?.() || 1); panelWidth = () => !this.props.layoutDoc._showSidebar ? 0 : this.props.layoutDoc.type === DocumentType.RTF ? this.props.PanelWidth() : (NumCast(this.props.layoutDoc.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.layoutDoc.nativeWidth); panelHeight = () => this.props.PanelHeight() - this.filtersHeight(); - addDocument = (doc: Doc | Doc[]) => this.props.sidebarAddDocument(doc, this.sidebarKey()); - moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey()); - removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey()); + addDocument = (doc: Doc | Doc[]) => this.props.sidebarAddDocument(doc, this.sidebarKey); + moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey); + removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey); docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])]; sidebarStyleProvider = (doc: Opt, props: Opt, property: string) => { @@ -85,14 +90,14 @@ export class SidebarAnnos extends React.Component { const renderTag = (tag: string) => { const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:check`); return

Doc.setDocFilter(this.props.rootDoc, tag, tag, "check", true, this.sidebarKey(), e.shiftKey)}> + onClick={e => Doc.setDocFilter(this.props.rootDoc, tag, tag, "check", true, this.sidebarKey, e.shiftKey)}> {tag}
; }; const renderUsers = (user: string) => { const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`author:${user}:check`); return
Doc.setDocFilter(this.props.rootDoc, "author", user, "check", true, this.sidebarKey(), e.shiftKey)}> + onClick={e => Doc.setDocFilter(this.props.rootDoc, "author", user, "check", true, this.sidebarKey, e.shiftKey)}> {user}
; }; @@ -116,7 +121,7 @@ export class SidebarAnnos extends React.Component { PanelWidth={this.panelWidth} styleProvider={this.sidebarStyleProvider} docFilters={this.docFilters} - scaleField={this.sidebarKey() + "-scale"} + scaleField={this.sidebarKey + "-scale"} isAnnotationOverlay={false} select={emptyFunction} scaling={returnOne} @@ -129,7 +134,7 @@ export class SidebarAnnos extends React.Component { ScreenToLocalTransform={this.screenToLocalTransform} renderDepth={this.props.renderDepth + 1} viewType={CollectionViewType.Stacking} - fieldKey={this.sidebarKey()} + fieldKey={this.sidebarKey} pointerEvents={"all"} />
-- cgit v1.2.3-70-g09d2 From 2ac055ce068c2380a76c03d962b1e8b218c213de Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Thu, 24 Jun 2021 18:22:25 -0400 Subject: more cleanup --- src/client/util/GroupMemberView.tsx | 1 - src/client/views/DocComponent.tsx | 7 ------- 2 files changed, 8 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index 927200ed3..b7f89794d 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -50,7 +50,6 @@ export class GroupMemberView extends React.Component { onChange={selectedOption => GroupManager.Instance.addMemberToGroup(this.props.group, (selectedOption as UserOptions).value)} placeholder={"Add members"} value={null} - closeMenuOnSelect={true} styles={{ dropdownIndicator: (base, state) => ({ ...base, diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 2baf9fbda..fc2a968fe 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -107,13 +107,6 @@ export function ViewBoxAnnotatableComponent

([ - [AclPrivate, SharingPermissions.None], - [AclReadonly, SharingPermissions.View], - [AclAddonly, SharingPermissions.Add], - [AclEdit, SharingPermissions.Edit], - [AclAdmin, SharingPermissions.Admin] - ]); lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result; -- cgit v1.2.3-70-g09d2 From 16c2a900e9116474caa67e4d5de15e2c51477292 Mon Sep 17 00:00:00 2001 From: vkalev Date: Tue, 27 Jul 2021 18:54:53 -0400 Subject: changing format of controls/handles + adding color enums --- src/client/views/InkControls.tsx | 73 ++++++++++++++++++--------------- src/client/views/InkHandles.tsx | 13 +++--- src/client/views/InkStrokeProperties.ts | 38 +++++++++-------- src/client/views/InkingStroke.tsx | 40 +++++++----------- 4 files changed, 84 insertions(+), 80 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/InkControls.tsx b/src/client/views/InkControls.tsx index 23f22c774..090af10cc 100644 --- a/src/client/views/InkControls.tsx +++ b/src/client/views/InkControls.tsx @@ -6,8 +6,13 @@ import { setupMoveUpEvents, emptyFunction } from "../../Utils"; import { UndoManager } from "../util/UndoManager"; import { ControlPoint, InkData, PointData } from "../../fields/InkField"; import { Transform } from "../util/Transform"; +import { Colors } from "./global/globalEnums"; +import { Doc } from "../../fields/Doc"; +import { listSpec } from "../../fields/Schema"; +import { Cast } from "../../fields/Types"; export interface InkControlProps { + inkDoc: Doc; data: InkData; addedPoints: PointData[]; format: number[]; @@ -30,19 +35,22 @@ export class InkControls extends React.Component { const controlUndo = UndoManager.StartBatch("DocDecs set radius"); const screenScale = this.props.ScreenToLocalTransform().Scale; const order = controlIndex % 4; - const handleIndexA = order === 2 ? controlIndex - 1 : controlIndex - 2; + const handleIndexA = order === 2 ? controlIndex - 1 : controlIndex - 2; const handleIndexB = order === 2 ? controlIndex + 2 : controlIndex + 1; + const brokenIndices = Cast(this.props.inkDoc, listSpec("number")); setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { InkStrokeProperties.Instance?.moveControl(-delta[0] * screenScale, -delta[1] * screenScale, controlIndex); return false; }, - () => controlUndo?.end(), - emptyFunction); - // action((e: PointerEvent, doubleTap: boolean | undefined) => - // { if (doubleTap && InkStrokeProperties.Instance?._brokenIndices.includes(controlIndex)) { - // InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB); - // }})); + () => controlUndo?.end(), + brokenIndices ? + action((e: PointerEvent, doubleTap: boolean | undefined) => { + if (doubleTap && brokenIndices.includes(controlIndex)) { + InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB); + } + }) + : emptyFunction); } } @@ -75,7 +83,7 @@ export class InkControls extends React.Component { @action onLeaveControl = () => { this._overControl = -1; }; @action onEnterAddPoint = (i: number) => { this._overAddPoint = i; }; @action onLeaveAddPoint = () => { this._overAddPoint = -1; }; - + render() { const formatInstance = InkStrokeProperties.Instance; if (!formatInstance) return (null); @@ -90,41 +98,42 @@ export class InkControls extends React.Component { } } const addedPoints = this.props.addedPoints; - const [left, top, scaleX, scaleY, strokeWidth, dotsize] = this.props.format; + const [left, top, scaleX, scaleY, strokeWidth] = this.props.format; return ( <> {addedPoints.map((pts, i) => - { formatInstance?.addPoints(pts.X, pts.Y, addedPoints, i, controlPoints); }} - onMouseEnter={() => this.onEnterAddPoint(i)} - onMouseLeave={this.onLeaveAddPoint} - pointerEvents="all" + { formatInstance?.addPoints(pts.X, pts.Y, addedPoints, i, controlPoints); }} + onMouseEnter={() => this.onEnterAddPoint(i)} + onMouseLeave={this.onLeaveAddPoint} + pointerEvents="all" cursor="all-scroll" /> )} {controlPoints.map((control, i) => - { - this.changeCurrPoint(control.I); - this.onControlDown(e, control.I); }} - onMouseEnter={() => this.onEnterControl(i)} - onMouseLeave={this.onLeaveControl} - pointerEvents="all" + { + this.changeCurrPoint(control.I); + this.onControlDown(e, control.I); + }} + onMouseEnter={() => this.onEnterControl(i)} + onMouseLeave={this.onLeaveControl} + pointerEvents="all" cursor="default" /> diff --git a/src/client/views/InkHandles.tsx b/src/client/views/InkHandles.tsx index 28b6dd820..6e890e184 100644 --- a/src/client/views/InkHandles.tsx +++ b/src/client/views/InkHandles.tsx @@ -10,6 +10,7 @@ import { Doc } from "../../fields/Doc"; import { listSpec } from "../../fields/Schema"; import { List } from "../../fields/List"; import { Cast } from "../../fields/Types"; +import { Colors } from "./global/globalEnums"; export interface InkHandlesProps { inkDoc: Doc; @@ -80,7 +81,7 @@ export class InkHandles extends React.Component { handleLines.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 1].X, Y2: data[i + 1].Y, X3: data[i + 3].X, Y3: data[i + 3].Y, dot1: i + 1, dot2: i + 2 }); } } - const [left, top, scaleX, scaleY, strokeWidth, dotsize] = this.props.format; + const [left, top, scaleX, scaleY, strokeWidth] = this.props.format; return ( <> @@ -91,7 +92,7 @@ export class InkHandles extends React.Component { cy={(pts.Y - top - strokeWidth / 2) * scaleY + strokeWidth / 2} r={strokeWidth / 2} strokeWidth={0} - fill="#1F85DE" + fill={Colors.MEDIUM_BLUE} onPointerDown={(e) => this.onHandleDown(e, pts.I)} pointerEvents="all" cursor="default" @@ -104,16 +105,16 @@ export class InkHandles extends React.Component { y1={(pts.Y1 - top - strokeWidth / 2) * scaleY + strokeWidth / 2} x2={(pts.X2 - left - strokeWidth / 2) * scaleX + strokeWidth / 2} y2={(pts.Y2 - top - strokeWidth / 2) * scaleY + strokeWidth / 2} - stroke="#1F85DE" - strokeWidth={dotsize / 8} + stroke={Colors.MEDIUM_BLUE} + strokeWidth={strokeWidth / 4} display={(pts.dot1 === formatInstance._currentPoint || pts.dot2 === formatInstance._currentPoint) ? "inherit" : "none"} /> )} diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 1a3585f3e..de8cf80bd 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -35,7 +35,7 @@ export class InkStrokeProperties { * @param func The inputted function. * @param requireCurrPoint Indicates whether the current selected point is needed. */ - applyFunction = (func: (doc: Doc, ink: InkData, ptsXscale: number, ptsYscale: number) => { X: number, Y: number }[] | undefined, requireCurrPoint: boolean = false) => { + applyFunction = (func: (doc: Doc, ink: InkData, ptsXscale: number, ptsYscale: number) => { X: number, Y: number }[] | undefined, requireCurrPoint: boolean = false) => { var appliedFunc = false; this.selectedInk?.forEach(action(inkView => { if (this.selectedInk?.length === 1 && (!requireCurrPoint || this._currentPoint !== -1)) { @@ -91,16 +91,16 @@ export class InkStrokeProperties { }); } } - if (end === 0) end = points.length-1; + if (end === 0) end = points.length - 1; // Index of new control point with regards to the ink data. const newIndex = Math.floor(counter / 2) * 4 + 2; // Creating new ink data with the new control point and handle points inputted. for (let i = 0; i < ink.length; i++) { - if (i === newIndex) { - const [handleA, handleB] = this.getNewHandlePoints(points.slice(start, index+1), points.slice(index, end), newControl); + if (i === newIndex) { + const [handleA, handleB] = this.getNewHandlePoints(points.slice(start, index + 1), points.slice(index, end), newControl); newPoints.push(handleA, newControl, newControl, handleB); // Adjusting the magnitude of the left handle line of the right neighboring control point. - const [rightControl, rightHandle] = [points[end], ink[i]]; + const [rightControl, rightHandle] = [points[end], ink[i]]; const scaledVector = this.getScaledHandlePoint(false, start, end, index, rightControl, rightHandle); rightHandle && newPoints.push({ X: rightControl.X - scaledVector.X, Y: rightControl.Y - scaledVector.Y }); } else if (i === newIndex - 1) { @@ -111,14 +111,14 @@ export class InkStrokeProperties { } else { ink[i] && newPoints.push({ X: ink[i].X, Y: ink[i].Y }); } - + } let brokenIndices = Cast(doc.brokenInkIndices, listSpec("number")); // Updating the indices of the control points whose handle tangency has been broken. if (brokenIndices) { - brokenIndices = new List(brokenIndices.map((control) => { + brokenIndices = new List(brokenIndices.map((control) => { if (control >= newIndex) { - return control + 4; + return control + 4; } else { return control; } @@ -158,7 +158,7 @@ export class InkStrokeProperties { getNewHandlePoints = (C: PointData[], D: PointData[], newControl: PointData) => { const [m, n] = [C.length, D.length]; let handleSizeA = Math.sqrt((Math.pow(newControl.X - C[0].X, 2)) + (Math.pow(newControl.Y - C[0].Y, 2))); - let handleSizeB = Math.sqrt((Math.pow(D[n-1].X - newControl.X, 2)) + (Math.pow(D[n-1].Y - newControl.Y, 2))); + let handleSizeB = Math.sqrt((Math.pow(D[n - 1].X - newControl.X, 2)) + (Math.pow(D[n - 1].Y - newControl.Y, 2))); // Scaling adjustments to improve the ratio between the magnitudes of the two handle lines. // (Ensures that the new point added doesn't augment the inital shape of the curve much). if (handleSizeA < 75 && handleSizeB < 75) { @@ -173,7 +173,7 @@ export class InkStrokeProperties { handleSizeB *= 2; } // Finding the last leg of the derivative curve of C. - const dC = { X: (handleSizeA / n) * (C[m-1].X - C[m-2].X), Y: (handleSizeA / n) * (C[m-1].Y - C[m-2].Y) }; + const dC = { X: (handleSizeA / n) * (C[m - 1].X - C[m - 2].X), Y: (handleSizeA / n) * (C[m - 1].Y - C[m - 2].Y) }; // Finding the first leg of the derivative curve of D. const dD = { X: (handleSizeB / m) * (D[1].X - D[0].X), Y: (handleSizeB / m) * (D[1].Y - D[0].Y) }; const handleA = { X: newControl.X - dC.X, Y: newControl.Y - dC.Y }; @@ -259,13 +259,17 @@ export class InkStrokeProperties { snapHandleTangent = (controlIndex: number, handleIndexA: number, handleIndexB: number) => { this.applyFunction((doc: Doc, ink: InkData) => { - // doc.brokenIndices.splice(this._brokenIndices.indexOf(controlIndex), 1); - const [controlPoint, handleA, handleB] = [ink[controlIndex], ink[handleIndexA], ink[handleIndexB]]; - const oppositeHandleA = this.rotatePoint(handleA, controlPoint, Math.PI); - const angleDifference = this.angleChange(handleB, oppositeHandleA, controlPoint); - const newHandleB = this.rotatePoint(handleB, controlPoint, angleDifference); - ink[handleIndexB] = newHandleB; - return ink; + const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number")); + if (brokenIndices) { + brokenIndices.splice(brokenIndices.indexOf(controlIndex), 1); + doc.brokenInkIndices = brokenIndices; + const [controlPoint, handleA, handleB] = [ink[controlIndex], ink[handleIndexA], ink[handleIndexB]]; + const oppositeHandleA = this.rotatePoint(handleA, controlPoint, Math.PI); + const angleDifference = this.angleChange(handleB, oppositeHandleA, controlPoint); + const newHandleB = this.rotatePoint(handleB, controlPoint, angleDifference); + ink[handleIndexB] = newHandleB; + return ink; + } }); } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index bd71aaf19..a8f32118c 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -19,6 +19,7 @@ import { InkStrokeProperties } from "./InkStrokeProperties"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { InkControls } from "./InkControls"; import { InkHandles } from "./InkHandles"; +import { Colors } from "./global/globalEnums"; type InkDocument = makeInterface<[typeof documentSchema]>; const InkDocument = makeInterface(documentSchema); @@ -66,6 +67,9 @@ export class InkingStroke extends ViewBoxBaseComponent 1 && lineRight - lineLeft > 1, - false); + const inkLine = InteractionUtils.CreatePolyline(data, left, top, strokeColor, strokeWidth, strokeWidth, StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"), StrCast(this.layoutDoc.strokeStartMarker), + StrCast(this.layoutDoc.strokeEndMarker), StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5 && lineBottom - lineTop > 1 && lineRight - lineLeft > 1, false); // Thin blue line indicating that the current ink stroke is selected. - const selectedLine = InteractionUtils.CreatePolyline( - data, lineLeft - strokeWidth * 3, lineTop - strokeWidth * 3, "#1F85DE", strokeWidth / 6, - strokeWidth / 6, StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"), - StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker), - StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", - this.props.isSelected() && strokeWidth <= 5 && lineBottom - lineTop > 1 && lineRight - lineLeft > 1, - false); + const selectedLine = InteractionUtils.CreatePolyline(data, left - strokeWidth / 3, top - strokeWidth / 3, Colors.MEDIUM_BLUE, strokeWidth / 6, strokeWidth / 6, StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"), + StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker), StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5 && lineBottom - lineTop > 1 && lineRight - lineLeft > 1, false); // Invisible polygonal line that enables the ink to be selected by the user. - const clickableLine = InteractionUtils.CreatePolyline(data, left, top, - this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, - strokeWidth + 15, StrCast(this.layoutDoc.strokeBezier), - StrCast(this.layoutDoc.fillColor, "none"), "none", "none", undefined, scaleX, scaleY, "", - this.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted", false, true); + const clickableLine = InteractionUtils.CreatePolyline(data, left, top, this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, strokeWidth + 15, StrCast(this.layoutDoc.strokeBezier), + StrCast(this.layoutDoc.fillColor, "none"), "none", "none", undefined, scaleX, scaleY, "", this.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted", false, true); // Set of points rendered upon the ink that can be added if a user clicks on one. - const addedPoints = InteractionUtils.CreatePoints(data, left, top, strokeColor, strokeWidth, strokeWidth, - StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"), - StrCast(this.layoutDoc.strokeStartMarker), StrCast(this.layoutDoc.strokeEndMarker), - StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", - this.props.isSelected() && strokeWidth <= 5, false); + const addedPoints = InteractionUtils.CreatePoints(data, left, top, strokeColor, strokeWidth, strokeWidth, StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "none"), StrCast(this.layoutDoc.strokeStartMarker), + StrCast(this.layoutDoc.strokeEndMarker), StrCast(this.layoutDoc.strokeDash), scaleX, scaleY, "", "none", this.props.isSelected() && strokeWidth <= 5, false); return ( : ""} -- cgit v1.2.3-70-g09d2 From ea6000690022d43b6bc8e1a546d28729a59faf7b Mon Sep 17 00:00:00 2001 From: vkalev Date: Wed, 28 Jul 2021 13:14:10 -0400 Subject: snapping broken tangency added --- src/client/views/InkControls.tsx | 14 ++++++-------- src/client/views/InkHandles.tsx | 1 + src/client/views/InkStrokeProperties.ts | 18 +++++++++++------- 3 files changed, 18 insertions(+), 15 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/InkControls.tsx b/src/client/views/InkControls.tsx index 090af10cc..6213a4075 100644 --- a/src/client/views/InkControls.tsx +++ b/src/client/views/InkControls.tsx @@ -37,20 +37,18 @@ export class InkControls extends React.Component { const order = controlIndex % 4; const handleIndexA = order === 2 ? controlIndex - 1 : controlIndex - 2; const handleIndexB = order === 2 ? controlIndex + 2 : controlIndex + 1; - const brokenIndices = Cast(this.props.inkDoc, listSpec("number")); + const brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number")); setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { InkStrokeProperties.Instance?.moveControl(-delta[0] * screenScale, -delta[1] * screenScale, controlIndex); return false; }, () => controlUndo?.end(), - brokenIndices ? - action((e: PointerEvent, doubleTap: boolean | undefined) => { - if (doubleTap && brokenIndices.includes(controlIndex)) { - InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB); - } - }) - : emptyFunction); + action((e: PointerEvent, doubleTap: boolean | undefined) => { + if (doubleTap && brokenIndices && brokenIndices.includes(controlIndex)) { + InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB); + } + })); } } diff --git a/src/client/views/InkHandles.tsx b/src/client/views/InkHandles.tsx index 6e890e184..f1eb4b9db 100644 --- a/src/client/views/InkHandles.tsx +++ b/src/client/views/InkHandles.tsx @@ -51,6 +51,7 @@ export class InkHandles extends React.Component { onBreakTangent = (e: KeyboardEvent, controlIndex: number) => { const doc: Doc = this.props.inkDoc; if (["Alt"].includes(e.key)) { + e.stopPropagation(); if (doc) { const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number")) || new List; if (brokenIndices && !brokenIndices.includes(controlIndex)) { diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index de8cf80bd..76ca5b5ec 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -257,6 +257,11 @@ export class InkStrokeProperties { return newPoints; }) + /** + * Snaps a control point with broken tangency back to synced rotation. + * @param handleIndexA The handle point that retains its current position. + * @param handleIndexB The handle point that is rotated to be 180 degrees from its opposite. + */ snapHandleTangent = (controlIndex: number, handleIndexA: number, handleIndexB: number) => { this.applyFunction((doc: Doc, ink: InkData) => { const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number")); @@ -278,13 +283,12 @@ export class InkStrokeProperties { */ @action rotatePoint = (target: PointData, origin: PointData, angle: number) => { - target.X -= origin.X; - target.Y -= origin.Y; - const newX = Math.cos(angle) * target.X - Math.sin(angle) * target.Y; - const newY = Math.sin(angle) * target.X + Math.cos(angle) * target.Y; - target.X = newX + origin.X; - target.Y = newY + origin.Y; - return target; + let rotatedTarget = { X: target.X - origin.X, Y: target.Y - origin.Y }; + const newX = Math.cos(angle) * rotatedTarget.X - Math.sin(angle) * rotatedTarget.Y; + const newY = Math.sin(angle) * rotatedTarget.X + Math.cos(angle) * rotatedTarget.Y; + rotatedTarget.X = newX + origin.X; + rotatedTarget.Y = newY + origin.Y; + return rotatedTarget; } /** -- cgit v1.2.3-70-g09d2 From 405114b0a2440939e89052539eb91f479e8d4176 Mon Sep 17 00:00:00 2001 From: Aubrey-Li <63608597+Aubrey-Li@users.noreply.github.com> Date: Wed, 28 Jul 2021 16:07:08 -0700 Subject: fix tree view collection drop shadow --- src/client/views/nodes/DocumentView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 60fa462ad..2b0d8fe91 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -979,7 +979,7 @@ export class DocumentViewInternal extends DocComponent Date: Thu, 29 Jul 2021 09:51:50 -0400 Subject: deselecting an ink stroke properly exits edit view --- src/client/views/InkingStroke.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index a8f32118c..21059b330 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -65,11 +65,19 @@ export class InkingStroke extends ViewBoxBaseComponent { + if (!this.props.isSelected() && this._properties) { + this._properties._controlButton = false; + } + } + render() { TraceMobx(); - // if (!this.props.isSelected() && this._properties) { - // this._properties._controlButton = false; - // } + this.toggleControlButton(); // Extracting the ink data and formatting information of the current ink stroke. const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? []; const inkDoc: Doc = this.layoutDoc; -- cgit v1.2.3-70-g09d2 From f59495f0aa5ba65b358f9430f55c653ed0fc3d70 Mon Sep 17 00:00:00 2001 From: Aubrey-Li <63608597+Aubrey-Li@users.noreply.github.com> Date: Sat, 31 Jul 2021 22:26:48 -0700 Subject: small UI changes --- src/client/views/global/globalCssVariables.scss | 22 +++++++++++----------- src/client/views/nodes/DocumentView.tsx | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss index 1b881ba43..7556f8b8a 100644 --- a/src/client/views/global/globalCssVariables.scss +++ b/src/client/views/global/globalCssVariables.scss @@ -1,15 +1,15 @@ -@import url("https://fonts.googleapis.com/css?family=Noto+Sans:400,700|Crimson+Text:400,400i,700"); +@import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap"); // colors $white: #ffffff; -$light-gray:#dfdfdf; -$medium-gray: #9F9F9F; +$light-gray: #dfdfdf; +$medium-gray: #9f9f9f; $dark-gray: #323232; $black: #000000; -$light-blue: #BDDDF5; -$medium-blue: #4476F7; -$pink: #E0217D; -$yellow: #F5D747; +$light-blue: #bdddf5; +$medium-blue: #4476f7; +$pink: #e0217d; +$yellow: #f5d747; $close-red: #e48282; @@ -24,7 +24,7 @@ $large-padding: 32px; $icon-size: 28px; // fonts -$sans-serif: "Noto Sans", sans-serif; +$sans-serif: "Roboto", sans-serif; $large-header: 16px; $body-text: 12px; $small-text: 9px; @@ -41,7 +41,7 @@ $contextMenu-zindex: 100000; // context menu shows up over everything $radialMenu-zindex: 100000; // context menu shows up over everything // borders -$standard-border: solid 1px #9F9F9F; +$standard-border: solid 1px #9f9f9f; $searchpanel-height: 32px; $mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc @@ -49,7 +49,7 @@ $docDecorations-zindex: 998; // then doc decorations appear over everything else $remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right? $COLLECTION_BORDER_WIDTH: 0; $SCHEMA_DIVIDER_WIDTH: 4; -$MINIMIZED_ICON_SIZE:24; +$MINIMIZED_ICON_SIZE: 24; $MAX_ROW_HEIGHT: 44px; $DFLT_IMAGE_NATIVE_DIM: 900px; $MENU_PANEL_WIDTH: 60px; @@ -67,4 +67,4 @@ $TREE_BULLET_WIDTH: 20px; DFLT_IMAGE_NATIVE_DIM: $DFLT_IMAGE_NATIVE_DIM; MENU_PANEL_WIDTH: $MENU_PANEL_WIDTH; TREE_BULLET_WIDTH: $TREE_BULLET_WIDTH; -} \ No newline at end of file +} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index df4cba01b..97ad37a08 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -971,7 +971,7 @@ export class DocumentViewInternal extends DocComponent Date: Sat, 31 Jul 2021 22:35:07 -0700 Subject: unified Properties font to Roboto --- src/client/views/PropertiesView.scss | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss index fa45a065d..934ede732 100644 --- a/src/client/views/PropertiesView.scss +++ b/src/client/views/PropertiesView.scss @@ -1,6 +1,6 @@ .propertiesView { height: 100%; - font-family: "Noto Sans"; + font-family: "Roboto"; cursor: auto; overflow-x: hidden; @@ -28,9 +28,7 @@ color: grey; cursor: pointer; } - } - } .propertiesView-name { @@ -80,7 +78,6 @@ padding-bottom: 10px; padding-top: 8px; } - } .propertiesView-sharing { @@ -140,8 +137,6 @@ } } - - .change-buttons { display: flex; @@ -216,7 +211,6 @@ } } - .propertiesView-appearance { //border-bottom: 1px solid black; //padding: 8.5px; @@ -305,7 +299,7 @@ .notify-button-icon { width: 6px; height: 6.5px; - margin-left: .5px; + margin-left: 0.5px; } &:hover { @@ -331,7 +325,6 @@ } .propertiesView-sharingTable { - // whatever's commented out - add it back in when adding the buttons // border: 1.5px solid black; @@ -347,7 +340,6 @@ width: 92%; .propertiesView-sharingTable-item { - display: flex; // padding: 5px; padding: 3px; @@ -421,7 +413,6 @@ cursor: pointer; } } - } .propertiesView-fields-checkbox { @@ -468,7 +459,6 @@ } .propertiesView-contexts { - .propertiesView-contexts-title { font-weight: bold; font-size: 12.5px; @@ -499,11 +489,9 @@ overflow: hidden; padding: 10px; } - } .propertiesView-layout { - .propertiesView-layout-title { font-weight: bold; font-size: 12.5px; @@ -534,7 +522,6 @@ overflow: hidden; padding: 10px; } - } .propertiesView-presTrails { @@ -576,7 +563,6 @@ } .inking-button { - display: flex; .inking-button-points { @@ -635,7 +621,6 @@ } .inputBox { - margin-top: 10px; display: flex; height: 19px; @@ -658,7 +643,6 @@ } .inputBox-button { - .inputBox-button-up { background-color: #333333; height: 9px; @@ -690,7 +674,6 @@ cursor: pointer; } } - } } @@ -767,7 +750,6 @@ } .widthAndDash { - .width { .width-top { display: flex; @@ -792,13 +774,11 @@ } .arrows { - display: flex; margin-bottom: 3px; margin-left: 4px; .arrows-head { - display: flex; margin-right: 35px; @@ -827,7 +807,6 @@ } .dashed { - display: flex; margin-left: 64px; margin-bottom: 6px; @@ -856,7 +835,6 @@ } } - .properties-flyout { grid-column: 2/4; -} \ No newline at end of file +} -- cgit v1.2.3-70-g09d2 From 149b2de414812063bc8c81305f284f74a24481cb Mon Sep 17 00:00:00 2001 From: geireann Date: Sun, 1 Aug 2021 18:45:21 -0400 Subject: small ui tweaks --- src/client/util/DragManager.ts | 9 +++ src/client/views/EditableView.scss | 8 ++- src/client/views/PropertiesView.scss | 9 ++- .../views/collections/CollectionLinearView.scss | 3 + src/client/views/nodes/LinkDescriptionPopup.scss | 65 ++++++++-------------- 5 files changed, 46 insertions(+), 48 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c4842e88a..5e16de617 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -15,6 +15,15 @@ import { SnappingManager } from "./SnappingManager"; import { UndoManager } from "./UndoManager"; export type dropActionType = "alias" | "copy" | "move" | "same" | "proto" | "none" | undefined; // undefined = move, "same" = move but don't call removeDropProperties + +/** + * Initialize drag + * @param _reference: The HTMLElement that is being dragged + * @param docFunc: The Dash document being moved + * @param moveFunc: The function called when the document is moved + * @param dropAction: What to do with the document when it is dropped + * @param dragStarted: Method to call when the drag is started + */ export function SetupDrag( _reference: React.RefObject, docFunc: () => Doc | Promise | undefined, diff --git a/src/client/views/EditableView.scss b/src/client/views/EditableView.scss index 5dc0c1962..1aebedf2e 100644 --- a/src/client/views/EditableView.scss +++ b/src/client/views/EditableView.scss @@ -26,4 +26,10 @@ width: 100%; background: inherit; pointer-events: all; -} \ No newline at end of file +} + +.editableView-input:focus { + border: none; + outline: none; +} + \ No newline at end of file diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss index 934ede732..321b83f52 100644 --- a/src/client/views/PropertiesView.scss +++ b/src/client/views/PropertiesView.scss @@ -1,3 +1,5 @@ +@import "./global/globalCssVariables.scss"; + .propertiesView { height: 100%; font-family: "Roboto"; @@ -823,15 +825,12 @@ } .editable-title { - border: none; padding: 6px; padding-bottom: 2px; - background: #eeeeee; - border-top: 1px solid; - border-left: 1px solid; + border: solid 1px $dark-gray; &:hover { - border: 0.75px solid rgb(122, 28, 28); + border: 0.75px solid $medium-blue; } } diff --git a/src/client/views/collections/CollectionLinearView.scss b/src/client/views/collections/CollectionLinearView.scss index 913a65774..46e40489b 100644 --- a/src/client/views/collections/CollectionLinearView.scss +++ b/src/client/views/collections/CollectionLinearView.scss @@ -22,6 +22,7 @@ .bottomPopup-background { background: $medium-blue; display: flex; + border-radius: 10px; height: 35; transform: translate3d(6px, 0px, 0px); align-content: center; @@ -40,6 +41,7 @@ } .bottomPopup-descriptions { + cursor:pointer; display: inline; white-space: nowrap; padding-left: 8px; @@ -52,6 +54,7 @@ } .bottomPopup-exit { + cursor:pointer; display: inline; white-space: nowrap; margin-right: 10px; diff --git a/src/client/views/nodes/LinkDescriptionPopup.scss b/src/client/views/nodes/LinkDescriptionPopup.scss index d92823ccc..a8db5d360 100644 --- a/src/client/views/nodes/LinkDescriptionPopup.scss +++ b/src/client/views/nodes/LinkDescriptionPopup.scss @@ -1,9 +1,13 @@ +@import "../global/globalCssVariables.scss"; + .linkDescriptionPopup { display: flex; - - border: 1px solid rgb(170, 26, 26); - + flex-direction: row; + justify-content: center; + align-items: center; + border: 2px solid $medium-blue; + background-color: $white; width: auto; position: absolute; @@ -11,17 +15,11 @@ z-index: 10000; border-radius: 10px; font-size: 12px; - //white-space: nowrap; - - background-color: rgba(250, 250, 250, 0.95); - padding-top: 9px; - padding-bottom: 9px; - padding-left: 9px; - padding-right: 9px; + gap: 5px; + padding: 9px; .linkDescriptionPopup-input { float: left; - background-color: rgba(250, 250, 250, 0.95); color: rgb(100, 100, 100); border: none; min-width: 160px; @@ -30,46 +28,29 @@ .linkDescriptionPopup-btn { float: right; - justify-content: center; vertical-align: middle; - .linkDescriptionPopup-btn-dismiss { - background-color: white; - color: black; + cursor: pointer; display: inline; - right: 0; - border-radius: 10px; - border: 1px solid black; - padding: 3px; - font-size: 9px; - text-align: center; - position: relative; - margin-right: 4px; - justify-content: center; - - &:hover{ - cursor: pointer; - } + white-space: nowrap; + padding: 5px; + vertical-align: middle; + background-color: $close-red; + border-radius: 3px; + color: black; } .linkDescriptionPopup-btn-add { - background-color: black; - color: white; + cursor: pointer; display: inline; - right: 0; - border-radius: 10px; - border: 1px solid black; - padding: 3px; - font-size: 9px; - text-align: center; - position: relative; - justify-content: center; - - &:hover{ - cursor: pointer; - } + white-space: nowrap; + padding: 5px; + vertical-align: middle; + background-color: $light-blue; + border-radius: 3px; + color: black; } } -- cgit v1.2.3-70-g09d2 From 0546ecf205b7d2b76f341a7157beebf95fb888a8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 1 Aug 2021 22:43:46 -0400 Subject: made url server references relative. --- src/Utils.ts | 1 - .../apis/google_docs/GooglePhotosClientUtils.ts | 2 +- src/client/documents/Documents.ts | 90 ++-------------------- src/client/util/HypothesisUtils.ts | 2 +- src/client/views/collections/CollectionSubView.tsx | 2 +- .../views/collections/CollectionTimeView.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 2 +- src/client/views/nodes/DocumentLinksButton.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 6 +- src/client/views/nodes/FieldView.tsx | 6 +- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/LinkDocPreview.tsx | 4 +- src/client/views/nodes/PDFBox.tsx | 24 ------ src/client/views/nodes/ScreenshotBox.tsx | 4 +- src/client/views/nodes/VideoBox.tsx | 8 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 6 +- .../views/nodes/formattedText/RichTextMenu.tsx | 8 +- src/client/views/pdf/AnchorMenu.tsx | 1 - src/fields/Doc.ts | 12 ++- src/fields/URLField.ts | 15 +++- src/mobile/ImageUpload.tsx | 2 +- src/server/server_Initialization.ts | 3 +- 22 files changed, 58 insertions(+), 146 deletions(-) (limited to 'src/client/views') diff --git a/src/Utils.ts b/src/Utils.ts index d87c3cc6b..194c38a6f 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -67,7 +67,6 @@ export namespace Utils { export function prepend(extension: string): string { return window.location.origin + extension; } - export function fileUrl(filename: string): string { return prepend(`/files/${filename}`); } diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts index 899e65a16..ff9460b62 100644 --- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts +++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts @@ -285,7 +285,7 @@ export namespace GooglePhotos { const photos = await endpoint(); const albumId = StrCast(collection.albumId); if (albumId && albumId.length) { - const enrichment = new photos.TextEnrichment(content || Utils.prepend("/doc/" + collection[Id])); + const enrichment = new photos.TextEnrichment(content || Doc.globalServerPath(collection)); const position = new photos.AlbumPosition(photos.AlbumPosition.POSITIONS.FIRST_IN_ALBUM); const enrichmentItem = await photos.albums.addEnrichment(albumId, enrichment, position); if (enrichmentItem) { diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e863b4198..ac52b0acf 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -549,84 +549,6 @@ export namespace Docs { */ export namespace Create { - /** - * Synchronously returns a collection into which - * the device documents will be put. This is initially empty, - * but gets populated by updates from the web socket. When everything is over, - * this function cleans up after itself. - * s - * Look at Websocket.ts for the server-side counterpart to this - * function. - */ - export function Buxton() { - let responded = false; - const loading = new Doc; - loading.title = "Please wait for the import script..."; - const parent = TreeDocument([loading], { - title: "The Buxton Collection", - _width: 400, - _height: 400 - }); - const parentProto = Doc.GetProto(parent); - const { _socket } = DocServer; - - // just in case, clean up - _socket.off(MessageStore.BuxtonDocumentResult.Message); - _socket.off(MessageStore.BuxtonImportComplete.Message); - - // this is where the client handles the receipt of a new valid parsed document - Utils.AddServerHandler(_socket, MessageStore.BuxtonDocumentResult, ({ device, invalid: errors }) => { - if (!responded) { - responded = true; - parentProto.data = new List(); - } - if (device) { - const { title, __images, additionalMedia } = device; - delete device.__images; - delete device.additionalMedia; - const { ImageDocument, StackingDocument } = Docs.Create; - const constructed = __images.map(({ url, nativeWidth, nativeHeight }) => ({ url: Utils.prepend(url), nativeWidth, nativeHeight })); - const deviceImages = constructed.map(({ url, nativeWidth, nativeHeight }, i) => { - const imageDoc = ImageDocument(url, { - title: `image${i}.${extname(url)}`, - _nativeWidth: nativeWidth, - _nativeHeight: nativeHeight - }); - const media = additionalMedia[i]; - if (media) { - for (const key of Object.keys(media)) { - imageDoc[`additionalMedia_${key}`] = Utils.prepend(`/files/${key}/buxton/${media[key]}`); - } - } - return imageDoc; - }); - // the main document we create - const doc = StackingDocument(deviceImages, { title, hero: new ImageField(constructed[0].url) }); - doc.nameAliases = new List([title.toLowerCase()]); - // add the parsed attributes to this main document - Doc.Get.FromJson({ data: device, appendToExisting: { targetDoc: Doc.GetProto(doc) } }); - Doc.AddDocToList(parentProto, "data", doc); - } else if (errors) { - console.log("Documents:" + errors); - } else { - alert("A Buxton document import was completely empty (??)"); - } - }); - - // when the import is complete, we stop listening for these creation - // and termination events and alert the user - Utils.AddServerHandler(_socket, MessageStore.BuxtonImportComplete, ({ deviceCount, errorCount }) => { - _socket.off(MessageStore.BuxtonDocumentResult.Message); - _socket.off(MessageStore.BuxtonImportComplete.Message); - alert(`Successfully imported ${deviceCount} device${deviceCount === 1 ? "" : "s"}, with ${errorCount} error${errorCount === 1 ? "" : "s"}, in ${(Date.now() - startTime) / 1000} seconds.`); - }); - const startTime = Date.now(); - Utils.Emit(_socket, MessageStore.BeginBuxtonImport, ""); // signal the server to start importing - return parent; // synchronously return the collection, to be populateds - } - - Scripting.addGlobal(Buxton); - /** * This function receives the relevant document prototype and uses * it to create a new of that base-level prototype, or the @@ -675,7 +597,7 @@ export namespace Docs { } export function ImageDocument(url: string, options: DocumentOptions = {}) { - const imgField = new ImageField(new URL(url)); + const imgField = new ImageField(url); return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: path.basename(url), ...options }); } @@ -689,11 +611,11 @@ export namespace Docs { } export function VideoDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(new URL(url)), options); + return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(url), options); } export function YoutubeDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(new URL(url)), options); + return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(url), options); } export function WebCamDocument(url: string, options: DocumentOptions = {}) { @@ -709,7 +631,7 @@ export namespace Docs { } export function AudioDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(new URL(url)), + return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url), { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any }); } @@ -782,11 +704,11 @@ export namespace Docs { } export function PdfDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(new URL(url)), options); + return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(url), options); } export function WebDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.WEB), url ? new WebField(new URL(url)) : undefined, options); + return InstanceFromProto(Prototypes.get(DocumentType.WEB), url ? new WebField(url) : undefined, options); } export function HtmlDocument(html: string, options: DocumentOptions = {}) { diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts index 8ddfce772..635673025 100644 --- a/src/client/util/HypothesisUtils.ts +++ b/src/client/util/HypothesisUtils.ts @@ -126,7 +126,7 @@ export namespace Hypothesis { }); const annotationId = StrCast(linkDoc.annotationId); - const linkUrl = Utils.prepend("/doc/" + sourceDoc[Id]); + const linkUrl = Doc.globalServerPath(sourceDoc); const interval = setInterval(() => {// keep trying to edit until annotations have loaded and editing is successful !success && document.dispatchEvent(new CustomEvent<{ targetUrl: string, id: string }>("deleteLink", { detail: { targetUrl: linkUrl, id: annotationId }, diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index a5d27f038..0d9b64d24 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -303,7 +303,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: } else { const path = window.location.origin + "/doc/"; if (text.startsWith(path)) { - const docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0]; + 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 diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 339163510..08b5e6bac 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -37,7 +37,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { @observable _focusRangeFilters: Opt; getAnchor = () => { - const anchor = Docs.Create.HTMLAnchorDocument({ + const anchor = Docs.Create.HTMLAnchorDocument([], { title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as any, annotationOn: this.rootDoc }); diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index a2e36f12e..82bad971d 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -196,7 +196,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent { const [{ result }] = await Networking.UploadFilesToServer(e.data); if (!(result instanceof Error)) { - this.props.Document[this.props.fieldKey] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client)); + this.props.Document[this.props.fieldKey] = new AudioField(result.accessPaths.agnostic.client); } }; this._recordStart = new Date().getTime(); diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index ddc36daa1..aa3f10188 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -194,7 +194,7 @@ export class DocumentLinksButton extends React.Component GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" }); moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" }); } - moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" }); + moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: "fingerprint" }); } } @@ -760,7 +760,7 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" }); - helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" }); + helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" }); !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" }); cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" }); } @@ -885,7 +885,7 @@ export class DocumentViewInternal extends DocComponent { const [{ result }] = await Networking.UploadFilesToServer(e.data); if (!(result instanceof Error)) { - const audioDoc = Docs.Create.AudioDocument(Utils.prepend(result.accessPaths.agnostic.client), { title: "audio test", _width: 200, _height: 32 }); + const audioDoc = Docs.Create.AudioDocument(result.accessPaths.agnostic.client, { title: "audio test", _width: 200, _height: 32 }); audioDoc.treeViewExpandedView = "layout"; const audioAnnos = Cast(self.dataDoc[self.LayoutFieldKey + "-audioAnnotations"], listSpec(Doc)); if (audioAnnos === undefined) { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 86250c9d1..ebbc1138a 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -64,9 +64,9 @@ export class FieldView extends React.Component { // else if (field instaceof PresBox) { // return ; // } - else if (field instanceof VideoField) { - return ; - } + // else if (field instanceof VideoField) { + // return ; + // } // else if (field instanceof AudioField) { // return ; //} diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index cfd43bb62..2c0106960 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -238,7 +238,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent { @computed get href() { if (this.props.hrefs?.length) { const href = this.props.hrefs[this._hrefInd]; - if (href.indexOf(Utils.prepend("/doc/")) !== 0) { // link to a web page URL -- try to show a preview + if (href.indexOf(Doc.localServerPath()) !== 0) { // link to a web page URL -- try to show a preview if (href.startsWith("https://en.wikipedia.org/wiki/")) { wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(action(summary => this._toolTipText = summary.substring(0, 500)))); } else { setTimeout(action(() => this._toolTipText = "url => " + href)); } } else { // hyperlink to a document .. decode doc id and retrieve from the server. this will trigger vals() being invalidated - const anchorDoc = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; + const anchorDoc = href.replace(Doc.localServerPath(), "").split("?")[0]; anchorDoc && DocServer.GetRefField(anchorDoc).then(action(anchor => { if (anchor instanceof Doc && DocListCast(anchor.links).length) { this._linkDoc = DocListCast(anchor.links)[0]; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 8f61e252b..0b451e2b4 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -53,30 +53,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href)); else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action(pdf => this._pdf = pdf)); } - - const backup = "oldPath"; - const href = this.pdfUrl?.url.href; - if (href) { - const pathCorrectionTest = /upload\_[a-z0-9]{32}.(.*)/g; - const matches = pathCorrectionTest.exec(href); - // console.log("\nHere's the { url } being fed into the outer regex:"); - // console.log(href); - // console.log("And here's the 'properPath' build from the captured filename:\n"); - if (matches !== null && href.startsWith(window.location.origin)) { - const properPath = Utils.prepend(`/files/pdfs/${matches[0]}`); - //console.log(properPath); - if (!properPath.includes(href)) { - console.log(`The two (url and proper path) were not equal`); - const proto = Doc.GetProto(this.props.Document); - proto[this.props.fieldKey] = new PdfField(properPath); - proto[backup] = href; - } else { - //console.log(`The two (url and proper path) were equal`); - } - } else { - console.log("Outer matches was null!"); - } - } } componentWillUnmount() { this._selectReactionDisposer?.(); } diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 700f8a7d3..0e235a62d 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -227,7 +227,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent { const [{ result }] = await Networking.UploadFilesToServer(aud_chunks); if (!(result instanceof Error)) { - this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client)); + this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(result.accessPaths.agnostic.client); } }; this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true }); @@ -244,7 +244,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent { const aspect = this.player!.videoWidth / this.player!.videoHeight; Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth); @@ -182,8 +178,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - const url = this.choosePath(Utils.prepend(relative)); + private createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => { + const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath; const width = this.layoutDoc._width || 1; const height = this.layoutDoc._height || 0; const imageSummary = Docs.Create.ImageDocument(url, { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 140d39929..f7e9ee028 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -371,7 +371,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; const anchor = Docs.Create.TextanchorDocument(); const alink = DocUtils.MakeLink({ doc: anchor }, { doc: target }, "automatic")!; - const allAnchors = [{ href: Utils.prepend("/doc/" + anchor[Id]), title: "a link", anchorId: anchor[Id] }]; + const allAnchors = [{ href: Doc.localServerPath(anchor), title: "a link", anchorId: anchor[Id] }]; const link = this._editorView!.state.schema.marks.linkAnchor.create({ allAnchors, title: "auto link", location }); tr = tr.addMark(flattened[i].from, flattened[i].to, link); }); @@ -705,7 +705,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp let tr = state.tr.addMark(sel.from, sel.to, splitter); if (sel.from !== sel.to) { const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: this._editorView?.state.doc.textBetween(sel.from, sel.to) }); - const href = targetHref ?? Utils.prepend("/doc/" + anchor[Id]); + const href = targetHref ?? Doc.localServerPath(anchor); if (anchor !== anchorDoc) this.addDocument(anchor); tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => { if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { @@ -1042,7 +1042,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } const marks = [...node.marks]; const linkIndex = marks.findIndex(mark => mark.type.name === "link"); - const allLinks = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }]; + const allLinks = [{ href: Doc.globalServerPath(linkId), title, linkId }]; const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "add:right", title, docref: true }); marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link); return node.mark(marks); diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index a6f8ff2e2..fb4114023 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -821,8 +821,8 @@ export class RichTextMenu extends AntimodeMenu { if (link) { const href = link.attrs.allAnchors.length > 0 ? link.attrs.allAnchors[0].href : undefined; if (href) { - if (href.indexOf(Utils.prepend("/doc/")) === 0) { - const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; + if (href.indexOf(Doc.localServerPath()) === 0) { + const linkclicked = href.replace(Doc.localServerPath(), "").split("?")[0]; if (linkclicked) { const linkDoc = await DocServer.GetRefField(linkclicked); if (linkDoc instanceof Doc) { @@ -864,8 +864,8 @@ export class RichTextMenu extends AntimodeMenu { const allAnchors = linkAnchor.attrs.allAnchors.slice(); this.TextView.RemoveAnchorFromSelection(allAnchors); // bcz: Argh ... this will remove the link from the document even it's anchored somewhere else in the text which happens if only part of the anchor text was selected. - allAnchors.filter((aref: any) => aref?.href.indexOf(Utils.prepend("/doc/")) === 0).forEach((aref: any) => { - const anchorId = aref.href.replace(Utils.prepend("/doc/"), "").split("?")[0]; + allAnchors.filter((aref: any) => aref?.href.indexOf(Doc.localServerPath()) === 0).forEach((aref: any) => { + const anchorId = aref.href.replace(Doc.localServerPath(), "").split("?")[0]; anchorId && DocServer.GetRefField(anchorId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc)); }); } diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 70ca19842..55816ed52 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -85,7 +85,6 @@ export class AnchorMenu extends AntimodeMenu { @action toggleLinkPopup = (e: React.MouseEvent) => { //ignore the potential null type error because this method cannot be called unless the user selects text and clicks the link button - console.log(window.getSelection().toString()) //change popup visibility field to visible this._showLinkPopup = !this._showLinkPopup; } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 111fd3f0d..a7e5d8541 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -596,7 +596,7 @@ export namespace Doc { const mapped = cloneMap.get(id); return href + (mapped ? mapped[Id] : id); }; - const regex = `(${Utils.prepend("/doc/")})([^"]*)`; + const regex = `(${Doc.localServerPath()})([^"]*)`; const re = new RegExp(regex, "g"); copy[key] = new RichTextField(field.Data.replace(/("textId":|"audioId":|"anchorId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text); }); @@ -896,6 +896,16 @@ export namespace Doc { return true; } + + // converts a document id to a url path on the server + export function globalServerPath(doc: Doc | string = ""): string { + return Utils.prepend("/doc/" + (doc instanceof Doc ? doc[Id] : doc)); + } + // converts a document id to a url path on the server + export function localServerPath(doc?: Doc): string { + return "/doc/" + (doc ? doc[Id] : ""); + } + export function overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) { const doc2Layout = Doc.Layout(doc2); const doc1Layout = Doc.Layout(doc1); diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts index fb71160ca..d96e8a70a 100644 --- a/src/fields/URLField.ts +++ b/src/fields/URLField.ts @@ -3,14 +3,17 @@ import { serializable, custom } from "serializr"; import { ObjectField } from "./ObjectField"; import { ToScriptString, ToString, Copy } from "./FieldSymbols"; import { Scripting, scriptingGlobal } from "../client/util/Scripting"; +import { Utils } from "../Utils"; function url() { return custom( function (value: URL) { - return value.href; + return value.origin === window.location.origin ? + value.pathname : + value.href; }, function (jsonValue: string) { - return new URL(jsonValue); + return new URL(jsonValue, window.location.origin); } ); } @@ -24,15 +27,21 @@ export abstract class URLField extends ObjectField { constructor(url: URL | string) { super(); if (typeof url === "string") { - url = new URL(url); + url = url.startsWith("http") ? new URL(url) : new URL(url, window.location.origin); } this.url = url; } [ToScriptString]() { + if (Utils.prepend(this.url.pathname) === this.url.href) { + return `new ${this.constructor.name}("${this.url.pathname}")`; + } return `new ${this.constructor.name}("${this.url.href}")`; } [ToString]() { + if (Utils.prepend(this.url.pathname) === this.url.href) { + return this.url.pathname; + } return this.url.href; } diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index 98696496f..f910d765e 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -50,7 +50,7 @@ export class Uploader extends React.Component { if (result instanceof Error) { return; } - const path = Utils.prepend(result.accessPaths.agnostic.client); + const path = result.accessPaths.agnostic.client; let doc = null; // Case 1: File is a video if (file.type === "video/mp4") { diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index e40f2b8e5..0f4a067fc 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -142,8 +142,9 @@ function registerCorsProxy(server: express.Express) { const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/; server.use("/corsProxy", async (req, res) => { - const requrl = decodeURIComponent(req.url.substring(1)); const referer = req.headers.referer ? decodeURIComponent(req.headers.referer) : ""; + const requrlraw = decodeURIComponent(req.url.substring(1)); + const requrl = requrlraw.startsWith("/") ? referer + requrlraw : requrlraw; // cors weirdness here... // if the referer is a cors page and the cors() route (I think) redirected to /corsProxy/ and the requested url path was relative, // then we redirect again to the cors referer and just add the relative path. -- cgit v1.2.3-70-g09d2 From 2abbe23a9c6054e4ff5314333bba25b88554d1ec Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 3 Aug 2021 11:41:48 -0400 Subject: fixed links being blown away in sidebars. --- src/client/views/SidebarAnnos.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 010418be5..7b11723c3 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -33,7 +33,7 @@ interface ExtraProps { export class SidebarAnnos extends React.Component { constructor(props: Readonly) { super(props); - this.props.dataDoc[this.sidebarKey] = new List(); + // this.props.dataDoc[this.sidebarKey] = new List(); // bcz: can't do this here. it blows away existing things and isn't a robust solution for making sure the field exists -- instead this should happen when the document is created and/or shared } _stackRef = React.createRef(); @computed get allHashtags() { -- cgit v1.2.3-70-g09d2 From 0e8aef275346b4ba3bc1bb91fda17a335c307bf1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 3 Aug 2021 13:09:48 -0400 Subject: fixed using viewSpecs when following links to set filters/viewType properly. --- src/client/util/DocumentManager.ts | 2 +- src/client/views/collections/CollectionSubView.tsx | 6 +++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 17 ++++++++++++----- src/client/views/nodes/DocumentView.tsx | 14 ++++++++------ 4 files changed, 24 insertions(+), 15 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 5b092258a..9c6100249 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -163,7 +163,7 @@ export class DocumentManager { const targetDocContext = contextDoc || annotatedDoc; const targetDocContextView = targetDocContext && getFirstDocView(targetDocContext); const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView; - if (!docView && annoContainerView && !focusView) { + if (!docView && annoContainerView) { annoContainerView.focus(targetDoc); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below } if (focusView) { diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 0d9b64d24..3b143aeef 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -82,13 +82,13 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: return Cast(this.dataField, listSpec(Doc)); } docFilters = () => { - return [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])]; + return (this.props.docFilters?.() ?? Cast(this.props.Document._docFilters, listSpec("string"), [])); } docRangeFilters = () => { - return [...this.props.docRangeFilters(), ...Cast(this.props.Document._docRangeFilters, listSpec("string"), [])]; + return (this.props.docRangeFilters?.() ?? Cast(this.props.Document._docRangeFilters, listSpec("string"), [])); } searchFilterDocs = () => { - return [...this.props.searchFilterDocs(), ...DocListCast(this.props.Document._searchFilterDocs)]; + return this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs); } @computed.struct get childDocs() { TraceMobx(); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 143d8e070..ecc93285e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -159,8 +159,8 @@ export class CollectionFreeFormView extends CollectionSubView this.fitToContent || force ? this.fitToContentVals : undefined; - freeformDocFilters = () => this._focusFilters || this.docFilters(); - freeformRangeDocFilters = () => this._focusRangeFilters || this.docRangeFilters(); + freeformDocFilters = () => this._focusFilters; + freeformRangeDocFilters = () => this._focusRangeFilters; reverseNativeScaling = () => this.fitToContent ? true : false; panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX); panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY); @@ -1197,14 +1197,21 @@ export class CollectionFreeFormView extends CollectionSubView(StrListCast(anchor.docFilters)); - this.layoutDoc._docRangeFilters = new List(StrListCast(anchor.docRangeFilters)); + } else { + if (anchor.docFilters) { + this.layoutDoc._docFilters = new List(StrListCast(anchor.docFilters)); + } + if (anchor.docRangeFilters) { + this.layoutDoc._docRangeFilters = new List(StrListCast(anchor.docRangeFilters)); + } } return 0; } getAnchor = () => { + if (this.props.Document.annotationOn) { + return this.rootDoc; + } const anchor = Docs.Create.TextanchorDocument({ title: StrCast(this.layoutDoc._viewType), annotationOn: this.rootDoc }); const proto = Doc.GetProto(anchor); proto[ViewSpecPrefix + "_viewType"] = this.layoutDoc._viewType; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f716eb7b1..c218b805e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -64,7 +64,7 @@ export enum ViewAdjustment { doNothing = 0 } -export const ViewSpecPrefix = "_VIEW"; // field prefix for anchor fields that are immediately copied over to the target document when link is followed. Other anchor properties will be copied over in the specific setViewSpec() method on their view (which allows for seting preview values instead of writing to the document) +export const ViewSpecPrefix = "viewSpec"; // field prefix for anchor fields that are immediately copied over to the target document when link is followed. Other anchor properties will be copied over in the specific setViewSpec() method on their view (which allows for seting preview values instead of writing to the document) export interface DocFocusOptions { originalTarget?: Doc; // set in JumpToDocument, used by TabDocView to determine whether to fit contents to tab @@ -105,9 +105,9 @@ export interface DocumentViewSharedProps { styleProvider: Opt; focus: DocFocusFunc; fitWidth?: () => boolean; - docFilters: () => string[]; - docRangeFilters: () => string[]; - searchFilterDocs: () => Doc[]; + docFilters?: () => Opt; + docRangeFilters?: () => Opt; + searchFilterDocs?: () => Opt; whenChildContentsActiveChanged: (isActive: boolean) => void; rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected addDocTab: (doc: Doc, where: string) => boolean; @@ -421,7 +421,9 @@ export class DocumentViewInternal extends DocComponent { LightboxView.SetCookie(StrCast(anchor["cookies-set"])); // copying over _VIEW fields immediately allows the view type to switch to create the right _componentView - Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec])); + Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => { + this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]); + }); // after a timeout, the right _componentView should have been created, so call it to update its view spec values setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false)); const focusSpeed = this._componentView?.scrollFocus?.(anchor, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here @@ -837,7 +839,7 @@ export class DocumentViewInternal extends DocComponent !d.hidden); + const filtered = DocUtils.FilterDocs(this.rootDoc.type === DocumentType.RTF ? this.allLinks : this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => !d.hidden); return filtered.map((link, i) =>