aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/LinkManager.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util/LinkManager.ts')
-rw-r--r--src/client/util/LinkManager.ts204
1 files changed, 106 insertions, 98 deletions
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 7a12a8580..ce422f849 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,13 +1,16 @@
import { action, observable, observe } from 'mobx';
import { computedFn } from 'mobx-utils';
-import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
+import { Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
+import { DirectLinks } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
import { ProxyField } from '../../fields/Proxy';
-import { Cast, StrCast } from '../../fields/Types';
+import { Cast, DocCast, PromiseValue, StrCast } from '../../fields/Types';
+import { DocServer } from '../DocServer';
+import { ScriptingGlobals } from './ScriptingGlobals';
/*
* link doc:
- * - anchor1: doc
- * - anchor2: doc
+ * - link_anchor_1: doc
+ * - link_anchor_2: doc
*
* group doc:
* - type: string representing the group type/name/category
@@ -19,85 +22,100 @@ import { Cast, StrCast } from '../../fields/Types';
export class LinkManager {
@observable static _instance: LinkManager;
@observable static userLinkDBs: Doc[] = [];
- public static currentLink: Opt<Doc>;
+ @observable public static currentLink: Opt<Doc>;
+ @observable public static currentLinkAnchor: Opt<Doc>;
public static get Instance() {
return LinkManager._instance;
}
- public static addLinkDB = (linkDb: any) => LinkManager.userLinkDBs.push(linkDb);
+
+ public static Links(doc: Doc | undefined) {
+ return doc ? LinkManager.Instance.getAllRelatedLinks(doc) : [];
+ }
+ public static addLinkDB = async (linkDb: any) => {
+ await Promise.all(
+ ((await DocListCastAsync(linkDb.data)) ?? []).map(link =>
+ // makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager
+ [PromiseValue(link?.link_anchor_1), PromiseValue(link?.link_anchor_2)]
+ )
+ );
+ LinkManager.userLinkDBs.push(linkDb);
+ };
public static AutoKeywords = 'keywords:Usages';
- static links: Doc[] = [];
+ static _links: Doc[] = [];
constructor() {
LinkManager._instance = this;
- this.createLinkrelationshipLists();
+ this.createlink_relationshipLists();
LinkManager.userLinkDBs = [];
const addLinkToDoc = (link: Doc) => {
- const a1Prom = link?.anchor1;
- const a2Prom = link?.anchor2;
- Promise.all([a1Prom, a2Prom]).then(all => {
- const a1 = all[0];
- const a2 = all[1];
- const a1ProtoProm = (link?.anchor1 as Doc)?.proto;
- const a2ProtoProm = (link?.anchor2 as Doc)?.proto;
- Promise.all([a1ProtoProm, a2ProtoProm]).then(
- action(all => {
- if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
- Doc.GetProto(a1)[DirectLinksSym].add(link);
- Doc.GetProto(a2)[DirectLinksSym].add(link);
- Doc.GetProto(link)[DirectLinksSym].add(link);
- }
- })
- );
+ Promise.all([link]).then(linkdoc => {
+ const link = DocCast(linkdoc[0]);
+ Promise.all([link.proto]).then(linkproto => {
+ Promise.all([link.link_anchor_1, link.link_anchor_2]).then(linkdata => {
+ const a1 = DocCast(linkdata[0]);
+ const a2 = DocCast(linkdata[1]);
+ a1 &&
+ a2 &&
+ Promise.all([Doc.GetProto(a1), Doc.GetProto(a2)]).then(
+ action(protos => {
+ (protos[0] as Doc)?.[DirectLinks].add(link);
+ (protos[1] as Doc)?.[DirectLinks].add(link);
+ })
+ );
+ });
+ });
});
};
const remLinkFromDoc = (link: Doc) => {
- const a1 = link?.anchor1;
- const a2 = link?.anchor2;
+ const a1 = link?.link_anchor_1;
+ const a2 = link?.link_anchor_2;
Promise.all([a1, a2]).then(
action(() => {
if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
- Doc.GetProto(a1)[DirectLinksSym].delete(link);
- Doc.GetProto(a2)[DirectLinksSym].delete(link);
- Doc.GetProto(link)[DirectLinksSym].delete(link);
+ Doc.GetProto(a1)[DirectLinks].delete(link);
+ Doc.GetProto(a2)[DirectLinks].delete(link);
+ Doc.GetProto(link)[DirectLinks].delete(link);
}
})
);
};
const watchUserLinkDB = (userLinkDBDoc: Doc) => {
- LinkManager.links.push(...DocListCast(userLinkDBDoc.data));
- const toRealField = (field: Field) => (field instanceof ProxyField ? field.value() : field); // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields
- observe(
- userLinkDBDoc.data as Doc,
- change => {
- // observe pushes/splices on a user link DB 'data' field (should only happen for local changes)
- switch (change.type as any) {
- case 'splice':
- (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link)));
- (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link)));
- break;
- case 'update': //let oldValue = change.oldValue;
- }
- },
- true
- );
- observe(
- userLinkDBDoc,
- 'data', // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link)
- change => {
- switch (change.type as any) {
- case 'update':
- Promise.all([...((change.oldValue as any as Doc[]) || []), ...((change.newValue as any as Doc[]) || [])]).then(doclist => {
- const oldDocs = doclist.slice(0, ((change.oldValue as any as Doc[]) || []).length);
- const newDocs = doclist.slice(((change.oldValue as any as Doc[]) || []).length, doclist.length);
+ LinkManager._links.push(...DocListCast(userLinkDBDoc.data));
+ const toRealField = (field: Field) => (field instanceof ProxyField ? field.value : field); // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields
+ if (userLinkDBDoc.data) {
+ observe(
+ userLinkDBDoc.data,
+ change => {
+ // observe pushes/splices on a user link DB 'data' field (should only happen for local changes)
+ switch (change.type as any) {
+ case 'splice':
+ (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link)));
+ (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link)));
+ break;
+ case 'update': //let oldValue = change.oldValue;
+ }
+ },
+ true
+ );
+ observe(
+ userLinkDBDoc,
+ 'data', // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link)
+ change => {
+ switch (change.type as any) {
+ case 'update':
+ Promise.all([...((change.oldValue as any as Doc[]) || []), ...((change.newValue as any as Doc[]) || [])]).then(doclist => {
+ const oldDocs = doclist.slice(0, ((change.oldValue as any as Doc[]) || []).length);
+ const newDocs = doclist.slice(((change.oldValue as any as Doc[]) || []).length, doclist.length);
- const added = newDocs?.filter(link => !(oldDocs || []).includes(link));
- const removed = oldDocs?.filter(link => !(newDocs || []).includes(link));
- added?.forEach((link: any) => addLinkToDoc(toRealField(link)));
- removed?.forEach((link: any) => remLinkFromDoc(toRealField(link)));
- });
- }
- },
- true
- );
+ const added = newDocs?.filter(link => !(oldDocs || []).includes(link));
+ const removed = oldDocs?.filter(link => !(newDocs || []).includes(link));
+ added?.forEach((link: any) => addLinkToDoc(toRealField(link)));
+ removed?.forEach((link: any) => remLinkFromDoc(toRealField(link)));
+ });
+ }
+ },
+ true
+ );
+ }
};
observe(
LinkManager.userLinkDBs,
@@ -112,25 +130,19 @@ export class LinkManager {
true
);
LinkManager.addLinkDB(Doc.LinkDBDoc());
- DocListCastAsync(Doc.LinkDBDoc()?.data).then(dblist =>
- dblist?.forEach(async link => {
- // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
- const a1 = await Cast(link?.anchor1, Doc, null);
- const a2 = await Cast(link?.anchor2, Doc, null);
- })
- );
}
- public createLinkrelationshipLists = () => {
+ public createlink_relationshipLists = () => {
//create new lists for link relations and their associated colors if the lists don't already exist
- !Doc.UserDoc().linkRelationshipList && (Doc.UserDoc().linkRelationshipList = new List<string>());
- !Doc.UserDoc().linkColorList && (Doc.UserDoc().linkColorList = new List<string>());
- !Doc.UserDoc().linkRelationshipSizes && (Doc.UserDoc().linkRelationshipSizes = new List<number>());
+ !Doc.UserDoc().link_relationshipList && (Doc.UserDoc().link_relationshipList = new List<string>());
+ !Doc.UserDoc().link_ColorList && (Doc.UserDoc().link_ColorList = new List<string>());
+ !Doc.UserDoc().link_relationshipSizes && (Doc.UserDoc().link_relationshipSizes = new List<number>());
};
public addLink(linkDoc: Doc, checkExists = false) {
if (!checkExists || !DocListCast(Doc.LinkDBDoc().data).includes(linkDoc)) {
Doc.AddDocToList(Doc.LinkDBDoc(), 'data', linkDoc);
+ setTimeout(DocServer.UPDATE_SERVER_CACHE, 100);
}
}
public deleteLink(linkDoc: Doc) {
@@ -144,37 +156,27 @@ export class LinkManager {
return this.relatedLinker(anchor);
} // finds all links that contain the given anchor
public getAllDirectLinks(anchor: Doc): Doc[] {
- // FIXME:glr Why is Doc undefined?
- if (Doc.GetProto(anchor)[DirectLinksSym]) {
- return Array.from(Doc.GetProto(anchor)[DirectLinksSym]);
- } else {
- return [];
- }
+ return Array.from(Doc.GetProto(anchor)[DirectLinks] ?? []);
} // finds all links that contain the given anchor
relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
- const lfield = Doc.LayoutFieldKey(anchor);
if (!anchor || anchor instanceof Promise || Doc.GetProto(anchor) instanceof Promise) {
console.log('WAITING FOR DOC/PROTO IN LINKMANAGER');
return [];
}
- const dirLinks = Doc.GetProto(anchor)[DirectLinksSym];
- const annos = DocListCast(anchor[lfield + '-annotations']);
- const timelineAnnos = DocListCast(anchor[lfield + '-annotations-timeline']);
- if (!annos || !timelineAnnos) {
- debugger;
- }
- const related = [...annos, ...timelineAnnos].reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], Array.from(dirLinks).slice());
- return related;
+ const dirLinks = Doc.GetProto(anchor)[DirectLinks];
+ const annos = DocListCast(anchor[Doc.LayoutFieldKey(anchor) + '_annotations']);
+ if (!annos) debugger;
+ return annos.reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], Array.from(dirLinks).slice());
}, true);
// returns map of group type to anchor's links in that group type
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 relation = StrCast(link.linkRelationship);
- const anchorRelation = relation.indexOf(':') !== -1 ? relation.split(':')[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation;
+ if (link.link_relationship && link.link_relationship !== '-ungrouped-') {
+ const relation = StrCast(link.link_relationship);
+ const anchorRelation = relation.indexOf(':') !== -1 ? relation.split(':')[Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), anchor) ? 0 : 1] : relation;
const group = anchorGroups.get(anchorRelation);
anchorGroups.set(anchorRelation, group ? [...group, link] : [link]);
} else {
@@ -190,12 +192,18 @@ export class LinkManager {
//TODO This should probably return undefined if there isn't an opposite anchor
//TODO This should also await the return value of the anchor so we don't filter out promises
public static getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc | undefined {
- const a1 = Cast(linkDoc.anchor1, Doc, null);
- const a2 = Cast(linkDoc.anchor2, Doc, null);
- if (Doc.AreProtosEqual(anchor, a1)) return a2;
- if (Doc.AreProtosEqual(anchor, a2)) return a1;
- if (Doc.AreProtosEqual(anchor, a1.annotationOn as Doc)) return a2;
- if (Doc.AreProtosEqual(anchor, a2.annotationOn as Doc)) return a1;
+ const a1 = Cast(linkDoc.link_anchor_1, Doc, null);
+ const a2 = Cast(linkDoc.link_anchor_2, Doc, null);
+ if (Doc.AreProtosEqual(DocCast(anchor.annotationOn, anchor), DocCast(a1.annotationOn, a1))) return a2;
+ if (Doc.AreProtosEqual(DocCast(anchor.annotationOn, anchor), DocCast(a2.annotationOn, a2))) return a1;
if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc;
}
}
+
+ScriptingGlobals.add(
+ function links(doc: any) {
+ return new List(LinkManager.Links(doc));
+ },
+ 'returns all the links to the document or its annotations',
+ '(doc: any)'
+);