From c6e407d7ab92244964c2fad05b6a68d124bf18df Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 7 Oct 2020 11:32:23 -0400
Subject: fixed following links to not center outer document if originating
 anchor is in that document.  fixed scroling to happen instantly if it's
 already scrolled there.  fixed clicking on pdf text anchor to select the pdf.

---
 src/client/views/pdf/Annotation.tsx | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

(limited to 'src/client/views/pdf/Annotation.tsx')

diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 84b14cd61..e7f901091 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -15,6 +15,7 @@ interface IAnnotationProps {
     addDocTab: (document: Doc, where: string) => boolean;
     pinToPres: (document: Doc, unpin?: boolean) => void;
     focus: (doc: Doc) => void;
+    select: (isCtrlPressed: boolean) => void;
     dataDoc: Doc;
     fieldKey: string;
     showInfo: (anno: Opt<Doc>) => void;
@@ -25,7 +26,7 @@ export
     class Annotation extends React.Component<IAnnotationProps> {
     render() {
         return DocListCast(this.props.anno.annotations).map(a =>
-            <RegionAnnotation {...this.props} showInfo={this.props.showInfo} pinToPres={this.props.pinToPres} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />);
+            <RegionAnnotation {...this.props} showInfo={this.props.showInfo} select={this.props.select} pinToPres={this.props.pinToPres} document={a} x={NumCast(a.x)} y={NumCast(a.y)} width={a[WidthSym]()} height={a[HeightSym]()} key={a[Id]} />);
     }
 }
 
@@ -37,6 +38,7 @@ interface IRegionAnnotationProps {
     height: number;
     addDocTab: (document: Doc, where: string) => boolean;
     pinToPres: (document: Doc, unpin: boolean) => void;
+    select: (isCtrlPressed: boolean) => void;
     document: Doc;
     dataDoc: Doc;
     fieldKey: string;
@@ -115,7 +117,8 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
             e.persist();
             e.stopPropagation();
             PromiseValue(this.props.document.group).then(annoGroup => annoGroup instanceof Doc &&
-                DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation), false, undefined)
+                DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation), false, undefined,
+                    () => this.props.select(false))
             );
         }
     }
-- 
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/pdf/Annotation.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

