From c150d0afa46c2c45e413a5269d26bc46b64ba765 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 8 Oct 2020 23:21:04 -0400
Subject: fixed dragging bottom of some pdfs not to grow wildly.  stopped
 writing renderContentBounds to annotation views.  fixed texts sidebar notes
 to not add extra chars on creation.

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5236fab27..796765a8d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1441,7 +1441,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     render() {
         TraceMobx();
         const clientRect = this._mainCont?.getBoundingClientRect();
-        !this.fitToContent && this._layoutElements?.length && setTimeout(() => this.Document._renderContentBounds = new List<number>([this.contentBounds.x, this.contentBounds.y, this.contentBounds.r, this.contentBounds.b]), 0);
+        !this.fitToContent && !this.props.annotationsKey && this._layoutElements?.length && setTimeout(() => this.Document._renderContentBounds = new List<number>([this.contentBounds.x, this.contentBounds.y, this.contentBounds.r, this.contentBounds.b]), 0);
         return <div className={"collectionfreeformview-container"} ref={this.createDashEventsTarget}
             onPointerOver={this.onPointerOver}
             onWheel={this.onPointerWheel}
-- 
cgit v1.2.3-70-g09d2


From c03949832eaff9c29fe48f8ec702362810c7f3b6 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 10 Oct 2020 17:56:34 -0400
Subject: fixed scrolling down to annotations that have no height (but really
 do -- eg pdf selections) to at least show 50 pixels.

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 796765a8d..d48961849 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -896,7 +896,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 const curScroll = NumCast(this.props.Document._scrollTop);
                 let scrollTo = curScroll;
                 if (curScroll + contextHgt < NumCast(doc.y)) {
-                    scrollTo = NumCast(doc.y) + NumCast(doc._height) - contextHgt;
+                    scrollTo = NumCast(doc.y) + Math.max(NumCast(doc._height), 50) - contextHgt;
                 } else if (curScroll > NumCast(doc.y)) {
                     scrollTo = NumCast(doc.y);
                 }
-- 
cgit v1.2.3-70-g09d2


From 3256d0f42401b8dd8745e7f32c25033e118b444b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 11 Oct 2020 17:22:03 -0400
Subject: removed setting fields to undefined when Acl is Private in hopes of
 not losing data when permissions are changed. prevented server writes on
 startup by making renderContentBounds setting a reaction, and making
 linearView not resize if documents are still promises and make text boxes not
 flip-flop their height all the time.

---
 src/client/views/collections/CollectionLinearView.tsx     |  4 ++--
 .../collectionFreeForm/CollectionFreeFormView.tsx         | 15 +++++++++++++--
 src/client/views/nodes/formattedText/FormattedTextBox.tsx |  3 +--
 src/fields/Doc.ts                                         | 10 +++++-----
 4 files changed, 21 insertions(+), 11 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 859ee9362..0eac5136a 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -38,8 +38,8 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
 
     componentDidMount() {
         // is there any reason this needs to exist? -syip.  yes, it handles autoHeight for stacking views (masonry isn't yet supported).
-        this._widthDisposer = reaction(() => this.props.Document[HeightSym]() + this.childDocs.length + (this.props.Document.linearViewIsExpanded ? 1 : 0),
-            () => this.props.Document._width = 5 + (this.props.Document.linearViewIsExpanded ? this.childDocs.length * (this.props.Document[HeightSym]()) : 10),
+        this._widthDisposer = reaction(() => 5 + (this.props.Document.linearViewIsExpanded ? this.childDocs.length * (this.props.Document[HeightSym]()) : 10),
+            width => this.childDocs.length && (this.props.Document._width = width),
             { fireImmediately: true }
         );
 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d48961849..4d9906f93 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -7,7 +7,7 @@ import { Id } from "../../../../fields/FieldSymbols";
 import { InkData, InkField, InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { RichTextField } from "../../../../fields/RichTextField";
-import { createSchema, makeInterface } from "../../../../fields/Schema";
+import { createSchema, makeInterface, listSpec } from "../../../../fields/Schema";
 import { ScriptField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
 import { TraceMobx } from "../../../../fields/util";
@@ -88,6 +88,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     private _clusterDistance: number = 75;
     private _hitCluster = false;
     private _layoutComputeReaction: IReactionDisposer | undefined;
+    private _boundsReaction: IReactionDisposer | undefined;
     private _layoutPoolData = new ObservableMap<string, PoolData>();
     private _layoutSizeData = new ObservableMap<string, { width?: number, height?: number }>();
     private _cachedPool: Map<string, PoolData> = new Map();
@@ -1159,12 +1160,22 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this._layoutComputeReaction = reaction(() => this.doLayoutComputation,
             (elements) => this._layoutElements = elements || [],
             { fireImmediately: true, name: "doLayout" });
+        if (!this.props.annotationsKey) {
+            this._boundsReaction = reaction(() => this.contentBounds,
+                bounds => (!this.fitToContent && this._layoutElements?.length) && setTimeout(() => {
+                    const rbounds = Cast(this.Document._renderContentBounds, listSpec("number"), [0, 0, 0, 0]);
+                    if (rbounds[0] !== bounds.x || rbounds[1] !== bounds.y || rbounds[2] !== bounds.r || rbounds[3] !== bounds.b) {
+                        this.Document._renderContentBounds = new List<number>([bounds.x, bounds.y, bounds.r, bounds.b]);
+                    }
+                }));
+        }
 
         this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
     }
 
     componentWillUnmount() {
         this._layoutComputeReaction?.();
+        this._boundsReaction?.();
         this._marqueeRef.current?.removeEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
     }
 
@@ -1438,10 +1449,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         return wscale < hscale ? wscale : hscale;
     }
     @computed get backgroundEvents() { return this.layoutDoc._isBackground && SnappingManager.GetIsDragging(); }
+
     render() {
         TraceMobx();
         const clientRect = this._mainCont?.getBoundingClientRect();
-        !this.fitToContent && !this.props.annotationsKey && this._layoutElements?.length && setTimeout(() => this.Document._renderContentBounds = new List<number>([this.contentBounds.x, this.contentBounds.y, this.contentBounds.r, this.contentBounds.b]), 0);
         return <div className={"collectionfreeformview-container"} ref={this.createDashEventsTarget}
             onPointerOver={this.onPointerOver}
             onWheel={this.onPointerWheel}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 94a11e3ac..da797eda0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1514,7 +1514,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     @action
     tryUpdateHeight(limitHeight?: number) {
         let scrollHeight = this.ProseRef?.scrollHeight || 0;
-        this.rootDoc[this.fieldKey + "-height"] = 0;
         if (this.props.renderDepth && this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight && scrollHeight && !this.props.dontRegisterView) {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
             scrollHeight = scrollHeight * NumCast(this.layoutDoc._viewScale, 1);
             if (limitHeight && scrollHeight > limitHeight) {
@@ -1545,7 +1544,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     this.rootDoc[this.fieldKey + "-height"] = finalHeight;
                 } catch (e) { console.log("Error in tryUpdateHeight"); }
             }
-        }
+        } else this.rootDoc[this.fieldKey + "-height"] = 0;
     }
 
     @computed get audioHandle() {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 7dd894ec4..086b7777f 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -240,11 +240,11 @@ export class Doc extends RefField {
                     if (prev === AclPrivate && GetEffectiveAcl(this) !== AclPrivate) {
                         DocServer.GetRefField(this[Id], true);
                     }
-                    if (prev !== AclPrivate && GetEffectiveAcl(this) === AclPrivate) {
-                        this[UpdatingFromServer] = true;
-                        this[FieldsSym](true);
-                        this[UpdatingFromServer] = false;
-                    }
+                    // if (prev !== AclPrivate && GetEffectiveAcl(this) === AclPrivate) {
+                    //     this[UpdatingFromServer] = true;
+                    //     this[FieldsSym](true);
+                    //     this[UpdatingFromServer] = false;
+                    // }
                 };
                 if (sameAuthor || fKey.startsWith("acl") || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) {
                     delete this[CachedUpdates][fKey];
-- 
cgit v1.2.3-70-g09d2


From 27635402ad810b910557eb1a86c7e85fa281aaee Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 12 Oct 2020 01:05:13 -0400
Subject: switched fonticonbox and colelctionsubview to test GetEffectiveAcl to
 determine if document is available (instead of hack of testing 'author'). 
 made GetEffectiveAcl a computedFn.  No longer create a pushpin when an
 annotation that's linked to text is dragged off a PDF.   fixed undo of
 pushpin navigation (used to call finish() twice).  fixed pushpin navigation
 to conistenly pan & sensibly toggle target

---
 src/client/util/DocumentManager.ts                  |  9 +++++----
 src/client/views/collections/CollectionSubView.tsx  |  5 +++--
 src/client/views/collections/CollectionView.tsx     |  3 ++-
 .../collectionFreeForm/CollectionFreeFormView.tsx   | 21 +++++++++++++--------
 src/client/views/nodes/FontIconBox.tsx              |  5 +++--
 src/client/views/pdf/Annotation.tsx                 |  2 ++
 src/client/views/pdf/PDFViewer.tsx                  |  2 +-
 src/fields/util.ts                                  | 14 ++++++++++----
 8 files changed, 39 insertions(+), 22 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 178daf5f0..b37181e57 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -164,13 +164,14 @@ export class DocumentManager {
             const sameContext = annotatedDoc && annotatedDoc === originatingDoc?.context;
             if (originatingDoc?.isPushpin) {
                 const hide = !docView.props.Document.hidden;
-                (!hide || !sameContext) && (docView.props.Document.hidden = !docView.props.Document.hidden);
-                docView.props.focus(docView.props.Document, willZoom, undefined, (notfocused: boolean) => { // bcz: Argh! TODO: Need to add a notFocused argument to the after finish callback function that indicates whether the window had to scroll to show the target
-                    notfocused && hide && (docView.props.Document.hidden = true);
+                docView.props.focus(docView.props.Document, willZoom, undefined, (notfocused: boolean) => { // bcz: Argh! TODO: Need to add a notFocused argument to the after finish callback function that indicates whether the window had to scroll to show the target  
+                    if (notfocused || docView.props.Document.hidden) {
+                        docView.props.Document.hidden = !docView.props.Document.hidden;
+                    }
                     return focusAndFinish();
                     // @ts-ignore   bcz: Argh TODO: Need to add a parameter to focus() everywhere for whether focus should center the target's container in the view or not. // here we don't want to focus the container if the source and target are in the same container
                 }, sameContext);
-                finished?.();
+                //finished?.();
             }
             else {
                 docView.select(false);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 493018093..b282d1e27 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -103,7 +103,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
             const { Document, DataDoc } = this.props;
             const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.annotationsKey ? DataDoc : undefined, doc)).
                 filter(pair => {  // filter out any documents that have a proto that we don't have permissions to (which we determine by not having any keys
-                    return pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && Object.keys(pair.layout.proto).length));
+                    return pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate));// Object.keys(pair.layout.proto).length));
                 });
             return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
         }
@@ -134,7 +134,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
                 rawdocs = rootDoc && !this.props.annotationsKey ? [Doc.GetProto(rootDoc)] : [];
             }
 
