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/DocComponent.tsx') 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 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/DocComponent.tsx') 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 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/DocComponent.tsx') 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 7995c8b9b810df4ed5ade1db71fcb0e2de3d3da2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 14 Jul 2021 09:47:18 -0400 Subject: fixed selections so that they persist on deselect to allow linking a selection on one document directly to a selection on another document. Also fixed recently closed to move closed document to top of list. --- src/client/views/DocComponent.tsx | 5 ++++- src/client/views/MarqueeAnnotator.tsx | 23 ++++++++++++----------- src/client/views/nodes/ImageBox.tsx | 15 ++++++++++++--- src/client/views/nodes/WebBox.tsx | 2 +- src/client/views/pdf/AnchorMenu.tsx | 4 ++-- 5 files changed, 31 insertions(+), 18 deletions(-) (limited to 'src/client/views/DocComponent.tsx') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index a878a7afb..0b54e3cd6 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -154,7 +154,10 @@ export function ViewBoxAnnotatableComponent

{ AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight("rgba(173, 216, 230, 0.75)", true)); AnchorMenu.Instance.Highlight = this.highlight; - AnchorMenu.Instance.GetAnchor = () => this.highlight("rgba(173, 216, 230, 0.75)", true); + AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap) => this.highlight("rgba(173, 216, 230, 0.75)", true, savedAnnotations); /** * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation. * It also initiates a Drag/Drop interaction to place the text annotation. @@ -102,12 +102,13 @@ export class MarqueeAnnotator extends React.Component { @undoBatch @action - makeAnnotationDocument = (color: string, isLinkButton?: boolean): Opt => { - if (this.props.savedAnnotations.size === 0) return undefined; - const savedAnnos = Array.from(this.props.savedAnnotations.values())[0]; + makeAnnotationDocument = (color: string, isLinkButton?: boolean, savedAnnotations?: ObservableMap): Opt => { + const savedAnnoMap = savedAnnotations ?? this.props.savedAnnotations; + if (savedAnnoMap.size === 0) return undefined; + const savedAnnos = Array.from(savedAnnoMap.values())[0]; if (savedAnnos.length && (savedAnnos[0] as any).marqueeing) { const scale = this.props.scaling?.() || 1; - const anno = Array.from(this.props.savedAnnotations.values())[0][0]; + const anno = savedAnnos[0]; const containerOffset = this.props.containerOffset?.() || [0, 0]; 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; @@ -115,7 +116,7 @@ export class MarqueeAnnotator extends React.Component { marqueeAnno._height = parseInt(anno.style.height || "0") / scale; marqueeAnno._width = parseInt(anno.style.width || "0") / scale; anno.remove(); - this.props.savedAnnotations.clear(); + savedAnnoMap.clear(); return marqueeAnno; } @@ -123,7 +124,7 @@ export class MarqueeAnnotator extends React.Component { let maxX = -Number.MAX_VALUE; let minY = Number.MAX_VALUE; const annoDocs: Doc[] = []; - this.props.savedAnnotations.forEach((value: HTMLDivElement[], key: number) => value.map(anno => { + savedAnnoMap.forEach((value: HTMLDivElement[], key: number) => value.map(anno => { const textRegion = new Doc(); textRegion.x = parseInt(anno.style.left ?? "0"); textRegion.y = parseInt(anno.style.top ?? "0"); @@ -142,15 +143,15 @@ export class MarqueeAnnotator extends React.Component { textRegionAnnoProto.x = Math.max(maxX, 0); // mainAnnoDocProto.text = this._selectionText; textRegionAnnoProto.textInlineAnnotations = new List(annoDocs); - this.props.savedAnnotations.clear(); + savedAnnoMap.clear(); return textRegionAnno; } @action - highlight = (color: string, isLinkButton: boolean) => { + highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap) => { // creates annotation documents for current highlights const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]); - const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton); - annotationDoc && this.props.addDocument(annotationDoc); + const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations); + !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc); return annotationDoc as Doc ?? undefined; } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 13dfad0fe..b803c94ba 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -7,7 +7,7 @@ import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { createSchema, makeInterface } from '../../../fields/Schema'; import { ComputedField } from '../../../fields/ScriptField'; -import { Cast, NumCast } from '../../../fields/Types'; +import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, OmitKeys, returnOne, Utils } from '../../../Utils'; @@ -28,6 +28,8 @@ import "./ImageBox.scss"; import React = require("react"); import { InkTool } from '../../../fields/InkField'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; +import { AnchorMenu } from '../pdf/AnchorMenu'; +import { Docs } from '../../documents/Documents'; const path = require('path'); export const pageSchema = createSchema({ @@ -62,6 +64,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent { + const anchor = AnchorMenu.Instance?.GetAnchor(this._savedAnnotations); + anchor && this.addDocument(anchor); + return anchor ?? this.rootDoc; + } + componentDidMount() { this.props.setContentView?.(this); // bcz: do not remove this. without it, stepping into an image in the lightbox causes an infinite loop.... this._disposers.sizer = reaction(() => ( @@ -74,8 +83,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent this.props.isSelected(), selected => !selected && setTimeout(() => { - Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove())); - this._savedAnnotations.clear(); + // Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove())); + // this._savedAnnotations.clear(); })); this._disposers.path = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }), ({ nativeSize, width }) => { diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index a16881b66..5791e2310 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -176,7 +176,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { const anchor = - AnchorMenu.Instance?.GetAnchor() ?? + AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ?? Docs.Create.TextanchorDocument({ title: StrCast(this.rootDoc.title + " " + this.layoutDoc._scrollTop), annotationOn: this.rootDoc, diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 86b124de5..c24c4eaaf 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -1,7 +1,7 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; -import { action, computed, observable, IReactionDisposer, reaction } from "mobx"; +import { action, computed, observable, IReactionDisposer, reaction, ObservableMap } from "mobx"; import { observer } from "mobx-react"; import { ColorState } from "react-color"; import { Doc, Opt } from "../../../fields/Doc"; @@ -46,7 +46,7 @@ export class AnchorMenu extends AntimodeMenu { public OnClick: (e: PointerEvent) => void = unimplementedFunction; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; public Highlight: (color: string, isPushpin: boolean) => Opt = (color: string, isPushpin: boolean) => undefined; - public GetAnchor: () => Opt = () => undefined; + public GetAnchor: (savedAnnotations?: ObservableMap) => Opt = () => undefined; public Delete: () => void = unimplementedFunction; public AddTag: (key: string, value: string) => boolean = returnFalse; public PinToPres: () => void = unimplementedFunction; -- cgit v1.2.3-70-g09d2 From 6ecd0222232a9fd1ac1f9c915cfb192a0da7dac1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 15 Jul 2021 10:33:54 -0400 Subject: hide sidebar handle and fix scrolling for secondary formattedTextBox views (eg when in a layout template) --- src/client/views/DocComponent.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 18 ++++++++++-------- 3 files changed, 12 insertions(+), 10 deletions(-) (limited to 'src/client/views/DocComponent.tsx') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 0b54e3cd6..da8af7cc0 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -119,7 +119,7 @@ export function ViewBoxAnnotatableComponent

{ const style: { [key: string]: any } = {}; - const divKeys = ["width", "height", "fontSize", "left", "background", "top", "pointerEvents", "position"]; + const divKeys = ["width", "height", "fontSize", "left", "background", "left", "right", "top", "bottom", "pointerEvents", "position"]; const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a property expression string: { script } into a value return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result as string || ""; }; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5646a9790..60fa462ad 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -137,7 +137,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps { hideDecorationTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings treeViewDoc?: Doc; isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events - isContentActive: () => boolean | undefined; // whether a document should handle pointer events + isContentActive: () => boolean | undefined; // whether document contents should handle pointer events contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents radialMenu?: String[]; LayoutTemplateString?: string; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 911ec1560..2070b9863 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -876,7 +876,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp var quickScroll: string | undefined = ""; this._disposers.scroll = reaction(() => NumCast(this.layoutDoc._scrollTop), pos => { - if (!this._ignoreScroll && this._scrollRef.current) { + if (!this._ignoreScroll && this._scrollRef.current && !this.props.dontSelectOnLoad) { const viewTrans = quickScroll ?? StrCast(this.Document._viewTransition); const durationMiliStr = viewTrans.match(/([0-9]*)ms/); const durationSecStr = viewTrans.match(/([0-9.]*)s/); @@ -1413,9 +1413,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } onScroll = (e: React.UIEvent) => { if (!LinkDocPreview.LinkInfo && this._scrollRef.current) { - this._ignoreScroll = true; - this.layoutDoc._scrollTop = this._scrollRef.current.scrollTop; - this._ignoreScroll = false; + if (this.props.dontSelectOnLoad) { + console.log("here"); + } else { + this._ignoreScroll = true; + this.layoutDoc._scrollTop = this._scrollRef.current.scrollTop; + this._ignoreScroll = false; + } } } tryUpdateScrollHeight() { @@ -1517,8 +1521,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const selPad = Math.min(margins, 10); const padding = Math.max(margins + ((selected && !this.layoutDoc._singleLine) || minimal ? -selPad : 0), 0); const selPaddingClass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : ""; - const col = this.props.color ? this.props.color : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color); - const back = this.props.background ? this.props.background : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor); return (

this.isContentActive() && e.stopPropagation()} @@ -1566,8 +1568,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }} />
- {(this.props.noSidebar || this.Document._noSidebar) || !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) : this.sidebarCollection} - {(this.props.noSidebar || this.Document._noSidebar) || this.Document._singleLine ? (null) : this.sidebarHandle} + {(this.props.noSidebar || this.Document._noSidebar) || this.props.dontSelectOnLoad || !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) : this.sidebarCollection} + {(this.props.noSidebar || this.Document._noSidebar) || this.props.dontSelectOnLoad || this.Document._singleLine ? (null) : this.sidebarHandle} {!this.layoutDoc._showAudio ? (null) : this.audioHandle} -- cgit v1.2.3-70-g09d2 From 14e66ac5bcdaa5e244be68ccb8cbb0c495917910 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 23 Jul 2021 14:41:18 -0400 Subject: fixed issues with layoutString templates (eg customView): scale properly when in a time view, have a data doc, scripts are called with 'scale' parmeter --- src/client/util/CurrentUserUtils.ts | 16 +++++++++------ src/client/views/DocComponent.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 11 +++++----- .../views/nodes/formattedText/FormattedTextBox.tsx | 11 +++++----- src/fields/Doc.ts | 24 ++++++++++++++++++++-- 5 files changed, 45 insertions(+), 19 deletions(-) (limited to 'src/client/views/DocComponent.tsx') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 8a98304b2..22504f102 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -405,14 +405,18 @@ export class CurrentUserUtils { selection: { type: "text", anchor: 1, head: 1 }, storedMarks: [] }; - const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { title: "text", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, cloneFieldFilter: new List(["system"]) }, "header"); // text needs to be a space to allow templateText to be created + const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { + title: "text", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all", + _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, _fitWidth: true, + cloneFieldFilter: new List(["system"]) + }, "header"); const headerBtnHgt = 10; headerTemplate[DataSym].layout = - "
" + - ` ` + - " " + - ` Metadata` + - "
"; + "" + + ` ` + + " " + + ` Metadata` + + ""; // "
" + // " " + diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index da8af7cc0..0b70ce68d 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -119,7 +119,7 @@ export function ViewBoxAnnotatableComponent

{ const style: { [key: string]: any } = {}; - const divKeys = ["width", "height", "fontSize", "left", "background", "left", "right", "top", "bottom", "pointerEvents", "position"]; + const divKeys = ["width", "height", "fontSize", "transform", "left", "background", "left", "right", "top", "bottom", "pointerEvents", "position"]; const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a property expression string: { script } into a value return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result as string || ""; }; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index a0a40becb..9b75cd8f9 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -64,6 +64,7 @@ interface HTMLtagProps { htmltag: string; onClick?: ScriptField; onInput?: ScriptField; + scaling: number; } //" {this.title}" @@ -82,7 +83,7 @@ interface HTMLtagProps { export class HTMLtag extends React.Component { click = (e: React.MouseEvent) => { const clickScript = (this.props as any).onClick as Opt; - clickScript?.script.run({ this: this.props.Document, self: this.props.RootDoc }); + clickScript?.script.run({ this: this.props.Document, self: this.props.RootDoc, scale: this.props.scaling }); } onInput = (e: React.FormEvent) => { const onInputScript = (this.props as any).onInput as Opt; @@ -90,9 +91,9 @@ export class HTMLtag extends React.Component { } render() { const style: { [key: string]: any } = {}; - const divKeys = OmitKeys(this.props, ["children", "htmltag", "RootDoc", "Document", "key", "onInput", "onClick", "__proto__"]).omit; + const divKeys = OmitKeys(this.props, ["children", "htmltag", "RootDoc", "scaling", "Document", "key", "onInput", "onClick", "__proto__"]).omit; const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a propery expression string: { script } into a value - return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ self: this.props.RootDoc, this: this.props.Document }).result as string || ""; + return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.props.RootDoc, this: this.props.Document, scale: this.props.scaling }).result as string || ""; }; Object.keys(divKeys).map((prop: string) => { const p = (this.props as any)[prop] as string; @@ -184,7 +185,7 @@ export class DocumentContentsView extends React.Component with corresponding HTML tag as in: becomes const replacer2 = (match: any, p1: string, offset: any, string: any) => { - return ` 1) { const code = XRegExp.matchRecursive(splits[1], "{", "}", "", { valueNames: ["between", "left", "match", "right", "between"] }); layoutFrame = splits[0] + ` ${func}={props.${func}} ` + splits[1].substring(code[1].end + 1); - return ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name, value: "string" }); + return ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name, scale: "number", value: "string" }); } return undefined; // add input function to props diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 95d8f555c..6dd63fb47 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -71,6 +71,7 @@ export interface FormattedTextBoxProps { xPadding?: number; // used to override document's settings for xMargin --- see CollectionCarouselView yPadding?: number; noSidebar?: boolean; + dontScale?: boolean; dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded (and mark as not being associated with scrollTop document field) } export const GoogleRef = "googleDocId"; @@ -126,7 +127,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @computed get scrollHeight() { return NumCast(this.rootDoc[this.fieldKey + "-scrollHeight"]); } @computed get sidebarHeight() { return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + "-height"]); } @computed get titleHeight() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0; } - @computed get autoHeightMargins() { return this.titleHeight + (this.layoutDoc._autoHeightMargins && !this.props.dontSelectOnLoad ? NumCast(this.layoutDoc._autoHeightMargins) : 0); } + @computed get autoHeightMargins() { return this.titleHeight + NumCast(this.layoutDoc._autoHeightMargins); } @computed get _recording() { return this.dataDoc?.mediaState === "recording"; } set _recording(value) { !this.dataDoc.recordingSource && (this.dataDoc.mediaState = value ? "recording" : undefined); @@ -1524,10 +1525,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp

this.isContentActive() && e.stopPropagation()} style={{ - transform: `scale(${scale})`, - transformOrigin: "top left", - width: `${100 / scale}%`, - height: `${100 / scale}%`, + transform: this.props.dontScale ? undefined : `scale(${scale})`, + transformOrigin: this.props.dontScale ? undefined : "top left", + width: this.props.dontScale ? undefined : `${100 / scale}%`, + height: this.props.dontScale ? undefined : `${100 / scale}%`, // overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined, ...this.styleFromLayoutString(scale) // this converts any expressions in the format string to style props. e.g., }}> diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index bd0ba3ad7..464a8ad05 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -803,6 +803,27 @@ export namespace Doc { return undefined; } + // Makes a delegate of a document by first creating a delegate where data should be stored + // (ie, the 'data' doc), and then creates another delegate of that (ie, the 'layout' doc). + // This is appropriate if you're trying to create a document that behaves like all + // regularly created documents (e.g, text docs, pdfs, etc which all have data/layout docs) + export function MakeDelegateWithProto(doc: Doc, id?: string, title?: string): Doc { + const delegateProto = new Doc(); + delegateProto[Initializing] = true; + delegateProto.proto = doc; + delegateProto.author = Doc.CurrentUserEmail; + delegateProto.isPrototype = true; + title && (delegateProto.title = title); + const delegate = new Doc(id, true); + delegate[Initializing] = true; + delegate.proto = delegateProto; + delegate.author = Doc.CurrentUserEmail; + Doc.AddDocToList(delegateProto[DataSym], "aliases", delegate); + delegate[Initializing] = false; + delegateProto[Initializing] = false; + return delegate; + } + let _applyCount: number = 0; export function ApplyTemplate(templateDoc: Doc) { if (templateDoc) { @@ -1150,8 +1171,7 @@ export namespace Doc { return ndoc; } export function delegateDragFactory(dragFactory: Doc) { - const ndoc = Doc.MakeDelegate(dragFactory); - ndoc.isPrototype = true; + const ndoc = Doc.MakeDelegateWithProto(dragFactory); if (ndoc && dragFactory["dragFactory-count"] !== undefined) { dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; Doc.GetProto(ndoc).title = ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(); -- cgit v1.2.3-70-g09d2 From e7bbdd3b489fea1c508af53345cd0d1f31685cb9 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 6 Aug 2021 18:03:38 -0400 Subject: collabortion fixes: added new acl for allowing people to edit their own text within the same note, fixed playground fields to write to the server without updating other clients. --- src/client/DocServer.ts | 5 +- src/client/documents/Documents.ts | 10 ++-- src/client/util/CurrentUserUtils.ts | 6 +- src/client/util/SharingManager.tsx | 13 ++-- src/client/views/DocComponent.tsx | 6 +- src/client/views/MainView.tsx | 2 +- src/client/views/MarqueeAnnotator.tsx | 4 +- src/client/views/PropertiesView.tsx | 14 ++--- .../collections/collectionFreeForm/MarqueeView.tsx | 4 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 12 +++- .../formattedText/ProsemirrorExampleTransfer.ts | 70 ++++++++++++++-------- src/fields/Doc.ts | 6 +- src/fields/util.ts | 22 ++++--- 13 files changed, 105 insertions(+), 69 deletions(-) (limited to 'src/client/views/DocComponent.tsx') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 59278d2af..d9ae7d64c 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -59,7 +59,10 @@ export namespace DocServer { export var PlaygroundFields: string[]; export function setPlaygroundFields(livePlaygroundFields: string[]) { DocServer.PlaygroundFields = livePlaygroundFields; - livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.Playground)); + livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground)); + } + export function IsPlaygroundField(field: string) { + return DocServer.PlaygroundFields?.includes(field.replace(/^_/, "")); } export function setFieldWriteMode(field: string, writeMode: WriteMode) { diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 24f777e88..93f0880a4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -577,7 +577,7 @@ export namespace Docs { dataProps.creationDate = new DateField; dataProps[`${fieldKey}-lastModified`] = new DateField; dataProps["acl-Override"] = "None"; - dataProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; + dataProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; dataProps[fieldKey] = data; @@ -588,7 +588,7 @@ export namespace Docs { viewProps.author = Doc.CurrentUserEmail; viewProps["acl-Override"] = "None"; - viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; + viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewProps, true, true); ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc); @@ -699,7 +699,7 @@ export namespace Docs { I.author = Doc.CurrentUserEmail; I.rotation = 0; I.data = new InkField(points); - I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; + I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; I["acl-Override"] = "None"; I[Initializing] = false; return I; @@ -1038,8 +1038,8 @@ export namespace DocUtils { 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, + "acl-Public": SharingPermissions.Augment, + "_acl-Public": SharingPermissions.Augment, layout_linkView: Cast(Cast(Doc.UserDoc()["template-button-link"], Doc, null).dragFactory, Doc, null), linkDisplay: true, hidden: true, linkRelationship, diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 1af6607a7..14c43fb1c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -917,7 +917,7 @@ export class CurrentUserUtils { linkDocs = new Doc(linkDatabaseId, true); (linkDocs as Doc).author = Doc.CurrentUserEmail; (linkDocs as Doc).data = new List([]); - (linkDocs as Doc)["acl-Public"] = SharingPermissions.Add; + (linkDocs as Doc)["acl-Public"] = SharingPermissions.Augment; } doc.myLinkDatabase = new PrefetchProxy(linkDocs); } @@ -926,10 +926,10 @@ export class CurrentUserUtils { if (!sharedDocs) { sharedDocs = Docs.Create.TreeDocument([], { title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, - _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Add, "_acl-Public": SharingPermissions.Add, + _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, _chromeHidden: true, boxShadow: "0 0", }, sharingDocumentId + "outer", sharingDocumentId); - (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Add; + (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment; } if (sharedDocs instanceof Doc) { Doc.GetProto(sharedDocs).userColor = sharedDocs.userColor || "rgb(202, 202, 202)"; diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index d283510b7..a8972b988 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, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc"; +import { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt, AclSelfEdit } from "../../fields/Doc"; import { List } from "../../fields/List"; import { Cast, NumCast, StrCast } from "../../fields/Types"; import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from "../../fields/util"; @@ -85,7 +85,8 @@ export class SharingManager extends React.Component<{}> { private AclMap = new Map([ [AclPrivate, SharingPermissions.None], [AclReadonly, SharingPermissions.View], - [AclAddonly, SharingPermissions.Add], + [AclAugment, SharingPermissions.Augment], + [AclSelfEdit, SharingPermissions.SelfEdit], [AclEdit, SharingPermissions.Edit], [AclAdmin, SharingPermissions.Admin] ]); @@ -101,7 +102,7 @@ export class SharingManager extends React.Component<{}> { this.targetDoc = target_doc || target?.props.Document; DictationOverlay.Instance.hasActiveModal = true; this.isOpen = this.targetDoc !== undefined; - this.permissions = SharingPermissions.Add; + this.permissions = SharingPermissions.Augment; }); } @@ -366,10 +367,10 @@ export class SharingManager extends React.Component<{}> { const dropdownValues: string[] = Object.values(SharingPermissions); if (!uniform) dropdownValues.unshift("-multiple-"); if (override) dropdownValues.unshift("None"); - return dropdownValues.filter(permission => permission !== SharingPermissions.View).map(permission => + return dropdownValues.filter(permission => !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission => ( ) ); @@ -546,7 +547,7 @@ export class SharingManager extends React.Component<{}> { ) : (
- {permissions === SharingPermissions.Add ? "Can Augment" : permissions} + {permissions}
)}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index fc36c7e43..99c695a4a 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -1,4 +1,4 @@ -import { Doc, Opt, DataSym, AclReadonly, AclAddonly, AclPrivate, AclEdit, AclSym, DocListCastAsync, DocListCast, AclAdmin } from '../../fields/Doc'; +import { Doc, Opt, DataSym, AclReadonly, AclAugment, AclPrivate, AclEdit, AclSym, DocListCast, AclAdmin, AclSelfEdit } from '../../fields/Doc'; import { Touchable } from './Touchable'; import { computed, action, observable } from 'mobx'; import { Cast, BoolCast, ScriptCast } from '../../fields/Types'; @@ -131,7 +131,7 @@ export function ViewBoxAnnotatableComponent

effectiveAcl === AclEdit || effectiveAcl === AclAdmin || GetEffectiveAcl(doc) === AclAdmin); + const docs = indocs.filter(doc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(doc) === AclAdmin); if (docs.length) { setTimeout(() => docs.map(doc => { // this allows 'addDocument' to see the annotationOn field in order to create a pushin Doc.SetInPlace(doc, "isPushpin", undefined, true); @@ -199,7 +199,7 @@ export function ViewBoxAnnotatableComponent

{ if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); doc.context = this.props.Document; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 005e46836..8f37172a0 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -107,7 +107,7 @@ export class MainView extends React.Component { new InkStrokeProperties(); this._sidebarContent.proto = undefined; 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.setPlaygroundFields(["dataTransition", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition", "panX", "panY", "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "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))); diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 805cda95c..a3a3bce56 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -1,6 +1,6 @@ import { action, observable, ObservableMap, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../fields/Doc"; +import { AclAugment, AclAdmin, AclEdit, DataSym, Doc, Opt, AclSelfEdit } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { List } from "../../fields/List"; import { NumCast } from "../../fields/Types"; @@ -156,7 +156,7 @@ export class MarqueeAnnotator extends React.Component { highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap) => { // creates annotation documents for current highlights const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]); - const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations); + const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations); !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc); return annotationDoc as Doc ?? undefined; } diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 8136edf04..de437e1df 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -5,7 +5,7 @@ import { intersection } from "lodash"; import { action, autorun, computed, Lambda, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { ColorState, SketchPicker } from "react-color"; -import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, Opt, WidthSym } from "../../fields/Doc"; +import { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, Opt, WidthSym, AclSelfEdit } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { InkField } from "../../fields/InkField"; import { ComputedField } from "../../fields/ScriptField"; @@ -342,12 +342,9 @@ export class PropertiesView extends React.Component { return ; } @@ -402,7 +399,8 @@ export class PropertiesView extends React.Component { [AclUnset, "None"], [AclPrivate, SharingPermissions.None], [AclReadonly, SharingPermissions.View], - [AclAddonly, SharingPermissions.Add], + [AclAugment, SharingPermissions.Augment], + [AclSelfEdit, SharingPermissions.SelfEdit], [AclEdit, SharingPermissions.Edit], [AclAdmin, SharingPermissions.Admin] ]); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 846d28214..19da7ea00 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -1,6 +1,6 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../../../fields/Doc"; +import { AclAugment, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../../../fields/Doc"; import { Id } from "../../../../fields/FieldSymbols"; import { InkData, InkField, InkTool } from "../../../../fields/InkField"; import { List } from "../../../../fields/List"; @@ -298,7 +298,7 @@ export class MarqueeView extends React.Component json?.indexOf("\"storedMarks\"") === -1 ? json?.replace(/"selection":.*/, "") : json?.replace(/"selection":"\"storedMarks\""/, "\"storedMarks\""); - if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { + if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin || effectiveAcl === AclSelfEdit) { const accumTags = [] as string[]; state.tr.doc.nodesBetween(0, state.doc.content.size, (node: any, pos: number, parent: any) => { if (node.type === schema.nodes.dashField && node.attrs.fieldKey.startsWith("#")) { @@ -1401,6 +1401,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._rules!.EnteringStyle = false; } e.stopPropagation(); + for (var i = state.selection.from; i < state.selection.to; i++) { + const node = state.doc.resolve(i); + if (node?.marks?.().some(mark => mark.type === schema.marks.user_mark && + mark.attrs.userid !== Doc.CurrentUserEmail) && + [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) { + e.preventDefault(); + } + } switch (e.key) { case "Escape": this._editorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from))); diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index d5c77786c..1f78b2204 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -7,13 +7,14 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list"; import { EditorState, Transaction, TextSelection } from "prosemirror-state"; import { SelectionManager } from "../../../util/SelectionManager"; import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types"; -import { Doc, DataSym, DocListCast } from "../../../../fields/Doc"; +import { Doc, DataSym, DocListCast, AclAugment } from "../../../../fields/Doc"; import { FormattedTextBox } from "./FormattedTextBox"; import { Id } from "../../../../fields/FieldSymbols"; import { Docs } from "../../../documents/Documents"; import { Utils } from "../../../../Utils"; import { listSpec } from "../../../../fields/Schema"; import { List } from "../../../../fields/List"; +import { GetEffectiveAcl } from "../../../../fields/util"; const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false; @@ -70,25 +71,39 @@ export function buildKeymap>(schema: S, props: any, mapKey return false; }; + const canEdit = (state: any) => { + for (var i = state.selection.from; i < state.selection.to; i++) { + const node = state.doc.resolve(i); + if (node?.marks?.().some((mark: any) => mark.type === schema.marks.user_mark && + mark.attrs.userid !== Doc.CurrentUserEmail) && + GetEffectiveAcl(props.Document) === AclAugment) { + return false; + } + } + return true; + } + + const toggleEditableMark = (mark: any) => (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && toggleMark(mark)(state, dispatch); + //History commands bind("Mod-z", undo); bind("Shift-Mod-z", redo); !mac && bind("Mod-y", redo); //Commands to modify Mark - bind("Mod-b", toggleMark(schema.marks.strong)); - bind("Mod-B", toggleMark(schema.marks.strong)); + bind("Mod-b", toggleEditableMark(schema.marks.strong)); + bind("Mod-B", toggleEditableMark(schema.marks.strong)); - bind("Mod-e", toggleMark(schema.marks.em)); - bind("Mod-E", toggleMark(schema.marks.em)); + bind("Mod-e", toggleEditableMark(schema.marks.em)); + bind("Mod-E", toggleEditableMark(schema.marks.em)); - bind("Mod-*", toggleMark(schema.marks.code)); + bind("Mod-*", toggleEditableMark(schema.marks.code)); - bind("Mod-u", toggleMark(schema.marks.underline)); - bind("Mod-U", toggleMark(schema.marks.underline)); + bind("Mod-u", toggleEditableMark(schema.marks.underline)); + bind("Mod-U", toggleEditableMark(schema.marks.underline)); //Commands for lists - bind("Ctrl-i", wrapInList(schema.nodes.ordered_list)); + bind("Ctrl-i", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state, dispatch as any)); bind("Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { /// bcz; Argh!! replace layotuTEmpalteString with a onTab prop conditionally handles Tab); @@ -96,6 +111,7 @@ export function buildKeymap>(schema: S, props: any, mapKey if (!props.LayoutTemplateString) return addTextBox(false, true); return true; } + if (!canEdit(state)) return true; const ref = state.selection; const range = ref.$from.blockRange(ref.$to); const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); @@ -121,6 +137,7 @@ export function buildKeymap>(schema: S, props: any, mapKey bind("Shift-Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { /// bcz; Argh!! replace with a onShiftTab prop conditionally handles Tab); if (props.Document._singleLine) return true; + if (!canEdit(state)) return true; const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); if (!liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => { @@ -140,24 +157,19 @@ export function buildKeymap>(schema: S, props: any, mapKey }); //Commands to modify BlockType - bind("Ctrl->", wrapIn(schema.nodes.blockquote)); - bind("Alt-\\", setBlockType(schema.nodes.paragraph)); - bind("Shift-Ctrl-\\", setBlockType(schema.nodes.code_block)); + bind("Ctrl->", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit((state) && wrapIn(schema.nodes.blockquote)(state, dispatch as any))); + bind("Alt-\\", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state, dispatch as any)); + bind("Shift-Ctrl-\\", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state, dispatch as any)); - bind("Ctrl-m", (state: EditorState, dispatch: (tx: Transaction) => void) => { - dispatch(state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: "math" + Utils.GenerateGuid() }))); - }); + bind("Ctrl-m", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: "math" + Utils.GenerateGuid() })))); for (let i = 1; i <= 6; i++) { - bind("Shift-Ctrl-" + i, setBlockType(schema.nodes.heading, { level: i })); + bind("Shift-Ctrl-" + i, (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state, dispatch as any)); } //Command to create a horizontal break line const hr = schema.nodes.horizontal_rule; - bind("Mod-_", (state: EditorState, dispatch: (tx: Transaction) => void) => { - dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView()); - return true; - }); + bind("Mod-_", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView())); //Command to unselect all bind("Escape", (state: EditorState, dispatch: (tx: Transaction) => void) => { @@ -173,13 +185,15 @@ export function buildKeymap>(schema: S, props: any, mapKey }; //Command to create a text document to the right of the selected textbox - bind("Alt-Enter", (state: EditorState, dispatch: (tx: Transaction>) => void) => addTextBox(false, true)); + bind("Alt-Enter", () => addTextBox(false, true)); //Command to create a text document to the bottom of the selected textbox - bind("Ctrl-Enter", (state: EditorState, dispatch: (tx: Transaction) => void) => addTextBox(true, true)); + bind("Ctrl-Enter", () => addTextBox(true, true)); // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward); bind("Backspace", (state: EditorState, dispatch: (tx: Transaction>) => void) => { + if (!canEdit(state)) return true; + if (!deleteSelection(state, (tx: Transaction>) => { dispatch(updateBullets(tx, schema)); })) { @@ -200,6 +214,9 @@ export function buildKeymap>(schema: S, props: any, mapKey //command to break line bind("Enter", (state: EditorState, dispatch: (tx: Transaction>) => void) => { if (addTextBox(true, false)) return true; + + if (!canEdit(state)) return true; + const trange = state.selection.$from.blockRange(state.selection.$to); const path = (state.selection.$from as any).path; const depth = trange ? liftTarget(trange) : undefined; @@ -238,18 +255,19 @@ export function buildKeymap>(schema: S, props: any, mapKey //Command to create a blank space bind("Space", (state: EditorState, dispatch: (tx: Transaction) => void) => { + if (!canEdit(state)) return true; const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); dispatch(splitMetadata(marks, state.tr)); return false; }); - bind("Alt-ArrowUp", joinUp); - bind("Alt-ArrowDown", joinDown); - bind("Mod-BracketLeft", lift); + bind("Alt-ArrowUp", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && joinUp(state, dispatch as any)); + bind("Alt-ArrowDown", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && joinDown(state, dispatch as any)); + bind("Mod-BracketLeft", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && lift(state, dispatch as any)); const cmd = chainCommands(exitCode, (state, dispatch) => { if (dispatch) { - dispatch(state.tr.replaceSelectionWith(schema.nodes.hard_break.create()).scrollIntoView()); + canEdit(state) && dispatch(state.tr.replaceSelectionWith(schema.nodes.hard_break.create()).scrollIntoView()); return true; } return false; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 0cbfaf067..85ea3cfa9 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -90,7 +90,8 @@ export const DirectLinksSym = Symbol("DirectLinks"); export const AclUnset = Symbol("AclUnset"); export const AclPrivate = Symbol("AclOwnerOnly"); export const AclReadonly = Symbol("AclReadOnly"); -export const AclAddonly = Symbol("AclAddonly"); +export const AclAugment = Symbol("AclAugment"); +export const AclSelfEdit = Symbol("AclSelfEdit"); export const AclEdit = Symbol("AclEdit"); export const AclAdmin = Symbol("AclAdmin"); export const UpdatingFromServer = Symbol("UpdatingFromServer"); @@ -102,7 +103,8 @@ const AclMap = new Map([ ["None", AclUnset], [SharingPermissions.None, AclPrivate], [SharingPermissions.View, AclReadonly], - [SharingPermissions.Add, AclAddonly], + [SharingPermissions.Augment, AclAugment], + [SharingPermissions.SelfEdit, AclSelfEdit], [SharingPermissions.Edit, AclEdit], [SharingPermissions.Admin, AclAdmin] ]); diff --git a/src/fields/util.ts b/src/fields/util.ts index 526e5af72..2bb6b45c2 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, Initializing } from "./Doc"; +import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAugment, AclSym, DataSym, DocListCast, AclAdmin, HeightSym, WidthSym, updateCachedAcls, AclUnset, DocListCastAsync, ForceServerWrite, Initializing, AclSelfEdit } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; @@ -14,6 +14,7 @@ import CursorField from "./CursorField"; import { List } from "./List"; import { SnappingManager } from "../client/util/SnappingManager"; import { computedFn } from "mobx-utils"; +import { RichTextField } from "./RichTextField"; function _readOnlySetter(): never { throw new Error("Documents can't be modified in read-only mode"); @@ -77,7 +78,9 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number const fromServer = target[UpdatingFromServer]; const sameAuthor = fromServer || (receiver.author === Doc.CurrentUserEmail); const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (writeMode !== DocServer.WriteMode.LiveReadonly); - const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode === DocServer.WriteMode.Default) && !DocServer.Control.isReadOnly();// && !playgroundMode; + const writeToServer = + (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && (value instanceof RichTextField))) && + !DocServer.Control.isReadOnly(); if (writeToDoc) { if (value === undefined) { @@ -157,9 +160,10 @@ export function inheritParentAcls(parent: Doc, child: Doc) { */ export enum SharingPermissions { Admin = "Admin", - Edit = "Can Edit", - Add = "Can Augment", - View = "Can View", + Edit = "Edit", + SelfEdit = "Self Edit", + Augment = "Augment", + View = "View", None = "Not Shared" } @@ -176,7 +180,7 @@ export function GetEffectiveAcl(target: any, user?: string): symbol { function getPropAcl(target: any, prop: string | symbol | number) { 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 + if (prop && DocServer.IsPlaygroundField(prop.toString())) return AclEdit; // playground props are always editable return GetEffectiveAcl(target); } @@ -192,7 +196,8 @@ function getEffectiveAcl(target: any, user?: string): symbol { HierarchyMapping = HierarchyMapping || new Map([ [AclPrivate, 0], [AclReadonly, 1], - [AclAddonly, 2], + [AclAugment, 2], + [AclSelfEdit, 2.5], [AclEdit, 3], [AclAdmin, 4] ]); @@ -235,6 +240,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc ["Not Shared", 0], ["Can View", 1], ["Can Augment", 2], + ["Self Edit", 2.5], ["Can Edit", 3], ["Admin", 4] ]); @@ -294,7 +300,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean { let prop = in_prop; const effectiveAcl = getPropAcl(target, prop); - if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin) return true; + if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) return true; // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't if (typeof prop === "string" && prop.startsWith("acl") && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined, "None"].includes(value))) return true; // if (typeof prop === "string" && prop.startsWith("acl") && !["Can Edit", "Can Augment", "Can View", "Not Shared", undefined].includes(value)) return true; -- cgit v1.2.3-70-g09d2 From 88074b6b085b0b5542759cb41519de719ee61cee Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 10 Aug 2021 21:34:59 -0400 Subject: fixed error when adding to a collection that does not have a list as its data --- src/client/views/DocComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/DocComponent.tsx') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 99c695a4a..14d32ef12 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -219,7 +219,7 @@ export function ViewBoxAnnotatableComponent

; - if (annoDocs) annoDocs.push(...added); + if (annoDocs instanceof List) annoDocs.push(...added); else targetDataDoc[annotationKey ?? this.annotationKey] = new List(added); targetDataDoc[(annotationKey ?? this.annotationKey) + "-lastModified"] = new DateField(new Date(Date.now())); } -- cgit v1.2.3-70-g09d2 From 707038795f7129d30a1d4ed5f2311f18a282fecb Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 24 Aug 2021 02:30:06 -0400 Subject: fixed following link from ink stroke. made interacting with annotatable documents consistent when selections are cleared by clicking. fixed undo for webboxes. fixed limiting size of links button. --- src/client/documents/Documents.ts | 1 + src/client/views/DocComponent.tsx | 10 +++--- src/client/views/MarqueeAnnotator.tsx | 16 ++++----- src/client/views/PreviewCursor.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 10 ++++-- .../collections/collectionFreeForm/MarqueeView.tsx | 15 +++++--- src/client/views/nodes/DocumentView.tsx | 6 ++-- src/client/views/nodes/ImageBox.tsx | 25 ++++++++------ src/client/views/nodes/ScreenshotBox.tsx | 3 +- src/client/views/nodes/VideoBox.tsx | 19 +++++----- src/client/views/nodes/WebBox.tsx | 40 +++++++++------------- src/client/views/pdf/PDFViewer.tsx | 14 ++++---- 12 files changed, 82 insertions(+), 79 deletions(-) (limited to 'src/client/views/DocComponent.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 48886aa3b..f8e9e8702 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -703,6 +703,7 @@ export namespace Docs { I.data = new InkField(points); I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; I["acl-Override"] = "None"; + I.links = ComputedField.MakeFunction("links(self)") as any; I[Initializing] = false; return I; } diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 14d32ef12..d9cc29bed 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -90,9 +90,9 @@ export interface ViewBoxAnnotatableProps { renderDepth: number; isAnnotationOverlay?: boolean; } -export function ViewBoxAnnotatableComponent

(schemaCtor: (doc: Doc) => T, _annotationKey: string = "annotations") { +export function ViewBoxAnnotatableComponent

(schemaCtor: (doc: Doc) => T) { class Component extends Touchable

{ - @observable _annotationKey: string = _annotationKey; + @observable _annotationKeySuffix = () => "annotations"; @observable _isAnyChildContentActive = false; //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then @@ -125,7 +125,7 @@ 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; + if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document; this.props.layerProvider?.(doc, true); Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc); }); @@ -214,7 +214,7 @@ export function ViewBoxAnnotatableComponent

{ @observable private _width: number = 0; @observable private _height: number = 0; - constructor(props: any) { - super(props); - runInAction(() => { - AnchorMenu.Instance.Status = "marquee"; - AnchorMenu.Instance.fadeOut(true); - // clear out old marquees and initialize menu for new selection - Array.from(this.props.savedAnnotations.values()).forEach(v => v.forEach(a => a.remove())); - this.props.savedAnnotations.clear(); - }); + @action + static clearAnnotations(savedAnnotations: ObservableMap) { + AnchorMenu.Instance.Status = "marquee"; + AnchorMenu.Instance.fadeOut(true); + // clear out old marquees and initialize menu for new selection + Array.from(savedAnnotations.values()).forEach(v => v.forEach(a => a.remove())); + savedAnnotations.clear(); } @action componentDidMount() { diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 679a4b81e..2b82ef475 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -158,7 +158,7 @@ export class PreviewCursor extends React.Component<{}> { } render() { return (!PreviewCursor._clickPoint || !PreviewCursor.Visible) ? (null) : -

e && e.focus()} +
e?.focus()} style={{ transform: `translate(${PreviewCursor._clickPoint[0]}px, ${PreviewCursor._clickPoint[1]}px)` }}> I
; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index a82128e47..a821aeeea 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -36,6 +36,7 @@ import { CollectionTimeView } from './CollectionTimeView'; import { CollectionTreeView } from "./CollectionTreeView"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import './CollectionView.scss'; +import { returnEmptyString } from '../../../Utils'; export const COLLECTION_BORDER_WIDTH = 2; const path = require('path'); @@ -62,7 +63,7 @@ export enum CollectionViewType { export interface CollectionViewProps extends FieldViewProps { isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc) layoutEngine?: () => string; - setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; + setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void; // property overrides for child documents children?: never | (() => JSX.Element[]) | React.ReactNode; @@ -84,7 +85,7 @@ export interface CollectionViewProps extends FieldViewProps { type CollectionDocument = makeInterface<[typeof documentSchema]>; const CollectionDocument = makeInterface(documentSchema); @observer -export class CollectionView extends ViewBoxAnnotatableComponent(CollectionDocument, "") { +export class CollectionView extends ViewBoxAnnotatableComponent(CollectionDocument) { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); } @observable private static _safeMode = false; @@ -92,6 +93,11 @@ export class CollectionView extends ViewBoxAnnotatableComponent this._annotationKeySuffix = returnEmptyString); + } + get collectionViewType(): CollectionViewType | undefined { const viewField = StrCast(this.layoutDoc._viewType); if (CollectionView._safeMode) { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 437a3cba8..81f6307d1 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -41,7 +41,7 @@ interface MarqueeViewProps { trySelectCluster: (addToSel: boolean) => boolean; nudge?: (x: number, y: number, nudgeTime?: number) => boolean; ungroup?: () => void; - setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; + setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void; } @observer export class MarqueeView extends React.Component @@ -211,7 +211,7 @@ export class MarqueeView extends React.Component { - if (drag) { + setPreviewCursor = action((x: number, y: number, drag: boolean, hide: boolean) => { + if (hide) { + this._downX = this._lastX = x; + this._downY = this._lastY = y; + this._commandExecuted = false; + PreviewCursor.Visible = false; + } else if (drag) { this._downX = this._lastX = x; this._downY = this._lastY = y; this._commandExecuted = false; @@ -313,7 +318,7 @@ export class MarqueeView extends React.Component this.onClickHandler; setHeight = (height: number) => this.layoutDoc._height = height; setContentView = (view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view; - isContentActive = (outsideReaction?: boolean) => this.props.isContentActive() ? true : false; + isContentActive = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || this.props.isContentActive() ? true : false; @computed get contents() { TraceMobx(); const audioView = !this.layoutDoc._showAudio ? (null) : @@ -792,7 +792,7 @@ export class DocumentViewInternal extends DocComponent; return
{this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints} {this.hideLinkButton ? (null) : -
+
} diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 2c0106960..c4e74ebd2 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,19 +1,21 @@ -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx'; import { observer } from "mobx-react"; import { DataSym, Doc, DocListCast, WidthSym } from '../../../fields/Doc'; import { documentSchema } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; +import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { createSchema, makeInterface } from '../../../fields/Schema'; import { ComputedField } from '../../../fields/ScriptField'; -import { Cast, NumCast, StrCast } from '../../../fields/Types'; +import { Cast, NumCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, OmitKeys, returnOne, Utils, returnFalse } from '../../../Utils'; +import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils'; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices'; import { Networking } from '../../Network'; +import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from "../../views/ContextMenu"; @@ -21,15 +23,13 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; +import { AnchorMenu } from '../pdf/AnchorMenu'; import { StyleProp } from '../StyleProvider'; 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'; -import { AnchorMenu } from '../pdf/AnchorMenu'; -import { Docs } from '../../documents/Documents'; +import { SnappingManager } from '../../util/SnappingManager'; const path = require('path'); export const pageSchema = createSchema({ @@ -294,7 +294,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent {fadepath === srcpath ? (null) :
; } - @action marqueeDown = (e: React.PointerEvent) => { 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]; - e.stopPropagation(); + setupMoveUpEvents(this, e, action(e => { + MarqueeAnnotator.clearAnnotations(this._savedAnnotations) + this._marqueeing = [e.clientX, e.clientY]; + return true; + }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false); } } @action diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index f0db0b594..7ad96bf05 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -175,8 +175,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent { this._videoRef = r; setTimeout(() => { diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index fc97a6f4d..d4df30b48 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -9,7 +9,7 @@ import { InkTool } from "../../../fields/InkField"; import { makeInterface } from "../../../fields/Schema"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { AudioField, nullAudio, VideoField } from "../../../fields/URLField"; -import { emptyFunction, formatTime, OmitKeys, returnOne, setupMoveUpEvents, Utils } from "../../../Utils"; +import { emptyFunction, formatTime, OmitKeys, returnOne, setupMoveUpEvents, Utils, returnFalse } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { Networking } from "../../Network"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; @@ -209,11 +209,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent this.props.isSelected(), - selected => !selected && setTimeout(() => { - Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove())); - this._savedAnnotations.clear(); - })); this._disposers.triggerVideo = reaction( () => !LinkDocPreview.LinkInfo && this.props.renderDepth !== -1 ? NumCast(this.Document._triggerVideo, null) : undefined, time => time !== undefined && setTimeout(() => { @@ -550,9 +545,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent; } - marqueeDown = action((e: React.PointerEvent) => { - 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]; - }); + marqueeDown = (e: React.PointerEvent) => { + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + setupMoveUpEvents(this, e, action(e => { + MarqueeAnnotator.clearAnnotations(this._savedAnnotations); + this._marqueeing = [e.clientX, e.clientY]; + return true; + }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false); + } + } finishMarquee = action(() => { this._marqueeing = undefined; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 2ff41c73a..0c32a30db 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -44,7 +44,7 @@ const WebDocument = makeInterface(documentSchema); export class WebBox extends ViewBoxAnnotatableComponent(WebDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } public static openSidebarWidth = 250; - private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); + private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void); private _mainCont: React.RefObject = React.createRef(); private _outerRef: React.RefObject = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; @@ -52,16 +52,16 @@ export class WebBox extends ViewBoxAnnotatableComponent(); private _initialScroll: Opt; private _sidebarRef = React.createRef(); - @observable private _urlHash: string = ""; @observable private _scrollTimer: any; @observable private _overlayAnnoInfo: Opt; @observable private _marqueeing: number[] | undefined; - @observable private _url: string = "hello"; @observable private _isAnnotating = false; @observable private _iframeClick: HTMLIFrameElement | undefined = undefined; @observable private _iframe: HTMLIFrameElement | null = null; @observable private _savedAnnotations = new ObservableMap(); @observable private _scrollHeight = 1500; + @computed get _url() { return this.webField?.toString() || ""; } + @computed get _urlHash() { return this._url ? WebBox.urlHash(this._url) + "" : ""; } @computed get scrollHeight() { return this._scrollHeight; } @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); } @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); } @@ -82,9 +82,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { - this._url = this.webField?.toString() || ""; - this._urlHash = WebBox.urlHash(this._url) + ""; - this._annotationKey = this._urlHash + "-annotations"; + this._annotationKeySuffix = () => this._urlHash + "-annotations"; // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created) this.dataDoc[this.fieldKey + "-annotations"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`); this.dataDoc[this.fieldKey + "-sidebar"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`); @@ -214,6 +212,8 @@ export class WebBox extends ViewBoxAnnotatableComponent([...history, this._url]); - this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = future.pop()!)); - this._urlHash = WebBox.urlHash(this._url) + ""; - this._annotationKey = this._urlHash + "-annotations"; + this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!)); return true; } return false; @@ -321,9 +319,7 @@ export class WebBox extends ViewBoxAnnotatableComponent([this._url]); else this.dataDoc[this.fieldKey + "-future"] = new List([...future, this._url]); - this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = history.pop()!)); - this._urlHash = WebBox.urlHash(this._url) + ""; - this._annotationKey = this._urlHash + "-annotations"; + this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!)); return true; } return false; @@ -342,17 +338,10 @@ export class WebBox extends ViewBoxAnnotatableComponent([url]); - } else { - this.dataDoc[this.fieldKey + "-history"] = new List([...history, url]); - } + this.dataDoc[this.fieldKey + "-history"] = new List([...(history || []), url]); this.layoutDoc._scrollTop = 0; future && (future.length = 0); } - this._url = newUrl; - this._urlHash = WebBox.urlHash(this._url) + ""; - this._annotationKey = this._urlHash + "-annotations"; if (!preview) this.dataDoc[this.fieldKey] = new WebField(new URL(newUrl)); } catch (e) { console.log("WebBox URL error:" + this._url); @@ -413,15 +402,18 @@ export class WebBox extends ViewBoxAnnotatableComponent { 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); + setupMoveUpEvents(this, e, action(e => { + MarqueeAnnotator.clearAnnotations(this._savedAnnotations); + this._marqueeing = [e.clientX, e.clientY]; + return true; + }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false); } } @action finishMarquee = (x?: number, y?: number) => { this._marqueeing = undefined; this._isAnnotating = false; this._iframeClick = undefined; - x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false); + x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false); } @computed @@ -511,7 +503,7 @@ export class WebBox extends ViewBoxAnnotatableComponent) => this._overlayAnnoInfo = anno); - setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func; + setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func; panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop)); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index ee553fd43..41a60bedf 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -69,7 +69,7 @@ export class PDFViewer extends React.Component { private _pdfViewer: any; private _styleRule: any; // stylesheet rule for making hyperlinks clickable private _retries = 0; // number of times tried to create the PDF viewer - private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); + private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void); private _annotationLayer: React.RefObject = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; private _viewer: React.RefObject = React.createRef(); @@ -371,10 +371,11 @@ export class PDFViewer extends React.Component { this._downY = e.clientY; if ((this.props.Document._viewScale || 1) !== 1) return; if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) { - this._setPreviewCursor?.(e.clientX, e.clientY, true); + this._setPreviewCursor?.(e.clientX, e.clientY, true, false); } if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { this.props.select(false); + MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; if (e.target && ((e.target as any).className.includes("endOfContent") || ((e.target as any).parentElement.className !== "textLayer"))) { this._textSelecting = false; @@ -382,10 +383,7 @@ export class PDFViewer extends React.Component { } else { // if textLayer is hit, then we select text instead of using a marquee so clear out the marquee. setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it. - // clear out old marquees and initialize menu for new selection - AnchorMenu.Instance.Status = "marquee"; - Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove())); - this._savedAnnotations.clear(); + this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, "htmlAnnotation", { "pointer-events": "none" }); document.addEventListener("pointerup", this.onSelectEnd); document.addEventListener("pointermove", this.onSelectMove); @@ -454,12 +452,12 @@ export class PDFViewer extends React.Component { if (this._setPreviewCursor && e.button === 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { - this._setPreviewCursor(e.clientX, e.clientY, false); + this._setPreviewCursor(e.clientX, e.clientY, false, false); } // e.stopPropagation(); // bcz: not sure why this was here. We need to allow the DocumentView to get clicks to process doubleClicks } - setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func; + setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func; getCoverImage = () => { if (!this.props.Document[HeightSym]() || !Doc.NativeHeight(this.props.Document)) { -- cgit v1.2.3-70-g09d2 From 2d8b3c6b73da1b7685903697525a277fd53340a5 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 26 Aug 2021 00:32:47 -0400 Subject: a lot of changes to move isContentActive into DocumentView --- src/Utils.ts | 2 +- src/client/views/DocComponent.tsx | 23 ++++++++---------- src/client/views/collections/CollectionSubView.tsx | 3 +-- src/client/views/collections/CollectionView.tsx | 8 ++++++- src/client/views/collections/TabDocView.tsx | 3 ++- .../collectionFreeForm/CollectionFreeFormView.tsx | 10 ++++---- src/client/views/nodes/AudioBox.tsx | 8 +++---- .../views/nodes/CollectionFreeFormDocumentView.tsx | 6 +++-- src/client/views/nodes/ComparisonBox.tsx | 17 ++++++++++---- src/client/views/nodes/DocumentView.tsx | 15 ++++++++---- src/client/views/nodes/ImageBox.tsx | 3 +-- src/client/views/nodes/PDFBox.tsx | 11 ++++----- src/client/views/nodes/VideoBox.tsx | 27 +++++++++++----------- src/client/views/nodes/WebBox.tsx | 12 ++++------ .../views/nodes/formattedText/FormattedTextBox.tsx | 10 ++++---- src/client/views/pdf/AnchorMenu.tsx | 1 - src/client/views/pdf/PDFViewer.tsx | 3 +-- src/fields/Doc.ts | 3 +++ 18 files changed, 91 insertions(+), 74 deletions(-) (limited to 'src/client/views/DocComponent.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index 102ac520b..de3b13f63 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -116,7 +116,7 @@ export namespace Utils { } const isTransparentFunctionHack = "isTransparent(__value__)"; - const noRecursionHack = "__noRecursion"; + export const noRecursionHack = "__noRecursion"; export function IsRecursiveFilter(val: string) { return !val.includes(noRecursionHack); } diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index d9cc29bed..33dff9da5 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -1,17 +1,17 @@ -import { Doc, Opt, DataSym, AclReadonly, AclAugment, AclPrivate, AclEdit, AclSym, DocListCast, AclAdmin, AclSelfEdit } from '../../fields/Doc'; -import { Touchable } from './Touchable'; -import { computed, action, observable } from 'mobx'; -import { Cast, BoolCast, ScriptCast } from '../../fields/Types'; +import { action, computed, observable } from 'mobx'; +import { DateField } from '../../fields/DateField'; +import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, DocListCast, Opt } from '../../fields/Doc'; import { InkTool } from '../../fields/InkField'; -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, inheritParentAcls } from '../../fields/util'; -import { CurrentUserUtils } from '../util/CurrentUserUtils'; -import { DocUtils } from '../documents/Documents'; +import { Cast, ScriptCast } from '../../fields/Types'; +import { denormalizeEmail, distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util'; import { returnFalse } from '../../Utils'; +import { DocUtils } from '../documents/Documents'; +import { CurrentUserUtils } from '../util/CurrentUserUtils'; +import { InteractionUtils } from '../util/InteractionUtils'; import { UndoManager } from '../util/UndoManager'; +import { Touchable } from './Touchable'; /// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView) @@ -107,6 +107,7 @@ export function ViewBoxAnnotatableComponent

this._isAnyChildContentActive; lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result; @@ -229,10 +230,6 @@ export function ViewBoxAnnotatableComponent

this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive)); - isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.SelectedTool !== InkTool.None || - (this.props.isContentActive?.() || this.props.Document.forceActive || - this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || - this.props.rootSelected(outsideReaction)) ? true : false) } return Component; } \ No newline at end of file diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 1f4c35daa..b70df93da 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -23,6 +23,7 @@ import ReactLoading from 'react-loading'; export interface SubCollectionViewProps extends CollectionViewProps { CollectionView: Opt; SetSubView?: (subView: any) => void; + isAnyChildContentActive: () => boolean; } export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: X) { @@ -490,7 +491,5 @@ import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTex import { CollectionView, CollectionViewType, CollectionViewProps } from "./CollectionView"; import { SelectionManager } from "../../util/SelectionManager"; import { OverlayView } from "../OverlayView"; -import { Hypothesis } from "../../util/HypothesisUtils"; import { GetEffectiveAcl, TraceMobx } from "../../../fields/util"; -import { FilterBox } from "../nodes/FilterBox"; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index a821aeeea..229e93b9e 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -37,6 +37,7 @@ import { CollectionTreeView } from "./CollectionTreeView"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import './CollectionView.scss'; import { returnEmptyString } from '../../../Utils'; +import { InkTool } from '../../../fields/InkField'; export const COLLECTION_BORDER_WIDTH = 2; const path = require('path'); @@ -119,7 +120,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent Cast(doc.data, ImageField)).map(Doc.GetProto); // const allTagged = imageProtos.length > 0 && imageProtos.every(image => image.googlePhotosTags); // return !allTagged ? (null) : ; - this.isContentActive(); + //this.isContentActive(); } screenToLocalTransform = () => this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth()); @@ -253,6 +254,9 @@ export class CollectionView extends ViewBoxAnnotatableComponent { + return this.props.isContentActive() ? true : false; + } render() { TraceMobx(); const props: SubCollectionViewProps = { @@ -262,6 +266,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent { childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this. noOverlay={true} // don't render overlay Docs since they won't scale setHeight={returnFalse} - isContentActive={returnTrue} + isContentActive={returnFalse} + isAnyChildContentActive={returnFalse} select={emptyFunction} dropAction={undefined} isSelected={returnFalse} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b5d9ebd9f..fb949a36d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,6 +1,7 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; +import { DateField } from "../../../../fields/DateField"; import { Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc"; import { collectionSchema, documentSchema } from "../../../../fields/documentSchemas"; import { Id } from "../../../../fields/FieldSymbols"; @@ -17,6 +18,7 @@ import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUp import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { DocServer } from "../../../DocServer"; import { Docs, DocUtils } from "../../../documents/Documents"; +import { DocumentType } from "../../../documents/DocumentTypes"; import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; import { DocumentManager } from "../../../util/DocumentManager"; import { DragManager, dropActionType } from "../../../util/DragManager"; @@ -48,8 +50,7 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); -import { DocumentType } from "../../../documents/DocumentTypes"; -import { DateField } from "../../../../fields/DateField"; +import Color = require("color"); export const panZoomSchema = createSchema({ _panX: "number", @@ -117,7 +118,7 @@ export class CollectionFreeFormView extends CollectionSubView ele.bounds && !ele.bounds.z).map(ele => ele.ele); } @computed get backgroundEvents() { return this.props.layerProvider?.(this.layoutDoc) === false && SnappingManager.GetIsDragging(); } - @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && (this.props.ContainingCollectionView?.isContentActive() || this.props.isContentActive()); } + @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && this.props.isContentActive(); } @computed get fitToContentVals() { return { bounds: { ...this.contentBounds, cx: (this.contentBounds.x + this.contentBounds.r) / 2, cy: (this.contentBounds.y + this.contentBounds.b) / 2 }, @@ -170,6 +171,7 @@ export class CollectionFreeFormView extends CollectionSubView this.cachedGetContainerTransform.copy(); getTransformOverlay = () => this.getContainerTransform().translate(1, 1); getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout); + isAnyChildContentActive = () => this.props.isAnyChildContentActive(); addLiveTextBox = (newBox: Doc) => { FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed this.addDocument(newBox); @@ -1033,7 +1035,7 @@ export class CollectionFreeFormView extends CollectionSubView + return ; @@ -328,6 +328,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent}

: -
+
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 092823603..9cc4b1f9a 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -17,8 +17,8 @@ import { InkingStroke } from "../InkingStroke"; import { StyleProp } from "../StyleProvider"; import "./CollectionFreeFormDocumentView.scss"; import { DocumentView, DocumentViewProps } from "./DocumentView"; -import { FieldViewProps } from "./FieldView"; import React = require("react"); +import Color = require("color"); export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined; @@ -164,6 +164,8 @@ export class CollectionFreeFormDocumentView extends DocComponent this._contentView = r)} /> diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index 153176afc..6708a08ee 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -1,17 +1,18 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, observable } from 'mobx'; import { observer } from "mobx-react"; -import { Doc } from '../../../fields/Doc'; +import { Doc, Opt } from '../../../fields/Doc'; import { documentSchema } from '../../../fields/documentSchemas'; import { createSchema, makeInterface } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; -import { emptyFunction, OmitKeys, setupMoveUpEvents } from '../../../Utils'; +import { emptyFunction, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; +import { StyleProp } from '../StyleProvider'; import "./ComparisonBox.scss"; -import { DocumentView } from './DocumentView'; +import { DocumentView, DocumentViewProps } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import React = require("react"); @@ -71,6 +72,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent, props: Opt, property: string): any => { + if (property === StyleProp.PointerEvents) return "none"; + return this.props.styleProvider?.(doc, props, property); + } + render() { const clipWidth = NumCast(this.layoutDoc._clipWidth) + "%"; const clearButton = (which: string) => { @@ -84,6 +90,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent @@ -102,7 +111,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent +
{displayBox("after", 1, this.props.PanelWidth() - 3)}
{displayBox("before", 0, 0)} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index bb259da3e..11fb5cdb3 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -84,6 +84,7 @@ export interface DocComponentView { reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. shrinkWrap?: () => void; // requests a document to display all of its contents with no white space. currently only implemented (needed?) for freeform views menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected. + isAnyChildContentActive?: () => boolean; // is any child content of the document active getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) playFrom?: (time: number, endTime?: number) => void; @@ -182,7 +183,7 @@ export class DocumentViewInternal extends DocComponent; // needs to be accessed from DocumentView wrapper class + @observable _componentView: Opt; // needs to be accessed from DocumentView wrapper class private get topMost() { return this.props.renderDepth === 0 && !LightboxView.LightboxDoc; } public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive @@ -778,8 +779,14 @@ export class DocumentViewInternal extends DocComponent this.ContentScale; onClickFunc = () => this.onClickHandler; setHeight = (height: number) => this.layoutDoc._height = height; - setContentView = (view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view; - isContentActive = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || this.props.isContentActive() ? true : false; + setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view); + isContentActive = (outsideReaction?: boolean) => { + return CurrentUserUtils.SelectedTool !== InkTool.None || + this.props.Document.forceActive || + this.props.isSelected(outsideReaction) || + this._componentView?.isAnyChildContentActive?.() || + this.props.isContentActive() ? true : false; + } @computed get contents() { TraceMobx(); const audioView = !this.layoutDoc._showAudio ? (null) : @@ -794,7 +801,7 @@ export class DocumentViewInternal extends DocComponent; return
; } marqueeDown = (e: React.PointerEvent) => { - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations) this._marqueeing = [e.clientX, e.clientY]; @@ -368,7 +368,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent; const searchTitle = `${!this._searching ? "Open" : "Close"} Search Bar`; const curPage = this.Document._curPage || 1; - return !this.isContentActive() ? (null) : + return !this.props.isContentActive() ? (null) :
[KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true} - onPointerDown={e => e.stopPropagation()} style={{ display: this.isContentActive() ? "flex" : "none" }}> + onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? "flex" : "none" }}>
e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}> @@ -229,7 +229,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent
{this.props.Document.title} @@ -268,7 +268,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent {this.settingsPanel()}
; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index d4df30b48..484dec7e2 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -307,7 +307,7 @@ export class VideoBox extends ViewBoxAnnotatableComponentLoading
: -
+
} placement="bottom"> -
+ {"playback"}
} key="play" placement="bottom"> +
, - {"timecode"}
} placement="bottom"> + {"timecode"}
} key="time" placement="bottom">
{formatTime(curTime)} {" " + Math.floor((curTime - Math.trunc(curTime)) * 100).toString().padStart(2, "0")}
, - {"view full screen"}
} placement="bottom"> + {"view full screen"}
} key="full" placement="bottom">
]; return
{[...(VideoBox._nativeControls ? [] : nonNativeControls), - {"snapshot current frame"}
} placement="bottom"> + {"snapshot current frame"}
} key="snap" placement="bottom">
, - {"show annotation timeline"}
} placement="bottom"> + {"show annotation timeline"}
} key="timeline" placement="bottom">
@@ -429,7 +429,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { this._clicking = false; - if (this.isContentActive()) { + if (this.props.isContentActive()) { const local = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformPoint(e.clientX, e.clientY); this.layoutDoc._timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100)); } @@ -438,7 +438,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { this.layoutDoc._timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent; setTimeout(action(() => this._clicking = false), 500); - }, this.isContentActive(), this.isContentActive()); + }, this.props.isContentActive(), this.props.isContentActive()); }); onResetDown = (e: React.PointerEvent) => { @@ -529,12 +529,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent @@ -546,7 +546,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; @@ -570,7 +570,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent (this.props.scaling?.() || 1) * this.heightPercent / 100; marqueeOffset = () => [this.panelWidth() / 2 * (1 - this.heightPercent / 100) / (this.heightPercent / 100), 0]; - timelineDocFilter = () => ["_timelineLabel:true:x"]; + timelineDocFilter = () => [`_timelineLabel:true,${Utils.noRecursionHack}:x`]; render() { const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding); const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad; @@ -592,7 +592,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; @@ -484,7 +484,7 @@ export class WebBox extends ViewBoxAnnotatableComponent {this.urlContent}
; @@ -529,7 +529,6 @@ export class WebBox extends ViewBoxAnnotatableComponent; return ( -
+
{renderAnnotations(this.opaqueFilter)} - {renderAnnotations()} + {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()} {this.annotationLayer}
@@ -588,10 +587,9 @@ export class WebBox extends ViewBoxAnnotatableComponent diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index d1027dfd7..4b1d76d00 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1211,7 +1211,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if ((e.nativeEvent as any).formattedHandled) { console.log("handled"); } - if (!(e.nativeEvent as any).formattedHandled && this.isContentActive(true)) { + if (!(e.nativeEvent as any).formattedHandled && this.props.isContentActive(true)) { const editor = this._editorView!; const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY }); !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0)))); @@ -1481,7 +1481,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @computed get sidebarHandle() { TraceMobx(); const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length; - return (!annotated && !this.isContentActive()) ? (null) :
: = 10 ? "-selected" : ""; return (
this.isContentActive() && e.stopPropagation()} + onWheel={e => this.props.isContentActive() && e.stopPropagation()} style={{ transform: this.props.dontScale ? undefined : `scale(${scale})`, transformOrigin: this.props.dontScale ? undefined : "top left", diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 75e3f81fb..42bec38da 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -69,7 +69,6 @@ export class AnchorMenu extends AntimodeMenu { this._disposer = reaction(() => SelectionManager.Views(), selected => { this._showLinkPopup = false; - console.log("unmount"); AnchorMenu.Instance.fadeOut(true) }); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 734d9127c..bc35d2126 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -512,7 +512,6 @@ export class PDFViewer extends React.Component { const renderAnnotations = (docFilters?: () => string[]) => { transform: `scale(${this._zoomed})` }}> {renderAnnotations(this.opaqueFilter)} - {renderAnnotations()} + {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()}
; } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 17f41fac8..b09ff93d0 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1092,6 +1092,9 @@ export namespace Doc { const isTransparent = (color: string) => color !== "" && (Color(color).alpha() !== 1); return isTransparent(StrCast(doc[key])); } + if (typeof value === "string") { + value = value.replace(`,${Utils.noRecursionHack}`, ""); + } const fieldVal = doc[key]; if (Cast(fieldVal, listSpec("string"), []).length) { const vals = Cast(fieldVal, listSpec("string"), []); -- cgit v1.2.3-70-g09d2