diff options
author | bobzel <zzzman@gmail.com> | 2021-03-18 00:08:13 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2021-03-18 00:08:13 -0400 |
commit | 188e1e57860f58e9ebe3536a0e1f7cd84ea0db80 (patch) | |
tree | 72d1824332295c6a58b7b5933125133b19c5c0f3 | |
parent | 6f4f0ffb9f4ab816cf6055c62afc6f79b8e4961f (diff) |
cleaned up link making. Documents don't add to the Undo stack when being created and Initializing is set. Links to text regions automatically update their link line endpoints even if autoMove isn't set. regularized initialization fields to avoid special cases about setting delegate keys without a leading "_"
-rw-r--r-- | src/client/documents/Documents.ts | 227 | ||||
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 32 | ||||
-rw-r--r-- | src/client/util/LinkManager.ts | 1 | ||||
-rw-r--r-- | src/client/views/MarqueeAnnotator.tsx | 7 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx | 13 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 16 | ||||
-rw-r--r-- | src/client/views/nodes/LinkAnchorBox.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/RichTextRules.ts | 2 | ||||
-rw-r--r-- | src/fields/Doc.ts | 8 | ||||
-rw-r--r-- | src/fields/ScriptField.ts | 2 | ||||
-rw-r--r-- | src/fields/util.ts | 6 |
12 files changed, 156 insertions, 164 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 8aa478cc1..24409933e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -58,7 +58,6 @@ import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; import { DocumentType } from "./DocumentTypes"; import { EquationBox } from "../views/nodes/EquationBox"; import { FunctionPlotBox } from "../views/nodes/FunctionPlotBox"; -import { script } from "googleapis/build/src/apis/script"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; const path = require('path'); @@ -93,7 +92,7 @@ type PEVt = PEInfo | "none" | "all"; type DROPt = DAInfo | dropActionType; export class DocumentOptions { system?: BOOLt = new BoolInfo("is this a system created/owned doc"); - dropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto this doc "); + _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else"); childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection "); targetDropAction?: DROPt = new DAInfo("what should happen to the source document when ??? "); color?: string; // foreground color data doc @@ -156,9 +155,11 @@ export class DocumentOptions { y?: number; z?: number; // whether document is in overlay (1) or not (0 or undefined) author?: string; - layoutKey?: string; + _layoutKey?: string; type?: string; title?: string; + "acl-Public"?: string; // public permissions + "_acl-Public"?: string; // public permissions version?: string; // version identifier for a document label?: string; hidden?: boolean; @@ -182,7 +183,7 @@ export class DocumentOptions { caption?: RichTextField; opacity?: number; defaultBackgroundColor?: string; - isLinkButton?: boolean; + _isLinkButton?: boolean; // marks a document as a button that will follow its primary link when clicked isFolder?: boolean; lastFrame?: number; // the last frame of a frame-based collection (e.g., progressive slide) activeFrame?: number; // the active frame of a document in a frame base collection @@ -206,9 +207,15 @@ export class DocumentOptions { dockingConfig?: string; annotationOn?: Doc; isPushpin?: boolean; - removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document + _removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document iconShape?: string; // shapes of the fonticon border + layout_linkView?: Doc; // view template for a link document linkRelationship?: string; // type of relatinoship a link represents + linkDisplay?: boolean; // whether a link line should be dipslayed between the two link anchors + anchor1?: Doc; + anchor2?: Doc; + "anchor1-useLinkSmallAnchor"?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection) + "anchor2-useLinkSmallAnchor"?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection) ignoreClick?: boolean; onClick?: ScriptField; onDoubleClick?: ScriptField; @@ -304,31 +311,31 @@ export namespace Docs { const TemplateMap: TemplateMap = new Map([ [DocumentType.RTF, { layout: { view: FormattedTextBox, dataField: "text" }, - options: { _height: 150, _xMargin: 10, _yMargin: 10 } + options: { _height: 150, _xMargin: 10, _yMargin: 10, links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.SEARCH, { layout: { view: SearchBox, dataField: defaultDataKey }, - options: { _width: 400 } + options: { _width: 400, links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.FILTER, { layout: { view: FilterBox, dataField: defaultDataKey }, - options: { _width: 400 } + options: { _width: 400, links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.COLOR, { layout: { view: ColorBox, dataField: defaultDataKey }, - options: { _nativeWidth: 220, _nativeHeight: 300 } + options: { _nativeWidth: 220, _nativeHeight: 300, links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.IMG, { layout: { view: ImageBox, dataField: defaultDataKey }, - options: {} + options: { links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.WEB, { layout: { view: WebBox, dataField: defaultDataKey }, - options: { _height: 300, _fitWidth: true } + options: { _height: 300, _fitWidth: true, links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.COL, { layout: { view: CollectionView, dataField: defaultDataKey }, - options: { _panX: 0, _panY: 0, _viewScale: 1 } // , _width: 500, _height: 500 } + options: { _panX: 0, _panY: 0, _viewScale: 1, links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.KVP, { layout: { view: KeyValueBox, dataField: defaultDataKey }, @@ -336,15 +343,15 @@ export namespace Docs { }], [DocumentType.VID, { layout: { view: VideoBox, dataField: defaultDataKey }, - options: { _currentTimecode: 0 }, + options: { _currentTimecode: 0, links: ComputedField.MakeFunction("links(self)") as any }, }], [DocumentType.AUDIO, { layout: { view: AudioBox, dataField: defaultDataKey }, - options: { _height: 35, backgroundColor: "lightGray" } + options: { _height: 35, backgroundColor: "lightGray", links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.PDF, { layout: { view: PDFBox, dataField: defaultDataKey }, - options: { _curPage: 1, _fitWidth: true } + options: { _curPage: 1, _fitWidth: true, links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.IMPORT, { layout: { view: DirectoryImportBox, dataField: defaultDataKey }, @@ -352,7 +359,11 @@ export namespace Docs { }], [DocumentType.LINK, { layout: { view: LinkBox, dataField: defaultDataKey }, - options: { _height: 150, description: "" } + options: { + _height: 150, description: "", links: ComputedField.MakeFunction("links(self)") as any, + linkBoxExcludedKeys: new List(["treeViewExpandedView", "aliases", "treeViewHideTitle", "_removeDropProperties", + "linkBoxExcludedKeys", "treeViewOpen", "aliasNumber", "isPrototype", "creationDate", "author"]) + } }], [DocumentType.LINKDB, { data: new List<Doc>(), @@ -365,53 +376,58 @@ export namespace Docs { options: { childDropAction: "alias", title: "Global Script Database" } }], [DocumentType.SCRIPTING, { - layout: { view: ScriptingBox, dataField: defaultDataKey } + layout: { view: ScriptingBox, dataField: defaultDataKey }, + options: { links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.YOUTUBE, { layout: { view: YoutubeBox, dataField: defaultDataKey } }], [DocumentType.LABEL, { layout: { view: LabelBox, dataField: defaultDataKey }, + options: { links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.EQUATION, { layout: { view: EquationBox, dataField: defaultDataKey }, + options: { links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.FUNCPLOT, { layout: { view: FunctionPlotBox, dataField: defaultDataKey }, + options: { links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.BUTTON, { layout: { view: LabelBox, dataField: "onClick" }, + options: { links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.SLIDER, { layout: { view: SliderBox, dataField: defaultDataKey }, + options: { links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.PRES, { layout: { view: PresBox, dataField: defaultDataKey }, - options: {} + options: { links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.FONTICON, { layout: { view: FontIconBox, dataField: defaultDataKey }, - options: { _width: 40, _height: 40, borderRounding: "100%" }, + options: { _width: 40, _height: 40, borderRounding: "100%", links: ComputedField.MakeFunction("links(self)") as any }, }], - // [DocumentType.RECOMMENDATION, { - // layout: { view: RecommendationsBox, dataField: defaultDataKey }, - // options: { _width: 200, _height: 200 }, - // }], [DocumentType.WEBCAM, { - layout: { view: DashWebRTCVideo, dataField: defaultDataKey } + layout: { view: DashWebRTCVideo, dataField: defaultDataKey }, + options: { links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.PRESELEMENT, { layout: { view: PresElementBox, dataField: defaultDataKey } }], [DocumentType.INK, { layout: { view: InkingStroke, dataField: defaultDataKey }, - options: { _fontFamily: "cursive", backgroundColor: "transparent" } + options: { _fontFamily: "cursive", backgroundColor: "transparent", links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.SCREENSHOT, { layout: { view: ScreenshotBox, dataField: defaultDataKey }, + options: { links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.COMPARISON, { layout: { view: ComparisonBox, dataField: defaultDataKey }, + options: { links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.GROUPDB, { data: new List<Doc>(), @@ -419,10 +435,12 @@ export namespace Docs { options: { childDropAction: "alias", title: "Global Group Database" } }], [DocumentType.GROUP, { - layout: { view: EmptyBox, dataField: defaultDataKey } + layout: { view: EmptyBox, dataField: defaultDataKey }, + options: { links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.TEXTANCHOR, { - layout: { view: EmptyBox, dataField: defaultDataKey } + layout: { view: EmptyBox, dataField: defaultDataKey }, + options: { links: ComputedField.MakeFunction("links(self)") as any } }] ]); @@ -611,8 +629,6 @@ export namespace Docs { Scripting.addGlobal(Buxton); - const delegateKeys = ["x", "y", "system", "layoutKey", "dropAction", "lockedPosiiton", "childDropAction", "isLinkButton", "removeDropProperties", "treeViewOpen"]; - /** * This function receives the relevant document prototype and uses * it to create a new of that base-level prototype, or the @@ -632,59 +648,33 @@ export namespace Docs { * main document. */ export function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data", protoId?: string) { - const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys, "^_"); - - protoProps.system = delegateProps.system; - - if (!("author" in protoProps)) { - protoProps.author = Doc.CurrentUserEmail; - } - - if (!("creationDate" in protoProps)) { - protoProps.creationDate = new DateField; - protoProps[`${fieldKey}-lastModified`] = new DateField; - } - - protoProps.isPrototype = true; - - const dataDoc = MakeDataDelegate(proto, protoProps, data, fieldKey, protoId); - const viewDoc = Doc.MakeDelegate(dataDoc, delegId); - + const viewKeys = ["x", "y", "system"]; + const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, "^_"); + + dataProps.system = viewProps.system; + dataProps.isPrototype = true; + dataProps.author = Doc.CurrentUserEmail; + dataProps.creationDate = new DateField; + dataProps[`${fieldKey}-lastModified`] = new DateField; + dataProps["acl-Override"] = "None"; + dataProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; + 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<Doc>(); + const dataDoc = Doc.assign(Doc.MakeDelegate(proto, protoId), dataProps as any, undefined, true); - dataDoc[fieldKey + "-annotations"] = new List<Doc>(); - - proto.links = ComputedField.MakeFunction("links(self)"); - - viewDoc.author = Doc.CurrentUserEmail; - viewDoc.type !== DocumentType.LINK && viewDoc.type !== DocumentType.LABEL && DocUtils.MakeLinkToActiveAudio(viewDoc); - - viewDoc["acl-Public"] = dataDoc["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; - viewDoc["acl-Override"] = dataDoc["acl-Override"] = "None"; + const viewDoc = Doc.MakeDelegate(dataDoc, delegId); + viewProps.author = Doc.CurrentUserEmail; + viewProps.type !== DocumentType.LINK && viewDoc.type !== DocumentType.LABEL && DocUtils.MakeLinkToActiveAudio(viewDoc); + viewProps["acl-Override"] = "None"; + viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; + Doc.assign(viewDoc, viewProps, true, true); !Doc.IsSystem(dataDoc) && ![DocumentType.PDFANNO, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR, DocumentType.TEXTANCHOR].includes(proto.type as any) && - !dataDoc.isFolder && !protoProps.annotationOn && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", dataDoc); - - return Doc.assign(viewDoc, delegateProps, true); - } + !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", dataDoc); - /** - * This function receives the relevant top level document prototype - * and models a new instance by delegating from it. - * - * Note that it stores the data it recieves at the delegate's data key, - * and applies any document options to this new delegate / instance. - * @param proto the prototype from which to model this new delegate - * @param options initial values to apply to this new delegate - * @param value the data to store in this new delegate - */ - function MakeDataDelegate<D extends Field>(proto: Doc, options: DocumentOptions, value?: D, fieldKey: string = "data", id: string | undefined = undefined) { - const deleg = Doc.MakeDelegate(proto, id); - if (value !== undefined) { - deleg[fieldKey] = value; - } - return Doc.assign(deleg, options as any); + return viewDoc; } export function ImageDocument(url: string, options: DocumentOptions = {}) { @@ -763,24 +753,15 @@ export namespace Docs { } export function LinkDocument(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, options: DocumentOptions = {}, id?: string) { - const doc = InstanceFromProto(Prototypes.get(DocumentType.LINK), undefined, { - childDontRegisterViews: true, - isLinkButton: true, treeViewHideTitle: true, backgroundColor: "lightblue", // lightblue is default color for linking dot and link documents text comment area - treeViewExpandedView: "fields", removeDropProperties: new List(["_layerTags", "isLinkButton"]), ...options + const linkDoc = InstanceFromProto(Prototypes.get(DocumentType.LINK), undefined, { + childDontRegisterViews: true, treeViewOpen: true, anchor1: source.doc, anchor2: target.doc, + _isLinkButton: true, treeViewHideTitle: true, backgroundColor: "lightblue", // lightblue is default color for linking dot and link documents text comment area + treeViewExpandedView: "fields", _removeDropProperties: new List(["_layerTags", "isLinkButton"]), ...options }, id); - const linkDocProto = Doc.GetProto(doc); - linkDocProto.treeViewOpen = true;// setting this in the instance creator would set it on the view document. - linkDocProto.anchor1 = source.doc; - linkDocProto.anchor2 = target.doc; - - if (linkDocProto.linkBoxExcludedKeys === undefined) { - Cast(linkDocProto.proto, Doc, null).linkBoxExcludedKeys = new List(["treeViewExpandedView", "aliases", "treeViewHideTitle", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "aliasNumber", "isPrototype", "creationDate", "author"]); - Cast(linkDocProto.proto, Doc, null).layoutKey = undefined; - } - LinkManager.Instance.addLink(doc); + LinkManager.Instance.addLink(linkDoc); - return doc; + return linkDoc; } export function InkDocument(color: string, tool: string, strokeWidth: string, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) { @@ -1093,42 +1074,46 @@ export namespace DocUtils { if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target.doc) return; if (target.doc === Doc.UserDoc()) return undefined; + const makeLink = action((linkDoc: Doc, showPopup?: number[]) => { + if (showPopup) { + LinkManager.currentLink = linkDoc; - const makeLink = action((linkDoc: Doc, showPopup: number[]) => { - LinkManager.currentLink = linkDoc; + TaskCompletionBox.textDisplayed = "Link Created"; + TaskCompletionBox.popupX = showPopup[0]; + TaskCompletionBox.popupY = showPopup[1] - 33; + TaskCompletionBox.taskCompleted = true; - TaskCompletionBox.textDisplayed = "Link Created"; - TaskCompletionBox.popupX = showPopup[0]; - TaskCompletionBox.popupY = showPopup[1] - 33; - TaskCompletionBox.taskCompleted = true; + LinkDescriptionPopup.popupX = showPopup[0]; + LinkDescriptionPopup.popupY = showPopup[1]; + LinkDescriptionPopup.descriptionPopup = true; - LinkDescriptionPopup.popupX = showPopup[0]; - LinkDescriptionPopup.popupY = showPopup[1]; - LinkDescriptionPopup.descriptionPopup = true; + const rect = document.body.getBoundingClientRect(); + if (LinkDescriptionPopup.popupX + 200 > rect.width) { + LinkDescriptionPopup.popupX -= 190; + TaskCompletionBox.popupX -= 40; + } + if (LinkDescriptionPopup.popupY + 100 > rect.height) { + LinkDescriptionPopup.popupY -= 40; + TaskCompletionBox.popupY -= 40; + } - const rect = document.body.getBoundingClientRect(); - if (LinkDescriptionPopup.popupX + 200 > rect.width) { - LinkDescriptionPopup.popupX -= 190; - TaskCompletionBox.popupX -= 40; - } - if (LinkDescriptionPopup.popupY + 100 > rect.height) { - LinkDescriptionPopup.popupY -= 40; - TaskCompletionBox.popupY -= 40; + setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500); } - - setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500); + return linkDoc; }); - const linkDoc = Docs.Create.LinkDocument(source, target, { linkRelationship, layoutKey: "layout_linkView", description }, id); - Doc.GetProto(linkDoc)["anchor1-useLinkSmallAnchor"] = source.doc.useLinkSmallAnchor; - Doc.GetProto(linkDoc)["anchor2-useLinkSmallAnchor"] = target.doc.useLinkSmallAnchor; - linkDoc.linkDisplay = true; - linkDoc.hidden = true; - Doc.GetProto(linkDoc)["acl-Public"] = linkDoc["acl-Public"] = SharingPermissions.Add; - linkDoc.layout_linkView = Cast(Cast(Doc.UserDoc()["template-button-link"], Doc, null).dragFactory, Doc, null); - Doc.GetProto(linkDoc).title = ComputedField.MakeFunction("generateLinkTitle(self)"); - showPopup && makeLink(linkDoc, showPopup); - return linkDoc; + return makeLink(Docs.Create.LinkDocument(source, target, { + title: ComputedField.MakeFunction("generateLinkTitle(self)") as any, + "anchor1-useLinkSmallAnchor": source.doc.useLinkSmallAnchor ? true : undefined, + "anchor2-useLinkSmallAnchor": target.doc.useLinkSmallAnchor ? true : undefined, + "acl-Public": SharingPermissions.Add, + "_acl-Public": SharingPermissions.Add, + layout_linkView: Cast(Cast(Doc.UserDoc()["template-button-link"], Doc, null).dragFactory, Doc, null), + linkDisplay: true, hidden: true, + linkRelationship, + _layoutKey: "layout_linkView", + description + }, id), showPopup); } export function DocumentFromField(target: Doc, fieldKey: string, proto?: Doc, options?: DocumentOptions): Doc | undefined { @@ -1357,7 +1342,7 @@ export namespace DocUtils { const pushpin = Docs.Create.FontIconDocument({ title: "pushpin", label: "", annotationOn: Cast(doc.annotationOn, Doc, null), isPushpin: true, icon: "map-pin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), backgroundColor: "#ACCEF7", - _width: 15, _height: 15, _xPadding: 0, isLinkButton: true, _timecodeToShow: Cast(doc._timecodeToShow, "number", null) + _width: 15, _height: 15, _xPadding: 0, _isLinkButton: true, _timecodeToShow: Cast(doc._timecodeToShow, "number", null) }); Doc.AddDocToList(context, Doc.LayoutFieldKey(context) + "-annotations", pushpin); const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin", ""); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index e601d33f7..d7ab3564c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -483,12 +483,12 @@ export class CurrentUserUtils { title, toolTip, ignoreClick, - dropAction: "alias", + _dropAction: "alias", onDragStart: drag ? ScriptField.MakeFunction(drag) : undefined, onClick: click ? ScriptField.MakeScript(click) : undefined, backgroundColor, _hideContextMenu: true, - removeDropProperties: new List<string>(["_stayInCollection"]), + _removeDropProperties: new List<string>(["_stayInCollection"]), _stayInCollection: true, dragFactory, clickFactory, @@ -546,8 +546,8 @@ export class CurrentUserUtils { title, target, backgroundColor: "black", - dropAction: "alias", - removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]), + _dropAction: "alias", + _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]), _width: 60, _height: 60, watchedDocuments, @@ -626,27 +626,27 @@ export class CurrentUserUtils { // sets up the main document for the mobile button static mobileButton = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MulticolumnDocument(docs, { ...opts, - dropAction: undefined, removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 900, _nativeHeight: 250, _width: 900, _height: 250, _yMargin: 15, + _removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 900, _nativeHeight: 250, _width: 900, _height: 250, _yMargin: 15, borderRounding: "5px", boxShadow: "0 0", system: true }) as any as Doc // sets up the text container for the information contained within the mobile button static mobileTextContainer = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MultirowDocument(docs, { ...opts, - dropAction: undefined, removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 450, _nativeHeight: 250, _width: 450, _height: 250, _yMargin: 25, + _removeDropProperties: new List<string>(["dropAction"]), _nativeWidth: 450, _nativeHeight: 250, _width: 450, _height: 250, _yMargin: 25, backgroundColor: "rgba(0,0,0,0)", borderRounding: "0", boxShadow: "0 0", ignoreClick: true, system: true }) as any as Doc // Sets up the title of the button static mobileButtonText = (opts: DocumentOptions, buttonTitle: string) => Docs.Create.TextDocument(buttonTitle, { ...opts, - dropAction: undefined, title: buttonTitle, _fontSize: "37px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", system: true + title: buttonTitle, _fontSize: "37px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", system: true }) as any as Doc // Sets up the description of the button static mobileButtonInfo = (opts: DocumentOptions, buttonInfo: string) => Docs.Create.TextDocument(buttonInfo, { ...opts, - dropAction: undefined, title: "info", _fontSize: "25px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, system: true + title: "info", _fontSize: "25px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, system: true }) as any as Doc @@ -660,11 +660,12 @@ export class CurrentUserUtils { ]; return docProtoData.map(data => Docs.Create.FontIconDocument({ _nativeWidth: 10, _nativeHeight: 10, _width: 10, _height: 10, title: data.title, icon: data.icon, - dropAction: data.pointerDown ? "copy" : undefined, ignoreClick: data.ignoreClick, + _dropAction: data.pointerDown ? "copy" : undefined, ignoreClick: data.ignoreClick, onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, clipboard: data.clipboard, onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined, - backgroundColor: data.backgroundColor, removeDropProperties: new List<string>(["dropAction"]), dragFactory: data.dragFactory, system: true + backgroundColor: data.backgroundColor, + _removeDropProperties: new List<string>(["dropAction"]), dragFactory: data.dragFactory, system: true })); } @@ -721,7 +722,7 @@ export class CurrentUserUtils { // setup a color picker if (doc.myColorPicker === undefined) { const color = Docs.Create.ColorDocument({ - title: "color picker", _width: 300, dropAction: "alias", _hideContextMenu: true, _stayInCollection: true, _forceActive: true, removeDropProperties: new List<string>(["dropAction", "_stayInCollection", "_hideContextMenu", "forceActive"]), system: true + title: "color picker", _width: 300, _dropAction: "alias", _hideContextMenu: true, _stayInCollection: true, _forceActive: true, _removeDropProperties: new List<string>(["dropAction", "_stayInCollection", "_hideContextMenu", "forceActive"]), system: true }); doc.myColorPicker = new PrefetchProxy(color); } @@ -856,16 +857,16 @@ export class CurrentUserUtils { })) as any as Doc static ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({ - ...opts, dropAction: "alias", removeDropProperties: new List<string>(["dropAction", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true + ...opts, _dropAction: "alias", _removeDropProperties: new List<string>(["_dropAction", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true })) as any as Doc /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window static setupDockedButtons(doc: Doc) { if (doc["dockedBtn-undo"] === undefined) { - doc["dockedBtn-undo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), dontUndo: true, _stayInCollection: true, dropAction: "alias", _hideContextMenu: true, removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "click to undo", title: "undo", icon: "undo-alt", system: true }); + doc["dockedBtn-undo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), dontUndo: true, _stayInCollection: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "click to undo", title: "undo", icon: "undo-alt", system: true }); } if (doc["dockedBtn-redo"] === undefined) { - doc["dockedBtn-redo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), dontUndo: true, _stayInCollection: true, dropAction: "alias", _hideContextMenu: true, removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "click to redo", title: "redo", icon: "redo-alt", system: true }); + doc["dockedBtn-redo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), dontUndo: true, _stayInCollection: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "click to redo", title: "redo", icon: "redo-alt", system: true }); } if (doc.dockedBtns === undefined) { doc.dockedBtns = CurrentUserUtils.blist({ title: "docked buttons", ignoreClick: true }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]); @@ -906,9 +907,8 @@ export class CurrentUserUtils { if (!sharedDocs) { sharedDocs = Docs.Create.StackingDocument([], { title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, - _showTitle: "title", ignoreClick: true, _lockedPosition: true, + _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Add, "_acl-Public": SharingPermissions.Add }, sharingDocumentId + "outer", sharingDocumentId); - (sharedDocs as Doc)["acl-Public"] = Doc.GetProto(sharedDocs as Doc)["acl-Public"] = SharingPermissions.Add; } if (sharedDocs instanceof Doc) { sharedDocs.userColor = sharedDocs.userColor || "rgb(202, 202, 202)"; diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 0512864df..dde68401e 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,4 +1,3 @@ -import { runInAction } from "mobx"; import { computedFn } from "mobx-utils"; import { Doc, DocListCast, Opt } from "../../fields/Doc"; import { BoolCast, Cast, StrCast } from "../../fields/Types"; diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 1e97f9b41..2e50c9b6d 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -98,13 +98,13 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { @undoBatch @action - makeAnnotationDocument = (color: string): Opt<Doc> => { + makeAnnotationDocument = (color: string, isLinkButton?: boolean): Opt<Doc> => { if (this.props.savedAnnotations.size === 0) return undefined; if ((Array.from(this.props.savedAnnotations.values())[0][0] as any).marqueeing) { const scale = this.props.scaling?.() || 1; const anno = Array.from(this.props.savedAnnotations.values())[0][0]; const containerOffset = this.props.containerOffset?.() || [0, 0]; - const marqueeAnno = Docs.Create.FreeformDocument([], { backgroundColor: color, annotationOn: this.props.rootDoc, title: "Annotation on " + this.props.rootDoc.title }); + const marqueeAnno = Docs.Create.FreeformDocument([], { _isLinkButton: isLinkButton, backgroundColor: color, annotationOn: this.props.rootDoc, title: "Annotation on " + this.props.rootDoc.title }); marqueeAnno.x = (parseInt(anno.style.left || "0") - containerOffset[0]) / scale; marqueeAnno.y = (parseInt(anno.style.top || "0") - containerOffset[1]) / scale + NumCast(this.props.scrollTop); marqueeAnno._height = parseInt(anno.style.height || "0") / scale; @@ -144,9 +144,8 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { highlight = (color: string, isLinkButton: boolean) => { // creates annotation documents for current highlights const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]); - const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color); + const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton); annotationDoc && this.props.addDocument(annotationDoc); - annotationDoc && (annotationDoc.isLinkButton = isLinkButton); return annotationDoc as Doc ?? undefined; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 51cb9387a..a8f5e6dd2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -40,7 +40,6 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo if (SnappingManager.GetIsDragging() || !A.ContentDiv || !B.ContentDiv) return; setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render() setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line. - if (!linkDoc.linkAutoMove) return; const acont = A.rootDoc.type === DocumentType.LINK ? A.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : []; const bcont = B.rootDoc.type === DocumentType.LINK ? B.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : []; const adiv = acont.length ? acont[0] : A.ContentDiv; @@ -60,8 +59,10 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo const targetBhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.anchor2 as Doc)[Id])).lastElement(); if ((!targetAhyperlink && !a.width) || (!targetBhyperlink && !b.width)) return; if (!targetAhyperlink) { - linkDoc.anchor1_x = (apt.point.x - aleft) / awidth * 100; - linkDoc.anchor1_y = (apt.point.y - atop) / aheight * 100; + if (linkDoc.linkAutoMove) { + linkDoc.anchor1_x = (apt.point.x - aleft) / awidth * 100; + linkDoc.anchor1_y = (apt.point.y - atop) / aheight * 100; + } } else { const m = targetAhyperlink.getBoundingClientRect(); const mp = A.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5); @@ -69,8 +70,10 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo linkDoc.anchor1_y = Math.min(1, mp[1] / A.props.PanelHeight()) * 100; } if (!targetBhyperlink) { - linkDoc.anchor2_x = (bpt.point.x - bleft) / bwidth * 100; - linkDoc.anchor2_y = (bpt.point.y - btop) / bheight * 100; + if (linkDoc.linkAutoMove) { + linkDoc.anchor2_x = (bpt.point.x - bleft) / bwidth * 100; + linkDoc.anchor2_y = (bpt.point.y - btop) / bheight * 100; + } } else { const m = targetBhyperlink.getBoundingClientRect(); const mp = B.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a1bedcf6e..a9372aa64 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -558,9 +558,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps @undoBatch @action toggleFollowLink = (location: Opt<string>, zoom: boolean, setPushpin: boolean): void => { this.Document.ignoreClick = false; - this.Document.isLinkButton = !this.Document.isLinkButton; - setPushpin && (this.Document.isPushpin = this.Document.isLinkButton); - if (this.Document.isLinkButton && !this.onClickHandler) { + this.Document._isLinkButton = !this.Document._isLinkButton; + setPushpin && (this.Document.isPushpin = this.Document._isLinkButton); + if (this.Document._isLinkButton && !this.onClickHandler) { this.Document.followLinkZoom = zoom; this.Document.followLinkLocation = location; } else { @@ -570,13 +570,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps @undoBatch @action toggleTargetOnClick = (): void => { this.Document.ignoreClick = false; - this.Document.isLinkButton = true; + this.Document._isLinkButton = true; this.Document.isPushpin = true; } @undoBatch @action followLinkOnClick = (location: Opt<string>, zoom: boolean,): void => { this.Document.ignoreClick = false; - this.Document.isLinkButton = true; + this.Document._isLinkButton = true; this.Document.isPushpin = false; this.Document.followLinkZoom = zoom; this.Document.followLinkLocation = location; @@ -584,14 +584,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps @undoBatch @action selectOnClick = (): void => { this.Document.ignoreClick = false; - this.Document.isLinkButton = false; + this.Document._isLinkButton = false; this.Document.isPushpin = false; this.Document.onClick = this.layoutDoc.onClick = undefined; } @undoBatch noOnClick = (): void => { this.Document.ignoreClick = false; - this.Document.isLinkButton = false; + this.Document._isLinkButton = false; } @undoBatch deleteClicked = () => this.props.removeDocument?.(this.props.Document); @@ -630,7 +630,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps } this.Document.followLinkLocation = "inPlace"; this.Document.followLinkZoom = true; - this.Document.isLinkButton = true; + this.Document._isLinkButton = true; } @action diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 392565402..3d72d047e 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -93,7 +93,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch } openLinkTargetOnRight = (e: React.MouseEvent) => { const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null)); - alias.isLinkButton = undefined; + alias._isLinkButton = undefined; alias._layerTags = undefined; alias.layoutKey = "layout"; this.props.addDocTab(alias, "add:right"); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 575fbcf2e..0bded738a 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -152,8 +152,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD const b = Docs.Create.LabelDocument({ x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 1), _width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(), + _isLinkButton: true }); - b.isLinkButton = true; this.props.addDocument?.(b); DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, "video snapshot"); Networking.PostToServer("/youtubeScreenshot", { @@ -183,7 +183,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD const height = this.layoutDoc._height || 0; const imageSummary = Docs.Create.ImageDocument(url, { _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc), - x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0), isLinkButton: true, + x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0), _isLinkButton: true, _width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc._currentTimecode || 0) + " image-" }); Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc)); diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 4b9b78211..3fd7d61fa 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -90,7 +90,7 @@ export class RichTextRules { textDoc.inlineTextCount = numInlines + 1; const inlineFieldKey = "inline" + numInlines; // which field on the text document this annotation will write to const inlineLayoutKey = "layout_" + inlineFieldKey; // the field holding the layout string that will render the inline annotation - const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9px", title: "inline comment" }); + const textDocInline = Docs.Create.TextDocument("", { _layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: "9px", title: "inline comment" }); textDocInline.title = inlineFieldKey; // give the annotation its own title textDocInline["title-custom"] = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 4f9377aa0..5bc770c15 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -94,6 +94,7 @@ export const AclAddonly = Symbol("AclAddonly"); export const AclEdit = Symbol("AclEdit"); export const AclAdmin = Symbol("AclAdmin"); export const UpdatingFromServer = Symbol("UpdatingFromServer"); +export const Initializing = Symbol("Initializing"); export const ForceServerWrite = Symbol("ForceServerWrite"); export const CachedUpdates = Symbol("Cached updates"); @@ -189,6 +190,7 @@ export class Doc extends RefField { private [UpdatingFromServer]: boolean = false; private [ForceServerWrite]: boolean = false; + public [Initializing]: boolean = false; private [Update] = (diff: any) => { (!this[UpdatingFromServer] || this[ForceServerWrite]) && DocServer.UpdateField(this[Id], diff); @@ -371,7 +373,8 @@ export namespace Doc { * @param fields the fields to project onto the target. Its type signature defines a mapping from some string key * to a potentially undefined field, where each entry in this mapping is optional. */ - export function assign<K extends string>(doc: Doc, fields: Partial<Record<K, Opt<Field>>>, skipUndefineds: boolean = false) { + export function assign<K extends string>(doc: Doc, fields: Partial<Record<K, Opt<Field>>>, skipUndefineds: boolean = false, isInitializing = false) { + isInitializing && (doc[Initializing] = true); for (const key in fields) { if (fields.hasOwnProperty(key)) { const value = fields[key]; @@ -380,6 +383,7 @@ export namespace Doc { } } } + isInitializing && (doc[Initializing] = false); return doc; } @@ -779,10 +783,12 @@ export namespace Doc { export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc> { if (doc) { const delegate = new Doc(id, true); + delegate[Initializing] = true; delegate.proto = doc; delegate.author = Doc.CurrentUserEmail; if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DataSym], "aliases", delegate); title && (delegate.title = title); + delegate[Initializing] = false; return delegate; } return undefined; diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 9345ecde5..bd93bf5fb 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -150,7 +150,7 @@ export class ScriptField extends ObjectField { } public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Field }) { const compiled = CompileScript(script, { - params: { this: Doc.name, self: Doc.name, _last_: "any", ...params }, + params: { this: Doc?.name || "Doc", self: Doc?.name || "Doc", _last_: "any", ...params }, typecheck: false, editable: true, addReturn: addReturn, diff --git a/src/fields/util.ts b/src/fields/util.ts index 6038a0534..36604c790 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -1,5 +1,5 @@ import { UndoManager } from "../client/util/UndoManager"; -import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, DataSym, DocListCast, AclAdmin, HeightSym, WidthSym, updateCachedAcls, AclUnset, DocListCastAsync, ForceServerWrite } from "./Doc"; +import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, DataSym, DocListCast, AclAdmin, HeightSym, WidthSym, updateCachedAcls, AclUnset, DocListCastAsync, ForceServerWrite, Initializing } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; @@ -96,7 +96,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number } else { DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue); } - (!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) && UndoManager.AddEvent({ + !receiver[Initializing] && (!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) && UndoManager.AddEvent({ redo: () => receiver[prop] = value, undo: () => receiver[prop] = curValue }); @@ -162,7 +162,7 @@ export function GetEffectiveAcl(target: any, user?: string): symbol { } function getPropAcl(target: any, prop: string | symbol | number) { - if (prop === UpdatingFromServer || target[UpdatingFromServer] || prop === AclSym) return AclAdmin; // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent + if (prop === UpdatingFromServer || prop === Initializing || target[UpdatingFromServer] || prop === AclSym) return AclAdmin; // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent if (prop && DocServer.PlaygroundFields?.includes(prop.toString())) return AclEdit; // playground props are always editable return GetEffectiveAcl(target); } |