-            const docs = rawdocs.filter(d => !(d instanceof Promise) && Object.keys(d).length).map(d => d as Doc);
+            const docs = rawdocs.filter(d => !(d instanceof Promise) && GetEffectiveAcl(d) !== AclPrivate).map(d => d as Doc);
             const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField);
             const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
 
@@ -502,4 +502,5 @@ import { SelectionManager } from "../../util/SelectionManager";
 import { OverlayView } from "../OverlayView";
 import { setTimeout } from "timers";
 import { Hypothesis } from "../../util/HypothesisUtils";
+import { GetEffectiveAcl } from "../../../fields/util";
 
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 80e9b41ad..cfd24545b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -159,7 +159,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                 else {
                     added.filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))).map(doc => {  // only make a pushpin if we have acl's to edit the document
                         const context = Cast(doc.context, Doc, null);
-                        if (context && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) {
+                        const hasContextAnchor = DocListCast(doc.links).some(l => (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) || (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context));
+                        if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) {
                             const pushpin = Docs.Create.FontIconDocument({
                                 title: "pushpin", label: "",
                                 icon: "map-pin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), _backgroundColor: "#0000003d", color: "#ACCEF7",
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4d9906f93..4df90e8ea 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -893,21 +893,24 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 afterFocus && setTimeout(afterFocus, delay);
             } else {
                 const contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn._height);
-                const offset = annotOn && (contextHgt / 2);
                 const curScroll = NumCast(this.props.Document._scrollTop);
                 let scrollTo = curScroll;
                 if (curScroll + contextHgt < NumCast(doc.y)) {
-                    scrollTo = NumCast(doc.y) + Math.max(NumCast(doc._height), 50) - contextHgt;
+                    scrollTo = NumCast(doc.y) + Math.max(NumCast(doc._height), 100) - contextHgt;
                 } else if (curScroll > NumCast(doc.y)) {
-                    scrollTo = NumCast(doc.y);
+                    scrollTo = Math.max(0, NumCast(doc.y) - 50);
                 }
-                if (curScroll !== scrollTo) {
+                if (curScroll !== scrollTo || this.props.Document._viewTransition) {
                     this.props.Document._scrollPY = this.props.Document._scrollY = scrollTo;
                     delay = Math.abs(scrollTo - curScroll) > 5 ? 1000 : 0;
-                    !dontCenter && delay && this.props.focus(this.props.Document);
+                    !dontCenter && this.props.focus(this.props.Document);
                     afterFocus && setTimeout(afterFocus, delay);
+                } else {
+                    !dontCenter && delay && this.props.focus(this.props.Document);
                     // @ts-ignore
-                } else afterFocus(true);  // bcz: TODO Aragh -- need to add a parameter to afterFocus() functions to indicate whether the focus function didn't need to scroll
+                    afterFocus(true);  // bcz: TODO Aragh -- need to add a parameter to afterFocus() functions to indicate whether the focus function didn't need to scroll
+
+                }
             }
 
         } else {
@@ -929,14 +932,16 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             willZoom && this.setScaleToZoom(layoutdoc, scale);
             Doc.linkFollowHighlight(doc);
 
+            const notFocused = newPanX === savedState.px && newPanY === savedState.py;
             afterFocus && setTimeout(() => {
-                if (afterFocus?.()) {
+                // @ts-ignore
+                if (afterFocus?.(notFocused)) { // bcz: TODO Aragh -- need to add a parameter to afterFocus() functions to indicate whether the focus function didn't need to scroll
                     this.Document._panX = savedState.px;
                     this.Document._panY = savedState.py;
                     this.Document[this.scaleFieldKey] = savedState.s;
                     this.Document._viewTransition = savedState.pt;
                 }
-            }, 500);
+            }, notFocused ? 0 : 500);
         }
 
     }
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 156256fe5..276c66bb1 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -8,11 +8,12 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { StrCast, Cast, ScriptCast } from '../../../fields/Types';
 import { Utils, setupMoveUpEvents, returnFalse, emptyFunction } from "../../../Utils";
 import { runInAction, observable, reaction, IReactionDisposer } from 'mobx';
