aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
authorusodhi <61431818+usodhi@users.noreply.github.com>2020-12-17 16:22:06 +0530
committerusodhi <61431818+usodhi@users.noreply.github.com>2020-12-17 16:22:06 +0530
commit48fd017c46b930ae94e341ae73bfc35006008b44 (patch)
tree488aae6f3864622548c01433867309cc0bd03a24 /src/client/util
parent563d86f03f63f60ec47aef23d2022c660ee18697 (diff)
parentf4f4cb6b3a639c3e1c0d291f1d290e80097cfa06 (diff)
merged
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/CurrentUserUtils.ts7
-rw-r--r--src/client/util/DictationManager.ts16
-rw-r--r--src/client/util/DocumentManager.ts40
-rw-r--r--src/client/util/DragManager.ts40
-rw-r--r--src/client/util/HypothesisUtils.ts2
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx1
-rw-r--r--src/client/util/InteractionUtils.tsx9
-rw-r--r--src/client/util/LinkManager.ts73
-rw-r--r--src/client/util/SelectionManager.ts58
-rw-r--r--src/client/util/SharingManager.tsx43
10 files changed, 167 insertions, 122 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 213bc3d88..00ee0f4b9 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -252,7 +252,7 @@ export class CurrentUserUtils {
doc["template-note-Idea"] = new PrefetchProxy(noteView);
}
if (doc["template-note-Topic"] === undefined) {
- const noteView = Docs.Create.TextDocument("", { title: "text", style: "Topic", backgroundColor: "lightBlue", system: true });
+ const noteView = Docs.Create.TextDocument("", { title: "text", style: "Topic", backgroundColor: "lightblue", system: true });
noteView.isTemplateDoc = makeTemplate(noteView, true, "Topic");
doc["template-note-Topic"] = new PrefetchProxy(noteView);
}
@@ -801,7 +801,6 @@ export class CurrentUserUtils {
}
}
-
static setupUserDoc(doc: Doc) {
if (doc.myUserDoc === undefined) {
doc.treeViewOpen = true;
@@ -911,7 +910,7 @@ export class CurrentUserUtils {
}
if (doc.myImportPanel === undefined) {
const uploads = Cast(doc.myImportDocs, Doc, null);
- const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _backgroundColor: "black", _stayInCollection: true, _hideContextMenu: true, title: "Import", icon: "upload", system: true });
+ const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", icon: "upload", system: true });
doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, ignoreClick: true, _stayInCollection: true, _hideContextMenu: true, lockedPosition: true, system: true }));
}
}
@@ -1204,7 +1203,7 @@ Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
if (copy) {
CollectionDockingView.AddSplit(copy, "right");
const view = DocumentManager.Instance.getFirstDocumentView(copy);
- view && SelectionManager.SelectDoc(view, false);
+ view && SelectionManager.SelectView(view, false);
}
});
Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); },
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 231e1fa8d..c6b654dda 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -1,17 +1,17 @@
-import { SelectionManager } from "./SelectionManager";
-import { DocumentView } from "../views/nodes/DocumentView";
-import { UndoManager } from "./UndoManager";
import * as interpreter from "words-to-numbers";
-import { DocumentType } from "../documents/DocumentTypes";
import { Doc, Opt } from "../../fields/Doc";
import { List } from "../../fields/List";
-import { Docs } from "../documents/Documents";
-import { Cast, CastCtor } from "../../fields/Types";
+import { RichTextField } from "../../fields/RichTextField";
import { listSpec } from "../../fields/Schema";
+import { Cast, CastCtor } from "../../fields/Types";
import { AudioField, ImageField } from "../../fields/URLField";
import { Utils } from "../../Utils";
-import { RichTextField } from "../../fields/RichTextField";
+import { Docs } from "../documents/Documents";
+import { DocumentType } from "../documents/DocumentTypes";
import { DictationOverlay } from "../views/DictationOverlay";
+import { DocumentView } from "../views/nodes/DocumentView";
+import { SelectionManager } from "./SelectionManager";
+import { UndoManager } from "./UndoManager";
/**
* This namespace provides a singleton instance of a manager that
@@ -235,7 +235,7 @@ export namespace DictationManager {
export const execute = async (phrase: string) => {
return UndoManager.RunInBatch(async () => {
- const targets = SelectionManager.SelectedDocuments();
+ const targets = SelectionManager.Views();
if (!targets || !targets.length) {
return;
}
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index eaccbdb63..1f2dd350b 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,7 +1,7 @@
import { action, observable } from 'mobx';
import { Doc, DocListCast, DocListCastAsync, Opt } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
-import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { Cast, NumCast } from '../../fields/Types';
import { returnFalse } from '../../Utils';
import { DocumentType } from '../documents/DocumentTypes';
import { CollectionDockingView } from '../views/collections/CollectionDockingView';
@@ -9,8 +9,6 @@ import { CollectionView } from '../views/collections/CollectionView';
import { DocumentView } from '../views/nodes/DocumentView';
import { LinkManager } from './LinkManager';
import { Scripting } from './Scripting';
-import { LinkDocPreview } from '../views/nodes/LinkDocPreview';
-import { FormattedTextBoxComment } from '../views/nodes/formattedText/FormattedTextBoxComment';
export type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
@@ -46,12 +44,12 @@ export class DocumentManager {
});
this.DocumentViews.push(view);
}
- public RemoveView = (view: DocumentView) => {
+ public RemoveView = action((view: DocumentView) => {
const index = this.DocumentViews.indexOf(view);
index !== -1 && this.DocumentViews.splice(index, 1);
this.LinkedDocumentViews.slice().forEach(action((pair, i) => pair.a === view || pair.b === view ? this.LinkedDocumentViews.splice(i, 1) : null));
- }
+ });
//gets all views
public getDocumentViewsById(id: string) {
@@ -223,37 +221,5 @@ export class DocumentManager {
}
}
- public async FollowLink(link: Opt<Doc>, doc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
- LinkDocPreview.TargetDoc = undefined;
- FormattedTextBoxComment.linkDoc = undefined;
- const linkDocs = link ? [link] : DocListCast(doc.links);
- const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor1
- const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor2
- const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
- const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
- const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
- const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : (traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs);
- const followLinks = linkDocList.length ? (doc.isPushpin ? linkDocList : [linkDocList[0]]) : [];
- followLinks.forEach(async linkDoc => {
- if (linkDoc) {
- const target = (doc === linkDoc.anchor1 ? linkDoc.anchor2 : doc === linkDoc.anchor2 ? linkDoc.anchor1 :
- (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc;
- const targetTimecode = (doc === linkDoc.anchor1 ? Cast(linkDoc.anchor2_timecode, "number") :
- doc === linkDoc.anchor2 ? Cast(linkDoc.anchor1_timecode, "number") :
- (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc) ? Cast(linkDoc.anchor2_timecode, "number") : Cast(linkDoc.anchor1_timecode, "number")));
- if (target) {
- const containerDoc = (await Cast(target.annotationOn, Doc)) || target;
- containerDoc._currentTimecode = targetTimecode;
- const targetContext = await target?.context as Doc;
- const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined;
- DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "add:right"), finished), targetNavContext, linkDoc, undefined, doc, finished);
- } else {
- finished?.();
- }
- } else {
- finished?.();
- }
- });
- }
}
Scripting.addGlobal(function DocFocus(doc: any) { DocumentManager.Instance.getDocumentViews(Doc.GetProto(doc)).map(view => view.props.focus(doc, true)); }); \ No newline at end of file
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index e3019d288..d24348746 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -109,6 +109,7 @@ export namespace DragManager {
this.linkDragData = dragData instanceof LinkDragData ? dragData : undefined;
this.columnDragData = dragData instanceof ColumnDragData ? dragData : undefined;
}
+ linkDocument?: Doc;
aborted: boolean;
docDragData?: DocumentDragData;
annoDragData?: PdfAnnoDragData;
@@ -125,7 +126,6 @@ export namespace DragManager {
}
draggedDocuments: Doc[];
droppedDocuments: Doc[];
- dragDivName?: string;
treeViewDoc?: Doc;
offset: number[];
canEmbed?: boolean;
@@ -144,8 +144,7 @@ export namespace DragManager {
droppedDocuments: Doc[] = [];
linkSourceDocument: Doc;
dontClearTextBox?: boolean;
- linkDocument?: Doc;
- linkDropCallback?: (data: { linkDocument?: Doc }) => void;
+ linkDropCallback?: (data: { linkDocument: Doc }) => void;
}
export class ColumnDragData {
constructor(colKey: SchemaHeaderField) {
@@ -162,7 +161,6 @@ export namespace DragManager {
this.annotationDocument = annotationDoc;
this.offset = [0, 0];
}
- linkDocument?: Doc;
targetContext: Doc | undefined;
dragDocument: Doc;
annotationDocument: Doc;
@@ -170,7 +168,7 @@ export namespace DragManager {
offset: number[];
dropAction: dropActionType;
userDropAction: dropActionType;
- linkDropCallback?: (data: { linkDocument?: Doc }) => void;
+ linkDropCallback?: (data: { linkDocument: Doc }) => void;
}
export function MakeDropTarget(
@@ -215,7 +213,7 @@ export namespace DragManager {
};
const finishDrag = (e: DragCompleteEvent) => {
const docDragData = e.docDragData;
- if (dropEvent) dropEvent(); // glr: optional additional function to be called - in this case with presentation trails
+ dropEvent?.(); // glr: optional additional function to be called - in this case with presentation trails
if (docDragData && !docDragData.droppedDocuments.length) {
docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
docDragData.droppedDocuments =
@@ -342,12 +340,14 @@ export namespace DragManager {
dragLabel.style.zIndex = "100001";
dragLabel.style.fontSize = "10px";
dragLabel.style.position = "absolute";
- // dragLabel.innerText = "press 'a' to embed on drop"; // bcz: need to move this to a status bar
+ dragLabel.innerText = "press 'a' to embed on drop"; // bcz: need to move this to a status bar
dragDiv.appendChild(dragLabel);
DragManager.Root().appendChild(dragDiv);
}
+ dragDiv.style.width = "";
+ dragDiv.style.height = "";
+ dragDiv.style.overflow = "";
dragDiv.hidden = false;
- dragLabel.style.display = "";
const scaleXs: number[] = [];
const scaleYs: number[] = [];
const xs: number[] = [];
@@ -415,14 +415,12 @@ export namespace DragManager {
return dragElement;
});
- const hideSource = options?.hideSource ? true : false;
- eles.forEach(ele => {
- if (ele.parentElement && ele.parentElement?.className === dragData.dragDivName) {
- ele.parentElement.hidden = hideSource;
- } else {
- ele.hidden = hideSource;
- }
- });
+ const hideDragShowOriginalElements = (hide: boolean) => {
+ dragLabel.style.display = hide ? "" : "none";
+ !hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
+ eles.forEach(ele => ele.hidden = hide);
+ };
+ options?.hideSource && hideDragShowOriginalElements(true);
SnappingManager.SetIsDragging(true);
let lastX = downX;
@@ -517,13 +515,8 @@ export namespace DragManager {
);
};
- const hideDragShowOriginalElements = () => {
- dragLabel.style.display = "none";
- dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
- eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.hidden = ele.parentElement.hidden = false) : (ele.hidden = false));
- };
const endDrag = action(() => {
- hideDragShowOriginalElements();
+ hideDragShowOriginalElements(false);
document.removeEventListener("pointermove", moveHandler, true);
document.removeEventListener("pointerup", upHandler);
SnappingManager.SetIsDragging(false);
@@ -554,6 +547,9 @@ export namespace DragManager {
return ret;
});
dragDiv.hidden = true;
+ dragDiv.style.width = "0";
+ dragDiv.style.height = "0";
+ dragDiv.style.overflow = "hidden";
const target = document.elementFromPoint(e.x, e.y);
removed.map(r => {
r.ele.style.width = r.w;
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index f4cf336e2..7a449b882 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -29,7 +29,7 @@ export namespace Hypothesis {
* Search for a WebDocument whose url field matches the given uri, return undefined if not found
*/
export const findWebDoc = async (uri: string) => {
- const currentDoc = SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0].props.Document;
+ const currentDoc = SelectionManager.Views().length && SelectionManager.Views()[0].props.Document;
if (currentDoc && Cast(currentDoc.data, WebField)?.url.href === uri) return currentDoc; // always check first whether the currently selected doc is the annotation's source, only use Search otherwise
const results: Doc[] = [];
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 7f01966b9..d9f010557 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -158,7 +158,6 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
importContainer = Docs.Create.SchemaDocument(headers, docs, options);
}
runInAction(() => this.phase = 'External: uploading files to Google Photos...');
- importContainer._columnsStack = false;
await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer });
Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
!this.persistent && this.props.removeDocument && this.props.removeDocument(doc);
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index f58277717..01d00db30 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -364,12 +364,9 @@ export namespace InteractionUtils {
export function IsType(e: PointerEvent | React.PointerEvent, type: string): boolean {
switch (type) {
// pen and eraser are both pointer type 'pen', but pen is button 0 and eraser is button 5. -syip2
- case PENTYPE:
- return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0);
- case ERASERTYPE:
- return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON);
- default:
- return e.pointerType === type;
+ case PENTYPE: return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0);
+ case ERASERTYPE: return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON);
+ default: return e.pointerType === type;
}
}
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 38e81cf99..accf53676 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,7 +1,12 @@
+import { computedFn } from "mobx-utils";
import { Doc, DocListCast, Opt } from "../../fields/Doc";
-import { Cast, StrCast } from "../../fields/Types";
+import { BoolCast, Cast, StrCast } from "../../fields/Types";
+import { DocFocusFunc, DocumentViewSharedProps } from "../views/nodes/DocumentView";
+import { FormattedTextBoxComment } from "../views/nodes/formattedText/FormattedTextBoxComment";
+import { LinkDocPreview } from "../views/nodes/LinkDocPreview";
+import { CreateViewFunc, DocumentManager } from "./DocumentManager";
import { SharingManager } from "./SharingManager";
-import { computedFn } from "mobx-utils";
+import { UndoManager } from "./UndoManager";
/*
* link doc:
@@ -89,4 +94,68 @@ export class LinkManager {
if (Doc.AreProtosEqual(anchor, a2.annotationOn as Doc)) return a1;
if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc;
}
+
+
+ // follows a link - if the target is on screen, it highlights/pans to it.
+ // if the target isn't onscreen, then it will open up the target in a tab, on the right, or in place
+ // depending on the followLinkLocation property of the source (or the link itself as a fallback);
+ public static FollowLink = async (linkDoc: Opt<Doc>, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean) => {
+ const batch = UndoManager.StartBatch("follow link click");
+ // open up target if it's not already in view ...
+ const createViewFunc = (doc: Doc, followLoc: string, finished: Opt<() => void>) => {
+ const targetFocusAfterDocFocus = () => {
+ const where = StrCast(sourceDoc.followLinkLocation) || followLoc;
+ const hackToCallFinishAfterFocus = () => {
+ finished && setTimeout(finished, 0); // finished() needs to be called right after hackToCallFinishAfterFocus(), but there's no callback for that so we use the hacky timeout.
+ return false; // we must return false here so that the zoom to the document is not reversed. If it weren't for needing to call finished(), we wouldn't need this function at all since not having it is equivalent to returning false
+ };
+ const addTab = docViewProps.addDocTab(doc, where);
+ addTab && setTimeout(() => {
+ const targDocView = DocumentManager.Instance.getFirstDocumentView(doc);
+ targDocView?.props.focus(doc, BoolCast(sourceDoc.followLinkZoom, false), undefined, hackToCallFinishAfterFocus);
+ }); // add the target and focus on it.
+ return where !== "inPlace" || addTab; // return true to reset the initial focus&zoom (return false for 'inPlace' since resetting the initial focus&zoom will negate the zoom into the target)
+ };
+ if (!sourceDoc.followLinkZoom) {
+ targetFocusAfterDocFocus();
+ } else {
+ // first focus & zoom onto this (the clicked document). Then execute the function to focus on the target
+ docViewProps.focus(sourceDoc, BoolCast(sourceDoc.followLinkZoom, true), 1, targetFocusAfterDocFocus);
+ }
+ };
+ await LinkManager.traverseLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, false), docViewProps.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
+ }
+ public static async traverseLink(link: Opt<Doc>, doc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
+ LinkDocPreview.TargetDoc = undefined;
+ FormattedTextBoxComment.linkDoc = undefined;
+ const linkDocs = link ? [link] : DocListCast(doc.links);
+ const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor1
+ const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor2
+ const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
+ const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
+ const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
+ const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : (traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs);
+ const followLinks = linkDocList.length ? (doc.isPushpin ? linkDocList : [linkDocList[0]]) : [];
+ followLinks.forEach(async linkDoc => {
+ if (linkDoc) {
+ const target = (doc === linkDoc.anchor1 ? linkDoc.anchor2 : doc === linkDoc.anchor2 ? linkDoc.anchor1 :
+ (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc;
+ const targetTimecode = (doc === linkDoc.anchor1 ? Cast(linkDoc.anchor2_timecode, "number") :
+ doc === linkDoc.anchor2 ? Cast(linkDoc.anchor1_timecode, "number") :
+ (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc) ? Cast(linkDoc.anchor2_timecode, "number") : Cast(linkDoc.anchor1_timecode, "number")));
+ if (target) {
+ const containerDoc = (await Cast(target.annotationOn, Doc)) || target;
+ containerDoc._currentTimecode = targetTimecode;
+ const targetContext = await target?.context as Doc;
+ const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined;
+ DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "add:right"), finished), targetNavContext, linkDoc, undefined, doc, finished);
+ } else {
+ finished?.();
+ }
+ } else {
+ finished?.();
+ }
+ });
+ }
+
} \ No newline at end of file
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 34e88c7b0..f657e5b40 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,47 +1,47 @@
-import { observable, action, runInAction, ObservableMap } from "mobx";
-import { Doc, Opt } from "../../fields/Doc";
-import { DocumentView } from "../views/nodes/DocumentView";
+import { action, observable, ObservableMap } from "mobx";
import { computedFn } from "mobx-utils";
+import { Doc, Opt } from "../../fields/Doc";
import { CollectionSchemaView } from "../views/collections/CollectionSchemaView";
import { CollectionViewType } from "../views/collections/CollectionView";
+import { DocumentView } from "../views/nodes/DocumentView";
export namespace SelectionManager {
class Manager {
@observable IsDragging: boolean = false;
- SelectedDocuments: ObservableMap<DocumentView, boolean> = new ObservableMap();
+ SelectedViews: ObservableMap<DocumentView, boolean> = new ObservableMap();
@observable SelectedSchemaDocument: Doc | undefined;
@observable SelectedSchemaCollection: CollectionSchemaView | undefined;
@action
- SelectSchemaDoc(collectionView: Opt<CollectionSchemaView>, doc: Opt<Doc>) {
+ SelectSchemaView(collectionView: Opt<CollectionSchemaView>, doc: Opt<Doc>) {
manager.SelectedSchemaDocument = doc;
manager.SelectedSchemaCollection = collectionView;
}
@action
- SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
+ SelectView(docView: DocumentView, ctrlPressed: boolean): void {
// if doc is not in SelectedDocuments, add it
- if (!manager.SelectedDocuments.get(docView)) {
+ if (!manager.SelectedViews.get(docView)) {
if (!ctrlPressed) {
this.DeselectAll();
}
- manager.SelectedDocuments.set(docView, true);
+ manager.SelectedViews.set(docView, true);
docView.props.whenActiveChanged(true);
- } else if (!ctrlPressed && Array.from(manager.SelectedDocuments.entries()).length > 1) {
- Array.from(manager.SelectedDocuments.keys()).map(dv => dv !== docView && dv.props.whenActiveChanged(false));
+ } else if (!ctrlPressed && Array.from(manager.SelectedViews.entries()).length > 1) {
+ Array.from(manager.SelectedViews.keys()).map(dv => dv !== docView && dv.props.whenActiveChanged(false));
manager.SelectedSchemaDocument = undefined;
manager.SelectedSchemaCollection = undefined;
- manager.SelectedDocuments.clear();
- manager.SelectedDocuments.set(docView, true);
+ manager.SelectedViews.clear();
+ manager.SelectedViews.set(docView, true);
}
}
@action
- DeselectDoc(docView: DocumentView): void {
+ DeselectView(docView: DocumentView): void {
- if (manager.SelectedDocuments.get(docView)) {
- manager.SelectedDocuments.delete(docView);
+ if (manager.SelectedViews.get(docView)) {
+ manager.SelectedViews.delete(docView);
docView.props.whenActiveChanged(false);
}
}
@@ -49,49 +49,49 @@ export namespace SelectionManager {
DeselectAll(): void {
manager.SelectedSchemaCollection = undefined;
manager.SelectedSchemaDocument = undefined;
- Array.from(manager.SelectedDocuments.keys()).map(dv => dv.props.whenActiveChanged(false));
- manager.SelectedDocuments.clear();
+ Array.from(manager.SelectedViews.keys()).map(dv => dv.props.whenActiveChanged(false));
+ manager.SelectedViews.clear();
}
}
const manager = new Manager();
- export function DeselectDoc(docView: DocumentView): void {
- manager.DeselectDoc(docView);
+ export function DeselectView(docView: DocumentView): void {
+ manager.DeselectView(docView);
}
- export function SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
- manager.SelectDoc(docView, ctrlPressed);
+ export function SelectView(docView: DocumentView, ctrlPressed: boolean): void {
+ manager.SelectView(docView, ctrlPressed);
}
- export function SelectSchemaDoc(colSchema: Opt<CollectionSchemaView>, document: Opt<Doc>): void {
- manager.SelectSchemaDoc(colSchema, document);
+ export function SelectSchemaView(colSchema: Opt<CollectionSchemaView>, document: Opt<Doc>): void {
+ manager.SelectSchemaView(colSchema, document);
}
const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) { // wraapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed
- return manager.SelectedDocuments.get(doc) ? true : false;
+ return manager.SelectedViews.get(doc) ? true : false;
});
// computed functions, such as used in IsSelected generate errors if they're called outside of a
// reaction context. Specifying the context with 'outsideReaction' allows an efficiency feature
// to avoid unnecessary mobx invalidations when running inside a reaction.
export function IsSelected(doc: DocumentView | undefined, outsideReaction?: boolean): boolean {
return !doc ? false : outsideReaction ?
- manager.SelectedDocuments.get(doc) ? true : false : // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get()
+ manager.SelectedViews.get(doc) ? true : false : // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get()
IsSelectedCache(doc);
}
export function DeselectAll(except?: Doc): void {
let found: DocumentView | undefined = undefined;
if (except) {
- for (const view of Array.from(manager.SelectedDocuments.keys())) {
+ for (const view of Array.from(manager.SelectedViews.keys())) {
if (view.props.Document === except) found = view;
}
}
manager.DeselectAll();
- if (found) manager.SelectDoc(found, false);
+ if (found) manager.SelectView(found, false);
}
- export function SelectedDocuments(): Array<DocumentView> {
- return Array.from(manager.SelectedDocuments.keys()).filter(dv => dv.props.Document._viewType !== CollectionViewType.Docking);
+ export function Views(): Array<DocumentView> {
+ return Array.from(manager.SelectedViews.keys()).filter(dv => dv.props.Document._viewType !== CollectionViewType.Docking);
}
export function SelectedSchemaDoc(): Doc | undefined {
return manager.SelectedSchemaDocument;
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index fcd10406d..8c36a7f3c 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,13 +1,14 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable, runInAction, computed } from "mobx";
+import { intersection } from "lodash";
+import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
import Select from "react-select";
import * as RequestPromise from "request-promise";
-import { AclAdmin, AclPrivate, DataSym, Doc, DocListCast, Opt, AclSym, AclAddonly, AclEdit, AclReadonly, DocListCastAsync } from "../../fields/Doc";
+import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
import { List } from "../../fields/List";
-import { Cast, NumCast, StrCast } from "../../fields/Types";
-import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx, normalizeEmail } from "../../fields/util";
+import { Cast, StrCast } from "../../fields/Types";
+import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from "../../fields/util";
import { Utils } from "../../Utils";
import { DocServer } from "../DocServer";
import { CollectionView } from "../views/collections/CollectionView";
@@ -15,13 +16,12 @@ import { DictationOverlay } from "../views/DictationOverlay";
import { MainViewModal } from "../views/MainViewModal";
import { DocumentView } from "../views/nodes/DocumentView";
import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
+import { SearchBox } from "../views/search/SearchBox";
import { DocumentManager } from "./DocumentManager";
import { GroupManager, UserOptions } from "./GroupManager";
import { GroupMemberView } from "./GroupMemberView";
-import "./SharingManager.scss";
import { SelectionManager } from "./SelectionManager";
-import { intersection } from "lodash";
-import { SearchBox } from "../views/search/SearchBox";
+import "./SharingManager.scss";
export interface User {
email: string;
@@ -202,7 +202,7 @@ export class SharingManager extends React.Component<{}> {
const key = normalizeEmail(StrCast(group.title));
const acl = `acl-${key}`;
- const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document);
+ const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
docs.forEach(doc => {
doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc);
@@ -306,6 +306,25 @@ export class SharingManager extends React.Component<{}> {
}
}
+ /**
+ * Shares the document with a user.
+ */
+ setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc?: Doc) => {
+ const { user, sharingDoc } = recipient;
+ const target = targetDoc || this.targetDoc!;
+ const acl = `acl-${normalizeEmail(user.email)}`;
+ const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
+
+ const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
+ docs.forEach(doc => {
+ doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc);
+ distributeAcls(acl, permission as SharingPermissions, doc);
+
+ if (permission !== SharingPermissions.None) Doc.AddDocToList(sharingDoc, storage, doc);
+ else GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc || doc));
+ });
+ }
+
// private setExternalSharing = (permission: string) => {
// const sharingDoc = this.sharingDoc;
@@ -348,7 +367,7 @@ export class SharingManager extends React.Component<{}> {
private focusOn = (contents: string) => {
const title = this.targetDoc ? StrCast(this.targetDoc.title) : "";
- const docs = SelectionManager.SelectedDocuments().length > 1 ? SelectionManager.SelectedDocuments().map(docView => docView.props.Document) : [this.targetDoc];
+ const docs = SelectionManager.Views().length > 1 ? SelectionManager.Views().map(docView => docView.props.Document) : [this.targetDoc];
return (
<span
className={"focus-span"}
@@ -424,7 +443,7 @@ export class SharingManager extends React.Component<{}> {
distributeOverCollection = (targetDoc?: Doc) => {
const target = targetDoc || this.targetDoc!;
- const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document);
+ const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
docs.forEach(doc => {
for (const [key, value] of Object.entries(doc[AclSym])) {
distributeAcls(key, this.AclMap.get(value)! as SharingPermissions, target);
@@ -475,9 +494,9 @@ export class SharingManager extends React.Component<{}> {
const groups = this.groupSort === "ascending" ? groupList.slice().sort(this.sortGroups) : this.groupSort === "descending" ? groupList.slice().sort(this.sortGroups).reverse() : groupList;
// handles the case where multiple documents are selected
- let docs = SelectionManager.SelectedDocuments().length < 2 ?
+ let docs = SelectionManager.Views().length < 2 ?
[this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DataSym]]
- : SelectionManager.SelectedDocuments().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DataSym]);
+ : SelectionManager.Views().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DataSym]);
if (this.myDocAcls) {
const newDocs: Doc[] = [];