diff options
Diffstat (limited to 'src/client/util')
| -rw-r--r-- | src/client/util/CurrentUserUtils.ts | 55 | ||||
| -rw-r--r-- | src/client/util/DocumentManager.ts | 64 | ||||
| -rw-r--r-- | src/client/util/DragManager.ts | 33 | ||||
| -rw-r--r-- | src/client/util/LinkManager.ts | 21 | 
4 files changed, 108 insertions, 65 deletions
| diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index f02c5a5a1..3165e38a6 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -9,7 +9,7 @@ import { RichTextField } from "../../fields/RichTextField";  import { listSpec } from "../../fields/Schema";  import { ComputedField, ScriptField } from "../../fields/ScriptField";  import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; -import { nullAudio } from "../../fields/URLField"; +import { ImageField, nullAudio } from "../../fields/URLField";  import { SharingPermissions } from "../../fields/util";  import { Utils } from "../../Utils";  import { DocServer } from "../DocServer"; @@ -193,7 +193,6 @@ export class CurrentUserUtils {          const requiredTypes = [              doc["template-button-slides"] as Doc,              doc["template-mobile-button"] as Doc, -            doc["template-button-detail"] as Doc,              doc["template-button-link"] as Doc,              //doc["template-button-switch"] as Doc]          ]; @@ -293,10 +292,10 @@ export class CurrentUserUtils {          if (doc["template-icon-view"] === undefined) {              const iconView = Docs.Create.LabelDocument({                  title: "icon", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("title"), _backgroundColor: "dimgray", -                _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true +                _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true              });              //  Docs.Create.TextDocument("", { -            //     title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") +            //     title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" })              // });              // Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', "");              iconView.isTemplateDoc = makeTemplate(iconView); @@ -304,8 +303,9 @@ export class CurrentUserUtils {          }          if (doc["template-icon-view-rtf"] === undefined) {              const iconRtfView = Docs.Create.LabelDocument({ -                title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"), -                _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true +                title: "icon_" + DocumentType.RTF, _showTitle: "creationDate", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"), +                _singleLine: false, _minFontSize: 18, _maxFontSize: 24, +                _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true              });              iconRtfView.isTemplateDoc = makeTemplate(iconRtfView, true, "icon_" + DocumentType.RTF);              doc["template-icon-view-rtf"] = new PrefetchProxy(iconRtfView); @@ -313,26 +313,48 @@ export class CurrentUserUtils {          if (doc["template-icon-view-button"] === undefined) {              const iconBtnView = Docs.Create.FontIconDocument({                  title: "icon_" + DocumentType.BUTTON, _nativeHeight: 30, _nativeWidth: 30, -                _width: 30, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true +                _width: 30, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true              });              iconBtnView.isTemplateDoc = makeTemplate(iconBtnView, true, "icon_" + DocumentType.BUTTON);              doc["template-icon-view-button"] = new PrefetchProxy(iconBtnView);          }          if (doc["template-icon-view-img"] === undefined) {              const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { -                title: "data", _width: 50, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true +                title: "data", _width: 150, isTemplateDoc: true, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true              });              iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG);              doc["template-icon-view-img"] = new PrefetchProxy(iconImageView);          }          if (doc["template-icon-view-col"] === undefined) { -            const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true }); +            const iconColView = Docs.Create.ImageDocument("", { title: "icon", _showTitle: "title", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true });              iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL); +            const proto = iconColView.proto as Doc; +            proto["icon-nativeWidth"] = 360 / 4; +            proto["icon-nativeHeight"] = 270 / 4; +            proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png");              doc["template-icon-view-col"] = new PrefetchProxy(iconColView);          } +        if (doc["template-icon-view-video"] === undefined) { +            const iconVidView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); +            iconVidView.isTemplateDoc = makeTemplate(iconVidView, true, "icon_" + DocumentType.VID); +            const proto = iconVidView.proto as Doc; +            proto["icon-nativeWidth"] = 360 / 4; +            proto["icon-nativeHeight"] = 270 / 4; +            proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png"); +            doc["template-icon-view-video"] = new PrefetchProxy(iconVidView); +        } +        if (doc["template-icon-view-pdf"] === undefined) { +            const iconPdfView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); +            iconPdfView.isTemplateDoc = makeTemplate(iconPdfView, true, "icon_" + DocumentType.PDF); +            const proto = iconPdfView.proto as Doc; +            proto["icon-nativeWidth"] = 360 / 4; +            proto["icon-nativeHeight"] = 270 / 4; +            proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png"); +            doc["template-icon-view-pdf"] = new PrefetchProxy(iconPdfView); +        }          if (doc["template-icons"] === undefined) {              doc["template-icons"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, -            doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc, doc["template-icon-view-pdf"] as Doc], { title: "icon templates", _height: 75, system: true })); +            doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc, doc["template-icon-view-video"] as Doc, doc["template-icon-view-pdf"] as Doc], { title: "icon templates", _height: 75, system: true }));          } else {              const templateIconsDoc = Cast(doc["template-icons"], Doc, null);              const requiredTypes = [doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, @@ -367,7 +389,7 @@ export class CurrentUserUtils {              const textDoc = Docs.Create.TreeDocument([], {                  title: "Slide", _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true,                  allowOverlayDrop: true, treeViewType: "outline", _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, -                backgroundColor: "transparent", system: true, cloneFieldFilter: new List<string>(["system"]) +                backgroundColor: "white", system: true, cloneFieldFilter: new List<string>(["system"])              });              Doc.GetProto(textDoc).title = ComputedField.MakeFunction('self.text?.Text');              FormattedTextBox.SelectOnLoad = textDoc[Id]; @@ -425,8 +447,8 @@ export class CurrentUserUtils {              doc.emptyScreenshot = Docs.Create.ScreenshotDocument("empty screenshot", { _fitWidth: true, title: "empty screenshot", _width: 400, _height: 200, system: true, cloneFieldFilter: new List<string>(["system"]) });          }          if (doc.emptyWall === undefined) { -            doc.emptyWall = Docs.Create.ScreenshotDocument("", { _fitWidth: true, _width: 400, _height: 200, title: "screen snapshot", system: true, cloneFieldFilter: new List<string>(["system"]) }); -            (doc.emptyWall as Doc).videoWall = true; +            doc.emptyWall = Docs.Create.WebCamDocument("", { _width: 400, _height: 200, title: "recording", system: true, cloneFieldFilter: new List<string>(["system"]) }); +            (doc.emptyWall as Doc).recording = true;          }          if (doc.emptyAudio === undefined) {              doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { _width: 200, _height: 100, title: "audio recording", system: true, cloneFieldFilter: new List<string>(["system"]) }); @@ -1142,6 +1164,9 @@ export class CurrentUserUtils {      // Sharing sidebar is where shared documents are contained      static async setupSharingSidebar(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { +        if (doc.myPublishedDocs === undefined) { +            doc.myPublishedDocs = new List<Doc>(); +        }          if (doc.myLinkDatabase === undefined) {              let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId);              if (!linkDocs) { @@ -1319,9 +1344,6 @@ export class CurrentUserUtils {              // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)              doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme;          } -        if (doc.activeCollectionBackground === "white") { // temporary to avoid having to rebuild the databse for old accounts that have this set by default. -            doc.activeCollectionBackground = undefined; -        }          return doc;      } @@ -1502,6 +1524,7 @@ export class CurrentUserUtils {          return tbox;      } +    public static get DockedBtns() { return Cast(Doc.UserDoc().dockedBtns, Doc, null); }      public static get MySearchPanelDoc() { return Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null); }      public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); }      public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 0a00ab6e0..b6c28d2fe 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -5,6 +5,7 @@ import { Cast } from '../../fields/Types';  import { returnFalse } from '../../Utils';  import { DocumentType } from '../documents/DocumentTypes';  import { CollectionDockingView } from '../views/collections/CollectionDockingView'; +import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';  import { CollectionView } from '../views/collections/CollectionView';  import { LightboxView } from '../views/LightboxView';  import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView'; @@ -167,30 +168,43 @@ export class DocumentManager {          originalTarget = originalTarget ?? targetDoc;          const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;          const docView = getFirstDocView(targetDoc, originatingDoc); -        const wasHidden = targetDoc.hidden;  //  -        if (wasHidden) runInAction(() => targetDoc.hidden = false);  // if the target is hidden, un-hide it here. +        const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null); +        const resolvedTarget = targetDoc.type === DocumentType.MARKER ? annotatedDoc ?? targetDoc : targetDoc; // if target is a marker, then focus toggling should apply to the document it's on since the marker itself doesn't have a hidden field +        var wasHidden = resolvedTarget.hidden; +        if (wasHidden) { +            runInAction(() => { +                resolvedTarget.hidden = false;  // if the target is hidden, un-hide it here. +                docView?.props.bringToFront(resolvedTarget); +            }); +        }          const focusAndFinish = (didFocus: boolean) => { +            const finalTargetDoc = resolvedTarget;              if (originatingDoc?.isPushpin) {                  if (!didFocus && !wasHidden) {  // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal -                    targetDoc.hidden = !targetDoc.hidden; +                    finalTargetDoc.hidden = !finalTargetDoc.hidden;                  }              } else { -                targetDoc.hidden && (targetDoc.hidden = undefined); +                finalTargetDoc.hidden && (finalTargetDoc.hidden = undefined);                  !noSelect && docView?.select(false);              }              finished?.(); -            return false;          }; -        const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null); -        const annoContainerView = annotatedDoc && getFirstDocView(annotatedDoc); +        const annoContainerView = (!wasHidden || resolvedTarget !== annotatedDoc) && annotatedDoc && getFirstDocView(annotatedDoc);          const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;          const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined;          const targetDocContext = contextDoc || annotatedDoc;          const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) ||              (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above          const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView; -        if (!docView && annoContainerView) { -            annoContainerView.focus(targetDoc);  // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below +        if ((!docView && targetDoc.type !== DocumentType.MARKER) && annoContainerView) { +            if (annoContainerView.props.Document.layoutKey === "layout_icon") { +                annoContainerView.iconify(() => this.jumpToDocument( +                    targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc, +                    finished, originalTarget, noSelect, presZoom)); +                return; +            } else { +                annoContainerView.focus(targetDoc);  // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below +            }          }          if (focusView) {              !noSelect && Doc.linkFollowHighlight(focusView.rootDoc); //TODO:glr make this a setting in PresBox @@ -206,10 +220,17 @@ export class DocumentManager {                  createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc)));              } else {  // otherwise try to get a view of the context of the target                  if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first.. +                    wasHidden = wasHidden || targetDocContextView.rootDoc.hidden; +                    targetDocContextView.rootDoc.hidden = false; // make sure context isn't hidden                      targetDocContext._viewTransition = "transform 500ms";                      targetDocContextView.props.focus(targetDocContextView.rootDoc, {                          willZoom, afterFocus: async () => {                              targetDocContext._viewTransition = undefined; +                            if (targetDocContext.layoutKey === "layout_icon") { +                                targetDocContextView.iconify(() => this.jumpToDocument( +                                    targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc, +                                    finished, originalTarget, noSelect, presZoom)); +                            }                              return ViewAdjustment.doNothing;                          }                      }); @@ -220,27 +241,27 @@ export class DocumentManager {                          finished?.();                      } else { // no timecode means we need to find the context view and focus on our target                          const findView = (delay: number) => { -                            const retryDocView = getFirstDocView(targetDoc);  // test again for the target view snce we presumably created the context above by focusing on it -                            if (retryDocView) {   // we found the target in the context +                            const retryDocView = getFirstDocView(resolvedTarget);  // test again for the target view snce we presumably created the context above by focusing on it +                            if (retryDocView) {   // we found the target in the context.                                   Doc.linkFollowHighlight(retryDocView.rootDoc); -                                retryDocView.props.focus(targetDoc, { +                                retryDocView.focus(targetDoc, {                                      willZoom, afterFocus: (didFocus: boolean) =>                                          new Promise<ViewAdjustment>(res => {                                              !noSelect && focusAndFinish(didFocus);                                              res(ViewAdjustment.doNothing);                                          })                                  }); // focus on the target in the context -                            } else if (delay > 1500) { +                            } else if (delay > 1000) {                                  // we didn't find the target, so it must have moved out of the context.  Go back to just creating it.                                  if (closeContextIfNotFound) targetDocContextView.props.removeDocument?.(targetDocContextView.rootDoc);                                  if (targetDoc.layout) { // there will no layout for a TEXTANCHOR type document                                      createViewFunc(Doc.BrushDoc(targetDoc), finished); //  create a new view of the target                                  }                              } else { -                                setTimeout(() => findView(delay + 250), 250); +                                setTimeout(() => findView(delay + 200), 200);                              }                          }; -                        findView(0); +                        setTimeout(() => findView(0), 0);                      }                  } else {  // there's no context view so we need to create one first and try again when that finishes                      const finishFunc = () => this.jumpToDocument(targetDoc, true, createViewFunc, docContext, linkDoc, true /* if we don't find the target, we want to get rid of the context just created */, undefined, finished, originalTarget); @@ -250,12 +271,12 @@ export class DocumentManager {              }          }      } -  } -ScriptingGlobals.add(function DocFocusOrOpen(doc: any) { -    const dv = DocumentManager.Instance.getDocumentView(doc); -    if (dv && dv.props.Document === doc) { -        dv.props.focus(doc, { willZoom: true }); +export function DocFocusOrOpen(doc: any, collectionDoc?: Doc) { +    const cv = collectionDoc && DocumentManager.Instance.getDocumentView(collectionDoc); +    const dv = DocumentManager.Instance.getDocumentView(doc, (cv?.ComponentView as CollectionFreeFormView)?.props.CollectionView); +    if (dv && Doc.AreProtosEqual(dv.props.Document, doc)) { +        dv.props.focus(dv.props.Document, { willZoom: true });          Doc.linkFollowHighlight(dv?.props.Document, false);      }      else { @@ -264,4 +285,5 @@ ScriptingGlobals.add(function DocFocusOrOpen(doc: any) {          CollectionDockingView.AddSplit(showDoc === Doc.GetProto(showDoc) ? Doc.MakeAlias(showDoc) : showDoc, "right") && context &&              setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc));      } -});
\ No newline at end of file +} +ScriptingGlobals.add(DocFocusOrOpen);
\ No newline at end of file diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index db9986a41..8ac28bc89 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,3 +1,4 @@ +import { extend } from "lodash";  import { action } from "mobx";  import { DateField } from "../../fields/DateField";  import { Doc, Field, Opt } from "../../fields/Doc"; @@ -56,12 +57,7 @@ export function SetupDrag(              if (e.shiftKey) {                  e.persist();                  const dragDoc = await docFunc(); -                dragDoc && DragManager.StartWindowDrag?.({ -                    pageX: e.pageX, -                    pageY: e.pageY, -                    preventDefault: emptyFunction, -                    button: 0 -                }, [dragDoc]); +                dragDoc && DragManager.StartWindowDrag?.(e, [dragDoc]);              } else {                  document.addEventListener("pointermove", onRowMove);                  document.addEventListener("pointerup", onRowUp); @@ -74,7 +70,8 @@ export function SetupDrag(  export namespace DragManager {      let dragDiv: HTMLDivElement;      let dragLabel: HTMLDivElement; -    export let StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined; +    export let StartWindowDrag: Opt<((e: { pageX: number, pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => void)>; +    export let CompleteWindowDrag: Opt<(aborted: boolean) => void>;      export function Root() {          const root = document.getElementById("root"); @@ -387,8 +384,8 @@ export namespace DragManager {                  }              }              const rect = ele.getBoundingClientRect(); -            const scaleX = rect.width / ele.offsetWidth; -            const scaleY = ele.offsetHeight ? rect.height / ele.offsetHeight : scaleX; +            const scaleX = rect.width / (ele.offsetWidth || rect.width); +            const scaleY = ele.offsetHeight ? rect.height / (ele.offsetHeight || rect.height) : scaleX;              elesCont.left = Math.min(rect.left, elesCont.left);              elesCont.top = Math.min(rect.top, elesCont.top); @@ -412,7 +409,7 @@ export namespace DragManager {                  Array.from(pdfBox).filter(pb => pb.width && pb.height).map((pb, i) => pb.getContext('2d')!.drawImage(pdfBoxSrc[i], 0, 0));              }              [dragElement, ...Array.from(dragElement.getElementsByTagName('*'))].forEach(ele => -                ele.hasAttribute("style") && ((ele as any).style.pointerEvents = "none")); +                (ele as any).style && ((ele as any).style.pointerEvents = "none"));              dragDiv.appendChild(dragElement);              if (dragElement !== ele) { @@ -462,19 +459,15 @@ export namespace DragManager {              if (dragData instanceof DocumentDragData) {                  dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : dragData.defaultDropAction;              } -            if (e?.shiftKey && dragData.draggedDocuments.length === 1) { +            if (((e.target as any)?.className === "lm_tabs" || e?.shiftKey) && dragData.draggedDocuments.length === 1) {                  dragData.dropAction = dragData.userDropAction || "same"; -                if (dragData.dropAction === "move") { -                    dragData.removeDocument?.(dragData.draggedDocuments[0]); -                }                  AbortDrag();                  await finishDrag?.(new DragCompleteEvent(true, dragData)); -                DragManager.StartWindowDrag?.({ -                    pageX: e.pageX, -                    pageY: e.pageY, -                    preventDefault: emptyFunction, -                    button: 0 -                }, dragData.droppedDocuments); +                DragManager.StartWindowDrag?.(e, dragData.droppedDocuments, (aborted) => { +                    if (!aborted && (dragData.dropAction === "move" || dragData.dropAction === "same")) { +                        dragData.removeDocument?.(dragData.draggedDocuments[0]); +                    } +                });              }              const target = document.elementFromPoint(e.x, e.y); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index df2c02a8d..9445533dc 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -29,6 +29,7 @@ export class LinkManager {      public static currentLink: Opt<Doc>;      public static get Instance() { return LinkManager._instance; }      public static addLinkDB = (linkDb: any) => LinkManager.userLinkDBs.push(linkDb); +    public static AutoKeywords = "keywords:Usages";      static links: Doc[] = [];      constructor() {          LinkManager._instance = this; @@ -149,9 +150,11 @@ export class LinkManager {      public getRelatedGroupedLinks(anchor: Doc): Map<string, Array<Doc>> {          const anchorGroups = new Map<string, Array<Doc>>();          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]); +            if (link.linkRelationship && link.linkRelationship !== "-ungrouped-") { +                const relation = StrCast(link.linkRelationship); +                const anchorRelation = relation.indexOf(":") !== -1 ? relation.split(":")[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation; +                const group = anchorGroups.get(anchorRelation); +                anchorGroups.set(anchorRelation, group ? [...group, link] : [link]);              } else {                  // if link is in no groups then put it in default group                  const group = anchorGroups.get("*"); @@ -221,7 +224,9 @@ export class LinkManager {          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 ? (sourceDoc.isPushpin ? linkDocList : [linkDocList[0]]) : []; +        const followLinks = sourceDoc.isPushpin ? linkDocList : linkDocList.slice(0, 1); +        var count = 0; +        const allFinished = () => ++count === followLinks.length && finished?.();          followLinks.forEach(async linkDoc => {              if (linkDoc) {                  const target = (sourceDoc === linkDoc.anchor1 ? linkDoc.anchor2 : sourceDoc === linkDoc.anchor2 ? linkDoc.anchor1 : @@ -232,20 +237,20 @@ export class LinkManager {                          const tour = DocListCast(target[fieldKey]).reverse();                          LightboxView.SetLightboxDoc(currentContext, undefined, tour);                          setTimeout(LightboxView.Next); -                        finished?.(); +                        allFinished();                      } else {                          const containerAnnoDoc = Cast(target.annotationOn, Doc, null);                          const containerDoc = containerAnnoDoc || target;                          const containerDocContext = Cast(containerDoc?.context, Doc, null);                          const targetContext = LightboxView.LightboxDoc ? containerAnnoDoc || containerDocContext : containerDocContext;                          const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined; -                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetNavContext, linkDoc, undefined, sourceDoc, finished); +                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetNavContext, linkDoc, undefined, sourceDoc, allFinished);                      }                  } else { -                    finished?.(); +                    allFinished();                  }              } else { -                finished?.(); +                allFinished();              }          });      } | 