-import { Doc, DocListCast } from '../../../fields/Doc';
+import { Doc, DocListCast, AclPrivate } from '../../../fields/Doc';
 import { ContextMenu } from '../ContextMenu';
 import { ScriptField } from '../../../fields/ScriptField';
 import { Tooltip } from '@material-ui/core';
 import { DragManager } from '../../util/DragManager';
+import { GetEffectiveAcl } from '../../../fields/util';
 const FontIconSchema = createSchema({
     icon: "string",
 });
@@ -105,7 +106,7 @@ export class FontIconBadge extends React.Component<FontIconBadgeProps> {
 
     render() {
         if (!(this.props.collection instanceof Doc)) return (null);
-        const length = DocListCast(this.props.collection.data).filter(d => Object.keys(d).length).length; // filter out any documents that we can't read
+        const length = DocListCast(this.props.collection.data).filter(d => GetEffectiveAcl(d) !== AclPrivate).length; //  Object.keys(d).length).length; // filter out any documents that we can't read
         return <div className="fontIconBadge-container" style={{ width: 15, height: 15, top: 12 }} ref={this._notifsRef}>
             <div className="fontIconBadge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}
                 onPointerDown={this.onPointerDown} >
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index e7f901091..a071abd21 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -71,6 +71,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
         this._reactionDisposer && this._reactionDisposer();
     }
 
+    @undoBatch
     deleteAnnotation = () => {
         const annotation = DocListCast(this.props.dataDoc[this.props.fieldKey + "-annotations"]);
         const group = FieldValue(Cast(this.props.document.group, Doc));
@@ -86,6 +87,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
         PDFMenu.Instance.fadeOut(true);
     }
 
+    @undoBatch
     pinToPres = () => {
         const group = FieldValue(Cast(this.props.document.group, Doc));
         const isPinned = group && Doc.isDocPinned(group) ? true : false;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 78b95b385..d8be3defd 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -194,7 +194,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
                 if (scrollY !== undefined) {
                     (this._showCover || this._showWaiting) && this.setupPdfJsViewer();
                     if (this.props.renderDepth === -1 && scrollY >= 0) {
-                        if (!this._mainCont.current) setTimeout(() => smoothScroll(1000, this._mainCont.current!, scrollY || 0));
+                        if (!this._mainCont.current) setTimeout(() => this._mainCont.current && smoothScroll(1000, this._mainCont.current, scrollY || 0));
                         else smoothScroll(1000, this._mainCont.current, scrollY || 0);
                         this.Document._scrollPY = undefined;
                     }
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 881f301f3..b68d961b1 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -13,6 +13,7 @@ import { returnZero } from "../Utils";
 import CursorField from "./CursorField";
 import { List } from "./List";
 import { SnappingManager } from "../client/util/SnappingManager";
+import { computedFn } from "mobx-utils";
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
@@ -139,7 +140,7 @@ export function denormalizeEmail(email: string) {
  * 
  * Edit: a user with edit access to a document can remove/edit that document, add/remove/edit annotations (depending on permissions), but not change any access rights to that document.
  * 
- * Add: a user with add access to a document can add documents/annotations to that document but cannot edit or delete anything.
+ * Add: a user with add access to a document can augment documents/annotations to that document but cannot edit or delete anything.
  * 
  * View: a user with view access to a document can only view it - they cannot add/remove/edit anything.
  * 
@@ -148,7 +149,7 @@ export function denormalizeEmail(email: string) {
 export enum SharingPermissions {
     Admin = "Admin",
     Edit = "Can Edit",
-    Add = "Can Add",
+    Add = "Can Augment",
     View = "Can View",
     None = "Not Shared"
 }
@@ -157,6 +158,11 @@ export enum SharingPermissions {
  * Calculates the effective access right to a document for the current user.
  */
 export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number, user?: string): symbol {
+    return computedFn(function (target: any, in_prop?: string | symbol | number, user?: string) {
+        return getEffectiveAcl(target, in_prop, user);
+    }, true)(target, in_prop, user);
+}
+function getEffectiveAcl(target: any, in_prop?: string | symbol | number, user?: string): symbol {
     if (!target) return AclPrivate;
 
     // all changes received fromt the server must be processed as Admin
@@ -219,7 +225,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
     const HierarchyMapping = new Map<string, number>([
         ["Not Shared", 0],
         ["Can View", 1],
-        ["Can Add", 2],
+        ["Can Augment", 2],
         ["Can Edit", 3],
         ["Admin", 4]
     ]);
@@ -281,7 +287,7 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
 
     // 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 Add", "Can View", "Not Shared", undefined].includes(value)) return true;
+    // if (typeof prop === "string" && prop.startsWith("acl") && !["Can Edit", "Can Augment", "Can View", "Not Shared", undefined].includes(value)) return true;
 
     if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) {
         if (!prop.startsWith("_")) {
-- 
cgit v1.2.3-70-g09d2


From 9f6dba274539a78a541afd398c73a17dec996319 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 12 Oct 2020 01:34:00 -0400
Subject: capped zoom in at a factor of 20.  fixed zoom in not to pan when it
 hits its zoom in limit.

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4df90e8ea..71519f2b9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -783,10 +783,14 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         let deltaScale = deltaY > 0 ? (1 / 1.05) : 1.05;
         if (deltaScale < 0) deltaScale = -deltaScale;
         const [x, y] = this.getTransform().transformPoint(pointX, pointY);
+        const invTransform = this.getLocalTransform().inverse();
+        if (deltaScale * invTransform.Scale > 20) {
+            deltaScale = 20 / invTransform.Scale;
+        }
         const localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
 
         if (localTransform.Scale >= 0.15 || localTransform.Scale > this.zoomScaling()) {
-            const safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40);
+            const safeScale = Math.min(Math.max(0.15, localTransform.Scale), 20);
             this.props.Document[this.scaleFieldKey] = Math.abs(safeScale);
             this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
         }
-- 
cgit v1.2.3-70-g09d2


From 072d1aa988f36220e4b74c0ee5413f0597a4b619 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 13 Oct 2020 16:57:25 -0400
Subject: prevent zooming out on annotation overlays beyond 1-1

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 71519f2b9..f075aefe1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -789,7 +789,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
         const localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
 
-        if (localTransform.Scale >= 0.15 || localTransform.Scale > this.zoomScaling()) {
+        if ((localTransform.Scale > 1 || !this.props.annotationsKey) && (localTransform.Scale >= 0.15 || localTransform.Scale > this.zoomScaling())) {
             const safeScale = Math.min(Math.max(0.15, localTransform.Scale), 20);
             this.props.Document[this.scaleFieldKey] = Math.abs(safeScale);
             this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
-- 
cgit v1.2.3-70-g09d2


From 79221b73d0526ce31349cf4a5b75f0047c60b829 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 14 Oct 2020 11:02:40 -0400
Subject: fixed focusDocument in freeform views to work with annotation
 overlays(mostly) - still an issue when original image is in a frame that
 doen't match its dimensions

---
 src/client/util/CurrentUserUtils.ts                   |  1 +
 src/client/views/collections/TabDocView.tsx           | 12 +++++++-----
 .../collectionFreeForm/CollectionFreeFormView.tsx     | 19 +++++++++++--------
 src/client/views/nodes/ImageBox.tsx                   | 16 +++++++++-------
 4 files changed, 28 insertions(+), 20 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 7535d7c24..dcbeba8cd 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -881,6 +881,7 @@ export class CurrentUserUtils {
             let linkDocs = await DocServer.GetRefField(linkDatabaseId);
             if (!linkDocs) {
                 linkDocs = new Doc(linkDatabaseId, true);
+                (linkDocs as Doc).author = Doc.CurrentUserEmail;
                 (linkDocs as Doc).data = new List<Doc>([]);
                 (linkDocs as Doc)["acl-Public"] = SharingPermissions.Add;
             }
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 70eb5b895..0c7f39dc7 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -45,6 +45,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     @observable private _document: Doc | undefined;
     @observable private _view: DocumentView | undefined;
 
+    @computed get contentScaling() { return this.ContentScaling(); }
+
     get stack(): any { return (this.props as any).glContainer.parent.parent; }
     get tab() { return (this.props as any).glContainer.tab; }
     get view() { return this._view; }
@@ -194,7 +196,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     panelHeight = () => this.nativeAspect() && this.nativeAspect() > this._panelWidth / this._panelHeight ? this._panelWidth / this.nativeAspect() : this._panelHeight;
     nativeWidth = () => !this.layoutDoc?._fitWidth ? NumCast(this.layoutDoc?._nativeWidth) || this._panelWidth : 0;
     nativeHeight = () => !this.layoutDoc?._fitWidth ? NumCast(this.layoutDoc?._nativeHeight) || this._panelHeight : 0;
-    contentScaling = () => {
+    ContentScaling = () => {
         const nativeH = NumCast(this.layoutDoc?._nativeHeight);
         const nativeW = NumCast(this.layoutDoc?._nativeWidth);
         let scaling = 1;
@@ -210,12 +212,12 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         if (this._mainCont?.children) {
             const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont.children[0]?.firstChild as HTMLElement);
             const scale = Utils.GetScreenTransform(this._mainCont).scale;
-            return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale);
+            return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.ContentScaling() / scale);
         }
         return Transform.Identity();
     }
-    @computed get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; }
-    @computed get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this._panelWidth * 100}% ` : undefined; }
+    @computed get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.ContentScaling()) / 2 : 0; }
+    @computed get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.ContentScaling()) / this._panelWidth * 100}% ` : undefined; }
     @computed get layoutDoc() { return this._document && Doc.Layout(this._document); }
 
     // adds a tab to the layout based on the locaiton parameter which can be:
@@ -337,7 +339,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 rootSelected={returnTrue}
                 addDocument={undefined}
                 removeDocument={undefined}
-                ContentScaling={this.contentScaling}
+                ContentScaling={this.ContentScaling}
                 PanelWidth={this.panelWidth}
                 PanelHeight={this.panelHeight}
                 NativeHeight={this.nativeHeight() ? this.nativeHeight : undefined}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f075aefe1..8b9e84bd6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -787,9 +787,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         if (deltaScale * invTransform.Scale > 20) {
             deltaScale = 20 / invTransform.Scale;
         }
+        if (deltaScale * invTransform.Scale < 1 && this.isAnnotationOverlay) {
+            deltaScale = 1 / invTransform.Scale;
+        }
         const localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
 
-        if ((localTransform.Scale > 1 || !this.props.annotationsKey) && (localTransform.Scale >= 0.15 || localTransform.Scale > this.zoomScaling())) {
+        if (localTransform.Scale >= 0.15 || localTransform.Scale > this.zoomScaling()) {
             const safeScale = Math.min(Math.max(0.15, localTransform.Scale), 20);
             this.props.Document[this.scaleFieldKey] = Math.abs(safeScale);
             this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
@@ -919,21 +922,21 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
         } else {
             const layoutdoc = Doc.Layout(doc);
-            const newPanX = NumCast(doc.x) + NumCast(layoutdoc._width) / 2;
-            const newPanY = NumCast(doc.y) + NumCast(layoutdoc._height) / 2;
+
+            willZoom && this.setScaleToZoom(layoutdoc, scale);
+            const newPanX = (NumCast(doc.x) + doc[WidthSym]() / 2) - (this.isAnnotationOverlay ? (NumCast(this.props.Document._nativeWidth)) / 2 / this.zoomScaling() : 0);
+            const newPanY = (NumCast(doc.y) + doc[HeightSym]() / 2) - (this.isAnnotationOverlay ? (NumCast(this.props.Document._nativeHeight)) / 2 / this.zoomScaling() : 0);
             const newState = HistoryUtil.getState();
             newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY };
             HistoryUtil.pushState(newState);
 
             const savedState = { px: this.Document._panX, py: this.Document._panY, s: this.Document[this.scaleFieldKey], pt: this.Document._viewTransition };
-
-            if (DocListCast(this.dataDoc[this.props.fieldKey]).includes(doc)) {
+            if (DocListCast(this.dataDoc[this.props.annotationsKey || this.props.fieldKey]).includes(doc)) {
                 // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active...
                 if (!doc.z) this.setPan(newPanX, newPanY, doc.focusSpeed || doc.focusSpeed === 0 ? `transform ${doc.focusSpeed}ms` : "transform 500ms", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
             }
             Doc.BrushDoc(this.props.Document);
             this.props.focus(this.props.Document);
-            willZoom && this.setScaleToZoom(layoutdoc, scale);
             Doc.linkFollowHighlight(doc);
 
             const notFocused = newPanX === savedState.px && newPanY === savedState.py;
@@ -951,8 +954,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     setScaleToZoom = (doc: Doc, scale: number = 0.75) => {
-        const pw = this.props.PanelWidth();
-        const ph = this.props.PanelHeight();
+        const pw = this.isAnnotationOverlay ? NumCast(this.props.Document._nativeWidth) : this.props.PanelWidth();
+        const ph = this.isAnnotationOverlay ? NumCast(this.props.Document._nativeHeight) : this.props.PanelHeight();
         pw && ph && (this.Document[this.scaleFieldKey] = scale * Math.min(pw / NumCast(doc._width), ph / NumCast(doc._height)));
     }
 
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 164456cfb..bc38a17eb 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -67,6 +67,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
     @observable static _showControls: boolean;
     @observable uploadIcon = uploadIcons.idle;
 
+    @computed get contentScaling() { return this.props.ContentScaling(); }
+
     protected createDropTarget = (ele: HTMLDivElement) => {
         this._dropDisposer?.();
         ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
@@ -266,7 +268,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
     considerGooglePhotosLink = () => {
         const remoteUrl = this.dataDoc.googlePhotosUrl;
         return !remoteUrl ? (null) : (<img draggable={false}
-            style={{ transform: `scale(${this.props.ContentScaling()})`, transformOrigin: "bottom right" }}
+            style={{ transform: `scale(${this.contentScaling})`, transformOrigin: "bottom right" }}
             id={"google-photos"}
             src={"/assets/google_photos.png"}
             onClick={() => window.open(remoteUrl)}
@@ -291,7 +293,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
         return (
             <img
                 id={"upload-icon"} draggable={false}
-                style={{ transform: `scale(${1 / this.props.ContentScaling()})`, transformOrigin: "bottom right" }}
+                style={{ transform: `scale(${1 / this.contentScaling})`, transformOrigin: "bottom right" }}
                 src={`/assets/${this.uploadIcon}`}
                 onClick={async () => {
                     const { dataDoc } = this;
@@ -402,18 +404,18 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
             (this.props.PanelHeight() - this.props.PanelWidth() * aspect) / 2 : 0;
     }
 
-    screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.ycenter / this.props.ContentScaling());
+    screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.ycenter / this.contentScaling);
 
     contentFunc = () => [this.content];
     render() {
         TraceMobx();
         return (<div className={`imageBox`} onContextMenu={this.specificContextMenu}
             style={{
-                transform: this.props.PanelWidth() ? undefined : `scale(${this.props.ContentScaling()})`,
-                width: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
-                height: this.props.PanelWidth() ? undefined : `${100 / this.props.ContentScaling()}%`,
+                transform: this.props.PanelWidth() ? undefined : `scale(${this.contentScaling})`,
+                width: this.props.PanelWidth() ? undefined : `${100 / this.contentScaling}%`,
+                height: this.props.PanelWidth() ? undefined : `${100 / this.contentScaling}%`,
                 pointerEvents: this.layoutDoc._isBackground ? "none" : undefined,
-                borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.props.ContentScaling()}px`
+                borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.contentScaling}px`
             }} >
             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
                 forceScaling={true}
-- 
cgit v1.2.3-70-g09d2

