aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/DocumentManager.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util/DocumentManager.ts')
-rw-r--r--src/client/util/DocumentManager.ts138
1 files changed, 69 insertions, 69 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 7cc8afaa6..f730d17fe 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,6 +1,7 @@
-import { action, computed, observable, ObservableSet, observe } from 'mobx';
+import { Howl } from 'howler';
+import { action, computed, makeObservable, observable, ObservableSet, observe } from 'mobx';
import { Doc, DocListCast, Opt } from '../../fields/Doc';
-import { AclAdmin, AclEdit, Animation } from '../../fields/DocSymbols';
+import { AclAdmin, AclEdit, Animation, DocData } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
import { listSpec } from '../../fields/Schema';
import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
@@ -10,21 +11,27 @@ import { CollectionViewType } from '../documents/DocumentTypes';
import { CollectionDockingView } from '../views/collections/CollectionDockingView';
import { TabDocView } from '../views/collections/TabDocView';
import { LightboxView } from '../views/LightboxView';
-import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView';
+import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView';
+import { FocusViewOptions } from '../views/nodes/FieldView';
import { KeyValueBox } from '../views/nodes/KeyValueBox';
import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
import { PresBox } from '../views/nodes/trails';
import { ScriptingGlobals } from './ScriptingGlobals';
import { SelectionManager } from './SelectionManager';
-const { Howl } = require('howler');
export class DocumentManager {
+ private static _instance: DocumentManager;
+ public static get Instance(): DocumentManager {
+ return this._instance || (this._instance = new this());
+ }
+
//global holds all of the nodes (regardless of which collection they're in)
@observable _documentViews = new Set<DocumentView>();
- @observable public LinkAnchorBoxViews: DocumentView[] = [];
- @observable public LinkedDocumentViews: { a: DocumentView; b: DocumentView; l: Doc }[] = [];
+ @observable.shallow public CurrentlyLoading: Doc[] = [];
+ @observable.shallow public LinkAnchorBoxViews: DocumentView[] = [];
+ @observable.shallow public LinkedDocumentViews: { a: DocumentView; b: DocumentView; l: Doc }[] = [];
@computed public get DocumentViews() {
- return Array.from(this._documentViews).filter(view => !(view.ComponentView instanceof KeyValueBox) && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(view.docViewPath)));
+ return Array.from(this._documentViews).filter(view => !(view.ComponentView instanceof KeyValueBox) && (!LightboxView.LightboxDoc || LightboxView.Contains(view)));
}
public AddDocumentView(dv: DocumentView) {
this._documentViews.add(dv);
@@ -33,24 +40,19 @@ export class DocumentManager {
this._documentViews.delete(dv);
}
- private static _instance: DocumentManager;
- public static get Instance(): DocumentManager {
- return this._instance || (this._instance = new this());
- }
-
//private constructor so no other class can create a nodemanager
private constructor() {
- if (!Doc.CurrentlyLoading) Doc.CurrentlyLoading = [];
- observe(Doc.CurrentlyLoading, change => {
+ makeObservable(this);
+ observe(this.CurrentlyLoading, change => {
// watch CurrentlyLoading-- when something is loaded, it's removed from the list and we have to update its icon if it were iconified since LoadingBox icons are different than the media they become
switch (change.type as any) {
case 'update':
break;
case 'remove':
- // DocumentManager.Instance.getAllDocumentViews(change as any).forEach(dv => StrCast(dv.rootDoc.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify()));
+ // DocumentManager.Instance.getAllDocumentViews(change as any).forEach(dv => StrCast(dv.Document.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify()));
break;
case 'splice':
- (change as any).removed.forEach((doc: Doc) => DocumentManager.Instance.getAllDocumentViews(doc).forEach(dv => StrCast(dv.rootDoc.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify())));
+ (change as any).removed.forEach((doc: Doc) => DocumentManager.Instance.getAllDocumentViews(doc).forEach(dv => StrCast(dv.Document.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify())));
break;
}
});
@@ -71,7 +73,7 @@ export class DocumentManager {
return false;
};
callAddViewFuncs = (view: DocumentView) => {
- const callFuncs = this._viewRenderedCbs.filter(vc => vc.doc === view.rootDoc);
+ const callFuncs = this._viewRenderedCbs.filter(vc => vc.doc === view.Document);
if (callFuncs.length) {
this._viewRenderedCbs = this._viewRenderedCbs.filter(vc => !callFuncs.includes(vc));
const intTimer = setInterval(
@@ -88,11 +90,11 @@ export class DocumentManager {
@action
public AddView = (view: DocumentView) => {
- if (view.props.LayoutTemplateString?.includes(KeyValueBox.name)) return;
- if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
- const viewAnchorIndex = view.props.LayoutTemplateString.includes('link_anchor_2') ? 'link_anchor_2' : 'link_anchor_1';
- const link = view.rootDoc;
- this.LinkAnchorBoxViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)).forEach(otherView =>
+ if (view._props.LayoutTemplateString?.includes(KeyValueBox.name)) return;
+ if (view._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
+ const viewAnchorIndex = view._props.LayoutTemplateString.includes('link_anchor_2') ? 'link_anchor_2' : 'link_anchor_1';
+ const link = view.Document;
+ this.LinkAnchorBoxViews?.filter(dv => Doc.AreProtosEqual(dv.Document, link) && !dv._props.LayoutTemplateString?.includes(viewAnchorIndex)).forEach(otherView =>
this.LinkedDocumentViews.push({
a: viewAnchorIndex === 'link_anchor_2' ? otherView : view,
b: viewAnchorIndex === 'link_anchor_2' ? view : otherView,
@@ -115,7 +117,7 @@ export class DocumentManager {
})
);
- if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
+ if (view._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
const index = this.LinkAnchorBoxViews.indexOf(view);
this.LinkAnchorBoxViews.splice(index, 1);
} else {
@@ -128,13 +130,13 @@ export class DocumentManager {
public getDocumentViewsById(id: string) {
const toReturn: DocumentView[] = [];
DocumentManager.Instance.DocumentViews.forEach(view => {
- if (view.rootDoc[Id] === id) {
+ if (view.Document[Id] === id) {
toReturn.push(view);
}
});
if (toReturn.length === 0) {
DocumentManager.Instance.DocumentViews.forEach(view => {
- if (Doc.GetProto(view.rootDoc)?.[Id] === id) {
+ if (view.Document[DocData]?.[Id] === id) {
toReturn.push(view);
}
});
@@ -152,42 +154,42 @@ export class DocumentManager {
return passes.reduce(
(toReturn, pass) =>
toReturn ??
- docViewArray.filter(view => view.rootDoc === target).find(view => !pass || view.props.docViewPath().lastElement() === preferredCollection) ??
- docViewArray.filter(view => Doc.AreProtosEqual(view.rootDoc, target)).find(view => !pass || view.props.docViewPath().lastElement() === preferredCollection),
+ docViewArray.filter(view => view.Document === target).find(view => !pass || view.containerViewPath?.().lastElement() === preferredCollection) ??
+ docViewArray.filter(view => Doc.AreProtosEqual(view.Document, target)).find(view => !pass || view.containerViewPath?.().lastElement() === preferredCollection),
undefined as Opt<DocumentView>
);
}
public getLightboxDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
const views: DocumentView[] = [];
- DocumentManager.Instance.DocumentViews.forEach(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view));
- return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /*&& view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse*/) || (views.length ? views[0] : undefined);
+ DocumentManager.Instance.DocumentViews.forEach(view => LightboxView.Contains(view) && Doc.AreProtosEqual(view.Document, toFind) && views.push(view));
+ 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 getFirstDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
if (LightboxView.LightboxDoc) return DocumentManager.Instance.getLightboxDocumentView(toFind, originatingDoc);
- const views = this.getDocumentViews(toFind); //.filter(view => view.rootDoc !== originatingDoc);
- return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /*&& view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse*/) || (views.length ? views[0] : undefined);
+ const views = this.getDocumentViews(toFind); //.filter(view => view.Document !== originatingDoc);
+ 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(toFindIn: Doc): DocumentView[] {
const toFind =
// Array.from(DocumentManager.Instance.DocumentViews).find(
// dv =>
- // ((dv.rootDoc.data as any)?.url?.href && (dv.rootDoc.data as any)?.url?.href === (toFindIn.data as any)?.url?.href) ||
- // ((DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href && (DocCast(dv.rootDoc.annotationOn)?.data as any)?.url?.href === (DocCast(toFindIn.annotationOn)?.data as any)?.url?.href)
- // )?.rootDoc ??
+ // ((dv.Document.data as any)?.url?.href && (dv.Document.data as any)?.url?.href === (toFindIn.data as any)?.url?.href) ||
+ // ((DocCast(dv.Document.annotationOn)?.data as any)?.url?.href && (DocCast(dv.Document.annotationOn)?.data as any)?.url?.href === (DocCast(toFindIn.annotationOn)?.data as any)?.url?.href)
+ // )?.Document ??
toFindIn;
const toReturn: DocumentView[] = [];
- const docViews = DocumentManager.Instance.DocumentViews.filter(view => !LightboxView.IsLightboxDocView(view.docViewPath));
- const lightViews = DocumentManager.Instance.DocumentViews.filter(view => LightboxView.IsLightboxDocView(view.docViewPath));
+ const docViews = DocumentManager.Instance.DocumentViews.filter(view => !LightboxView.Contains(view));
+ const lightViews = DocumentManager.Instance.DocumentViews.filter(view => LightboxView.Contains(view));
// heuristic to return the "best" documents first:
// choose a document in the lightbox first
// choose an exact match over an embedding match
- lightViews.map(view => view.rootDoc === toFind && toReturn.push(view));
- lightViews.map(view => view.rootDoc !== toFind && Doc.AreProtosEqual(view.rootDoc, toFind) && toReturn.push(view));
- docViews.map(view => view.rootDoc === toFind && toReturn.push(view));
- docViews.map(view => view.rootDoc !== toFind && Doc.AreProtosEqual(view.rootDoc, toFind) && toReturn.push(view));
+ lightViews.map(view => view.Document === toFind && toReturn.push(view));
+ lightViews.map(view => view.Document !== toFind && Doc.AreProtosEqual(view.Document, toFind) && toReturn.push(view));
+ docViews.map(view => view.Document === toFind && toReturn.push(view));
+ docViews.map(view => view.Document !== toFind && Doc.AreProtosEqual(view.Document, toFind) && toReturn.push(view));
return toReturn;
}
@@ -223,7 +225,7 @@ export class DocumentManager {
}
public static removeOverlayViews() {
- DocumentManager._overlayViews?.forEach(action(view => (view.textHtmlOverlay = undefined)));
+ DocumentManager._overlayViews?.forEach(view => view.setTextHtmlOverlay(undefined, undefined));
DocumentManager._overlayViews?.clear();
}
static _overlayViews = new ObservableSet<DocumentView>();
@@ -235,12 +237,12 @@ export class DocumentManager {
// shows a documentView by:
// traverses down through the viewPath of contexts to the view:
// focusing on each context
- public showDocumentView = async (targetDocView: DocumentView, options: DocFocusOptions) => {
- const docViewPath = targetDocView.docViewPath.slice();
+ public showDocumentView = async (targetDocView: DocumentView, options: FocusViewOptions) => {
+ const docViewPath = [...(targetDocView.containerViewPath?.() ?? []), targetDocView];
let rootContextView = docViewPath.shift();
await (rootContextView && this.focusViewsInPath(rootContextView, options, async () => ({ childDocView: docViewPath.shift(), viewSpec: undefined, focused: false })));
- if (options.toggleTarget && (!options.didMove || targetDocView.rootDoc.hidden)) targetDocView.rootDoc.hidden = !targetDocView.rootDoc.hidden;
- else if (options.openLocation?.startsWith(OpenWhere.toggle) && !options.didMove && rootContextView) DocumentViewInternal.addDocTabFunc(rootContextView.rootDoc, options.openLocation);
+ if (options.toggleTarget && (!options.didMove || targetDocView.Document.hidden)) targetDocView.Document.hidden = !targetDocView.Document.hidden;
+ else if (options.openLocation?.startsWith(OpenWhere.toggle) && !options.didMove && rootContextView) DocumentViewInternal.addDocTabFunc(rootContextView.Document, options.openLocation);
};
// shows a document by first:
@@ -251,7 +253,7 @@ export class DocumentManager {
// and finally restoring the targetDoc to the viewSpec specified by the last document which may either be the targetDoc, or a viewSpec that describes the targetDoc configuration
public showDocument = async (
targetDoc: Doc, // document to display
- options: DocFocusOptions, // options for how to navigate to target
+ options: FocusViewOptions, // options for how to navigate to target
finished?: (changed: boolean) => void // func called after focusing on target with flag indicating whether anything needed to be done.
) => {
Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, targetDoc);
@@ -273,14 +275,14 @@ export class DocumentManager {
// even if we found the document view, if the target is a lightbox, we try to open it in the lightbox to preserve lightbox semantics (eg, there's only one active doc in the lightbox)
const target = DocCast(targetDoc.annotationOn, targetDoc);
const contextView = this.getDocumentView(DocCast(target.embedContainer));
- if (contextView?.docView?._componentView?.addDocTab?.(target, OpenWhere.lightbox)) {
+ if (contextView?.ComponentView?.addDocTab?.(target, OpenWhere.lightbox)) {
await new Promise<void>(waitres => setTimeout(() => waitres()));
}
}
docContextPath.shift();
const childViewIterator = async (docView: DocumentView) => {
const innerDoc = docContextPath.shift();
- return { focused: false, viewSpec: innerDoc, childDocView: innerDoc && !innerDoc.layout_unrendered ? (await docView.ComponentView?.getView?.(innerDoc)) ?? this.getDocumentView(innerDoc) : undefined };
+ return { focused: false, viewSpec: innerDoc, childDocView: innerDoc && !innerDoc.layout_unrendered ? (await docView.ComponentView?.getView?.(innerDoc, options)) ?? this.getDocumentView(innerDoc) : undefined };
};
if (rootContextView) {
@@ -292,58 +294,56 @@ export class DocumentManager {
focusViewsInPath = async (
docView: DocumentView, //
- options: DocFocusOptions,
+ options: FocusViewOptions,
iterator: (docView: DocumentView) => Promise<{ viewSpec: Opt<Doc>; childDocView: Opt<DocumentView>; focused: boolean }>
) => {
let contextView: DocumentView | undefined; // view containing context that contains target
let focused = false;
while (true) {
- if (docView.rootDoc.layout_fieldKey === 'layout_icon') {
+ if (docView.Document.layout_fieldKey === 'layout_icon') {
await new Promise<void>(res => docView.iconify(res));
options.didMove = true;
}
- const nextFocus = docView.props.focus(docView.rootDoc, options); // focus the view within its container
+ const nextFocus = docView._props.focus(docView.Document, options); // focus the view within its container
focused = focused || (nextFocus === undefined ? false : true); // keep track of whether focusing on a view needed to actually change anything
const { childDocView, viewSpec } = await iterator(docView);
- if (!childDocView) return { viewSpec: options.anchorDoc ?? viewSpec ?? docView.rootDoc, docView, contextView, focused };
- contextView = docView;
+ if (!childDocView) return { viewSpec: options.anchorDoc ?? viewSpec ?? docView.Document, docView, contextView, focused };
+ contextView = options.anchorDoc?.layout_unrendered && !childDocView.Document.layout_unrendered ? childDocView : docView;
docView = childDocView;
}
};
@action
- restoreDocView(viewSpec: Opt<Doc>, docView: DocumentView, options: DocFocusOptions, contextView: Opt<DocumentView>, targetDoc: Doc) {
+ restoreDocView(viewSpec: Opt<Doc>, docView: DocumentView, options: FocusViewOptions, contextView: Opt<DocumentView>, targetDoc: Doc) {
if (viewSpec && docView) {
//if (docView.ComponentView instanceof FormattedTextBox)
- //viewSpec !== docView.rootDoc &&
+ //viewSpec !== docView.Document &&
docView.ComponentView?.focus?.(viewSpec, options);
PresBox.restoreTargetDocView(docView, viewSpec, options.zoomTime ?? 500);
- Doc.linkFollowHighlight(viewSpec ? [docView.rootDoc, viewSpec] : docView.rootDoc, undefined, options.effect);
- if (options.playMedia) docView.ComponentView?.playFrom?.(NumCast(docView.rootDoc._layout_currentTimecode));
- if (options.playAudio) DocumentManager.playAudioAnno(docView.rootDoc);
- if (options.toggleTarget && (!options.didMove || docView.rootDoc.hidden)) docView.rootDoc.hidden = !docView.rootDoc.hidden;
- if (options.effect) docView.rootDoc[Animation] = options.effect;
+ Doc.linkFollowHighlight(viewSpec ? [docView.Document, viewSpec] : docView.Document, undefined, options.effect);
+ if (options.playMedia) docView.ComponentView?.playFrom?.(NumCast(docView.Document._layout_currentTimecode));
+ if (options.playAudio) DocumentManager.playAudioAnno(docView.Document);
+ if (options.toggleTarget && (!options.didMove || docView.Document.hidden)) docView.Document.hidden = !docView.Document.hidden;
+ if (options.effect) docView.Document[Animation] = options.effect;
- if (options.zoomTextSelections && Doc.UnhighlightTimer && contextView && viewSpec.text_html) {
+ if (options.zoomTextSelections && Doc.UnhighlightTimer && contextView && targetDoc.text_html) {
// if the docView is a text anchor, the contextView is the PDF/Web/Text doc
- contextView.htmlOverlayEffect = StrCast(options?.effect?.presentation_effect, StrCast(options?.effect?.followLinkAnimEffect));
- contextView.textHtmlOverlay = StrCast(targetDoc.text_html);
+ contextView.setTextHtmlOverlay(StrCast(targetDoc.text_html), options.effect);
DocumentManager._overlayViews.add(contextView);
}
Doc.AddUnHighlightWatcher(() => {
- docView.rootDoc[Animation] = undefined;
+ docView.Document[Animation] = undefined;
DocumentManager.removeOverlayViews();
- contextView && (contextView.htmlOverlayEffect = '');
});
}
}
}
-export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCentered: true, zoomScale: 0, openLocation: OpenWhere.toggleRight }, containingDoc?: Doc) {
+export function DocFocusOrOpen(doc: Doc, options: FocusViewOptions = { willZoomCentered: true, zoomScale: 0, openLocation: OpenWhere.toggleRight }, containingDoc?: Doc) {
const func = () => {
const cv = DocumentManager.Instance.getDocumentView(containingDoc);
const dv = DocumentManager.Instance.getDocumentView(doc, cv);
- if (dv && (!containingDoc || dv.props.docViewPath().lastElement()?.Document === containingDoc)) {
- DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.rootDoc));
+ if (dv && (!containingDoc || dv.containerViewPath?.().lastElement()?.Document === containingDoc)) {
+ DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.Document));
} else {
const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc));
const showDoc = !Doc.IsSystem(container) && !cv ? container : doc;
@@ -351,7 +351,7 @@ export function DocFocusOrOpen(doc: Doc, options: DocFocusOptions = { willZoomCe
DocumentManager.Instance.showDocument(showDoc, options, () => DocumentManager.Instance.showDocument(doc, { ...options, openLocation: undefined })).then(() => {
const cv = DocumentManager.Instance.getDocumentView(containingDoc);
const dv = DocumentManager.Instance.getDocumentView(doc, cv);
- dv && Doc.linkFollowHighlight(dv.rootDoc);
+ dv && Doc.linkFollowHighlight(dv.Document);
});
}
};