aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/CurrentUserUtils.ts23
-rw-r--r--src/client/util/DocumentManager.ts18
-rw-r--r--src/client/util/DragManager.ts28
-rw-r--r--src/client/util/Import & Export/ImageUtils.ts4
-rw-r--r--src/client/util/LinkManager.ts111
-rw-r--r--src/client/util/UndoManager.ts2
6 files changed, 71 insertions, 115 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index dcbeba8cd..4f054269f 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -405,7 +405,7 @@ export class CurrentUserUtils {
selection: { type: "text", anchor: 1, head: 1 },
storedMarks: []
};
- const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { title: "header", version: headerViewVersion, target: doc, _height: 70, _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, cloneFieldFilter: new List<string>(["system"]) }, "header"); // text needs to be a space to allow templateText to be created
+ const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { title: "header", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, cloneFieldFilter: new List<string>(["system"]) }, "header"); // text needs to be a space to allow templateText to be created
headerTemplate[DataSym].layout =
"<div style={'height:100%'}>" +
" <FormattedTextBox {...props} fieldKey={'header'} dontSelectOnLoad={'true'} ignoreAutoHeight={'true'} pointerEvents='{this._headerPointerEvents||`none`}' fontSize='{this._headerFontSize}px' height='{this._headerHeight}px' background='{this._headerColor||this.target.mySharedDocs.userColor}' />" +
@@ -878,7 +878,7 @@ export class CurrentUserUtils {
// Sharing sidebar is where shared documents are contained
static async setupSharingSidebar(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
if (doc.myLinkDatabase === undefined) {
- let linkDocs = await DocServer.GetRefField(linkDatabaseId);
+ let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId);
if (!linkDocs) {
linkDocs = new Doc(linkDatabaseId, true);
(linkDocs as Doc).author = Doc.CurrentUserEmail;
@@ -888,7 +888,7 @@ export class CurrentUserUtils {
doc.myLinkDatabase = new PrefetchProxy(linkDocs);
}
if (doc.mySharedDocs === undefined) {
- let sharedDocs = await DocServer.GetRefField(sharingDocumentId + "outer");
+ let sharedDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(sharingDocumentId + "outer");
if (!sharedDocs) {
sharedDocs = Docs.Create.StackingDocument([], {
title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 50, _gridGap: 15,
@@ -1024,6 +1024,7 @@ export class CurrentUserUtils {
// Doc.AddDocToList(Cast(doc["template-notes"], Doc, null), "data", deleg);
// }
// });
+ setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500);
return doc;
}
@@ -1047,8 +1048,12 @@ export class CurrentUserUtils {
await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => {
const { userDocumentId, sharingDocumentId, linkDatabaseId } = JSON.parse(ids);
if (userDocumentId !== "guest") {
- return DocServer.GetRefField(userDocumentId).then(async field =>
- this.updateUserDocument(Doc.SetUserDoc(field instanceof Doc ? field : new Doc(userDocumentId, true)), sharingDocumentId, linkDatabaseId));
+ return DocServer.GetRefField(userDocumentId).then(async field => {
+ Docs.newAccount = !(field instanceof Doc);
+ await Docs.Prototypes.initialize();
+ const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc;
+ return this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
+ });
} else {
throw new Error("There should be a user id! Why does Dash think there isn't one?");
}
@@ -1108,7 +1113,7 @@ export class CurrentUserUtils {
const response = await fetch(upload, { method: "POST", body: formData });
const json = await response.json();
if (json !== "error") {
- const doc = await DocServer.GetRefField(json);
+ const doc = Docs.newAccount ? undefined : await DocServer.GetRefField(json);
if (doc instanceof Doc) {
setTimeout(() => SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs =>
docs.docs.forEach(d => LinkManager.Instance.addLink(d))), 2000); // need to give solr some time to update so that this query will find any link docs we've added.
@@ -1119,6 +1124,9 @@ export class CurrentUserUtils {
const disposer = OverlayView.ShowSpinner();
DocListCastAsync(importDocs.data).then(async list => {
const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {});
+ if (results.length !== input.files?.length) {
+ alert("Error uploading files - possibly due to unsupported file types");
+ }
list?.splice(0, 0, ...results);
disposer();
});
@@ -1185,6 +1193,7 @@ export class CurrentUserUtils {
public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); }
public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); }
public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); }
+ public static get OverlayDocs() { return DocListCast((Doc.UserDoc().myOverlayDocs as Doc)?.data); }
}
Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
@@ -1203,7 +1212,5 @@ Scripting.addGlobal(function createNewPresentation() { return MainView.Instance.
"creates a new presentation when called");
Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); },
"returns all the links to the document or its annotations", "(doc: any)");
-Scripting.addGlobal(function directLinks(doc: any) { return new List(LinkManager.Instance.getAllDirectLinks(doc)); },
- "returns all the links directly to the document", "(doc: any)");
Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); },
"imports files from device directly into the import sidebar");
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index dc911ea75..a6816c7f9 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -110,7 +110,7 @@ export class DocumentManager {
public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
const views = this.getDocumentViews(toFind).filter(view => view.props.Document !== originatingDoc);
- return views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined);
+ return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined);
}
public getDocumentViews(toFind: Doc): DocumentView[] {
const toReturn: DocumentView[] = [];
@@ -128,19 +128,19 @@ export class DocumentManager {
}
- static addRightSplit = (doc: Doc, finished?: () => void) => {
+ static addView = (doc: Doc, finished?: () => void) => {
CollectionDockingView.AddSplit(doc, "right");
finished?.();
}
public jumpToDocument = async (
targetDoc: Doc, // document to display
willZoom: boolean, // whether to zoom doc to take up most of screen
- createViewFunc = DocumentManager.addRightSplit, // how to create a view of the doc if it doesn't exist
+ createViewFunc = DocumentManager.addView, // how to create a view of the doc if it doesn't exist
docContext?: Doc, // context to load that should contain the target
linkDoc?: Doc, // link that's being followed
closeContextIfNotFound: boolean = false, // after opening a context where the document should be, this determines whether the context should be closed if the Doc isn't actually there
originatingDoc: Opt<Doc> = undefined, // doc that initiated the display of the target odoc
- finished?: () => void
+ finished?: () => void,
): Promise<void> => {
const getFirstDocView = DocumentManager.Instance.getFirstDocumentView;
const focusAndFinish = () => { finished?.(); return false; };
@@ -163,21 +163,19 @@ export class DocumentManager {
if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight?
const sameContext = annotatedDoc && annotatedDoc === originatingDoc?.context;
if (originatingDoc?.isPushpin) {
- const hide = !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
- if (notfocused || docView.props.Document.hidden) {
+ docView.props.focus(docView.props.Document, willZoom, undefined, (didFocus: boolean) => {
+ if (!didFocus || 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);
+ }, sameContext, false);// don't want to focus the container if the source and target are in the same container, so pass 'sameContext' for dontCenter parameter
//finished?.();
}
else {
docView.select(false);
docView.props.Document.hidden && (docView.props.Document.hidden = undefined);
// @ts-ignore
- docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish, sameContext);
+ docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish, sameContext, false);
}
highlight();
} else {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 3a0f306f3..86e2d339e 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -201,7 +201,14 @@ export namespace DragManager {
}
// drag a document and drop it (or make an alias/copy on drop)
- export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
+ export function StartDocumentDrag(
+ eles: HTMLElement[],
+ dragData: DocumentDragData,
+ downX: number,
+ downY: number,
+ options?: DragOptions,
+ dropEvent?: () => any
+ ) {
const addAudioTag = (dropDoc: any) => {
dropDoc && !dropDoc.creationDate && (dropDoc.creationDate = new DateField);
dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(dropDoc);
@@ -209,6 +216,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
if (docDragData && !docDragData.droppedDocuments.length) {
docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
docDragData.droppedDocuments =
@@ -408,7 +416,13 @@ export namespace DragManager {
});
const hideSource = options?.hideSource ? true : false;
- eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.parentElement.hidden = hideSource) : (ele.hidden = hideSource));
+ eles.forEach(ele => {
+ if (ele.parentElement && ele.parentElement?.className === dragData.dragDivName) {
+ ele.parentElement.hidden = hideSource;
+ } else {
+ ele.hidden = hideSource;
+ }
+ });
SnappingManager.SetIsDragging(true);
let lastX = downX;
@@ -506,27 +520,25 @@ 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.parentElement.hidden = false) : (ele.hidden = false));
+ eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.hidden = ele.parentElement.hidden = false) : (ele.hidden = false));
};
const endDrag = action(() => {
+ hideDragShowOriginalElements();
document.removeEventListener("pointermove", moveHandler, true);
document.removeEventListener("pointerup", upHandler);
+ SnappingManager.SetIsDragging(false);
SnappingManager.clearSnapLines();
batch.end();
});
AbortDrag = () => {
- hideDragShowOriginalElements();
- SnappingManager.SetIsDragging(false);
options?.dragComplete?.(new DragCompleteEvent(true, dragData));
endDrag();
};
const upHandler = (e: PointerEvent) => {
- hideDragShowOriginalElements();
dispatchDrag(eles, e, dragData, xFromLeft, yFromTop, xFromRight, yFromBottom, options, finishDrag);
- SnappingManager.SetIsDragging(false);
- endDrag();
options?.dragComplete?.(new DragCompleteEvent(false, dragData));
+ endDrag();
};
document.addEventListener("pointermove", moveHandler, true);
document.addEventListener("pointerup", upHandler);
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index 0d12b39b8..9bd92a316 100644
--- a/src/client/util/Import & Export/ImageUtils.ts
+++ b/src/client/util/Import & Export/ImageUtils.ts
@@ -1,7 +1,6 @@
import { Doc } from "../../../fields/Doc";
import { ImageField } from "../../../fields/URLField";
-import { Cast, StrCast } from "../../../fields/Types";
-import { Docs } from "../../documents/Documents";
+import { Cast, StrCast, NumCast } from "../../../fields/Types";
import { Networking } from "../../Network";
import { Id } from "../../../fields/FieldSymbols";
import { Utils } from "../../../Utils";
@@ -22,6 +21,7 @@ export namespace ImageUtils {
} = await Networking.PostToServer("/inspectImage", { source });
document.exif = error || Doc.Get.FromJson({ data });
const proto = Doc.GetProto(document);
+ nativeWidth && (document._height = NumCast(document._width) * nativeHeight / nativeWidth);
proto["data-nativeWidth"] = nativeWidth;
proto["data-nativeHeight"] = nativeHeight;
proto["data-path"] = source;
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 1ba6cff6d..802b8ae7b 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -22,119 +22,58 @@ import { computedFn } from "mobx-utils";
export class LinkManager {
private static _instance: LinkManager;
-
public static currentLink: Opt<Doc>;
+ public static get Instance(): LinkManager { return this._instance || (this._instance = new this()); }
- public static get Instance(): LinkManager {
- return this._instance || (this._instance = new this());
- }
-
- private constructor() {
- }
+ public addLink(linkDoc: Doc) { return Doc.AddDocToList(Doc.LinkDBDoc(), "data", linkDoc); }
+ public deleteLink(linkDoc: Doc) { return Doc.RemoveDocFromList(Doc.LinkDBDoc(), "data", linkDoc); }
+ public deleteAllLinksOnAnchor(anchor: Doc) { LinkManager.Instance.relatedLinker(anchor).forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc)); }
+ public getAllRelatedLinks(anchor: Doc) { return this.relatedLinker(anchor); } // finds all links that contain the given anchor
+ public getAllDirectLinks(anchor: Doc): Doc[] { return this.directLinker(anchor); } // finds all links that contain the given anchor
+ public getAllLinks(): Doc[] { return this.allLinks(); }
- public getAllLinks(): Doc[] {
+ allLinks = computedFn(function allLinks(this: any): Doc[] {
const lset = new Set<Doc>(DocListCast(Doc.LinkDBDoc().data));
- SharingManager.Instance.users.forEach(user => {
- DocListCast(user.linkDatabase?.data).map(doc => {
- lset.add(doc);
- });
- });
+ SharingManager.Instance.users.forEach(user => DocListCast(user.linkDatabase?.data).forEach(doc => lset.add(doc)));
return Array.from(lset);
- }
-
- public addLink(linkDoc: Doc): boolean {
- return Doc.AddDocToList(Doc.LinkDBDoc(), "data", linkDoc);
- }
-
- public deleteLink(linkDoc: Doc): boolean {
- return Doc.RemoveDocFromList(Doc.LinkDBDoc(), "data", linkDoc);
- }
+ }, true);
- // finds all links that contain the given anchor
- public getAllDirectLinks(anchor: Doc): Doc[] {
- const related = LinkManager.Instance.getAllLinks().filter(link => link).filter(link => {
- const a1 = Cast(link.anchor1, Doc, null);
- const a2 = Cast(link.anchor2, Doc, null);
- const protomatch1 = Doc.AreProtosEqual(anchor, a1);
- const protomatch2 = Doc.AreProtosEqual(anchor, a2);
- return ((a1?.author !== undefined && a2?.author !== undefined) || link.author === Doc.CurrentUserEmail) && (protomatch1 || protomatch2 || Doc.AreProtosEqual(link, anchor));
+ directLinker = computedFn(function directLinker(this: any, anchor: Doc): Doc[] {
+ return LinkManager.Instance.allLinks().filter(link => {
+ const a1 = Cast(link?.anchor1, Doc, null);
+ const a2 = Cast(link?.anchor2, Doc, null);
+ return link && ((a1?.author !== undefined && a2?.author !== undefined) || link.author === Doc.CurrentUserEmail) && (Doc.AreProtosEqual(anchor, a1) || Doc.AreProtosEqual(anchor, a2) || Doc.AreProtosEqual(link, anchor));
});
- return related;
- }
+ }, true);
- relatedLinker = computedFn(function realtedLinker(this: any, anchor: Doc) {
- const related = LinkManager.Instance.getAllDirectLinks(anchor);
- DocListCast(anchor[Doc.LayoutFieldKey(anchor) + "-annotations"]).map(anno => {
- related.push(...LinkManager.Instance.getAllRelatedLinks(anno));
- });
- return related;
- }.bind(this), true);
-
- // finds all links that contain the given anchor
- public getAllRelatedLinks(anchor: Doc): Doc[] {
- return this.relatedLinker(anchor);
- }
-
- public deleteAllLinksOnAnchor(anchor: Doc) {
- const related = LinkManager.Instance.getAllRelatedLinks(anchor);
- related.forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc));
- }
-
- // gets the groups associates with an anchor in a link
- public getAnchorGroups(linkDoc: Doc, anchor?: Doc): Array<Doc> {
- if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, null))) {
- return DocListCast(linkDoc.anchor1Groups);
- } else {
- return DocListCast(linkDoc.anchor2Groups);
- }
- }
- public addGroupToAnchor(linkDoc: Doc, anchor: Doc, groupDoc: Doc, replace: boolean = false) {
- Doc.GetProto(linkDoc).linkRelationship = groupDoc.linkRelationship;
- }
-
- // removes group doc of given group type only from given anchor on given link
- public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) {
- Doc.GetProto(linkDoc).linkRelationship = "-ungrouped-";
- }
+ relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
+ return DocListCast(anchor[Doc.LayoutFieldKey(anchor) + "-annotations"]).reduce((list, anno) =>
+ [...list, ...LinkManager.Instance.relatedLinker(anno)],
+ LinkManager.Instance.directLinker(anchor).slice());
+ }, true);
// returns map of group type to anchor's links in that group type
public getRelatedGroupedLinks(anchor: Doc): Map<string, Array<Doc>> {
- const related = this.getAllRelatedLinks(anchor);
const anchorGroups = new Map<string, Array<Doc>>();
- related.forEach(link => {
+ this.relatedLinker(anchor).forEach(link => {
if (!link.linkRelationship || link?.linkRelationship !== "-ungrouped-") {
const group = anchorGroups.get(StrCast(link.linkRelationship));
anchorGroups.set(StrCast(link.linkRelationship), group ? [...group, link] : [link]);
-
} else {
// if link is in no groups then put it in default group
const group = anchorGroups.get("*");
anchorGroups.set("*", group ? [...group, link] : [link]);
}
-
});
return anchorGroups;
}
- // returns a list of all metadata docs associated with the given group type
- public getAllMetadataDocsInGroup(groupType: string): Array<Doc> {
- const md: Doc[] = [];
- const allLinks = LinkManager.Instance.getAllLinks();
- allLinks.forEach(linkDoc => {
- if (StrCast(linkDoc.linkRelationship).toUpperCase() === groupType.toUpperCase()) { md.push(linkDoc); }
- });
- return md;
- }
-
// checks if a link with the given anchors exists
public doesLinkExist(anchor1: Doc, anchor2: Doc): boolean {
- const allLinks = LinkManager.Instance.getAllLinks();
- const index = allLinks.findIndex(linkDoc => {
- return (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor2)) ||
- (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor1));
- });
- return index !== -1;
+ return -1 !== LinkManager.Instance.allLinks().findIndex(linkDoc =>
+ (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor2)) ||
+ (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor1)));
}
// finds the opposite anchor of a given anchor in a link
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 0f7ad6d0a..569ad8ab4 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -76,7 +76,7 @@ export namespace UndoManager {
export let undoStack: UndoBatch[] = observable([]);
export let redoStack: UndoBatch[] = observable([]);
let currentBatch: UndoBatch | undefined;
- let batchCounter = 0;
+ export let batchCounter = 0;
let undoing = false;
let tempEvents: UndoEvent[] | undefined = undefined;