From 871925ecc9f15377339afc907a2000fecfe3240d Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 7 Mar 2023 17:46:40 -0500 Subject: fixed presbox following to use presItem anchor instead of target doc as anchor. stopped allowing collections to have their data fields interpolate based on keyframe. fixed assigning current frame when following presbox --- src/client/util/DocumentManager.ts | 2 +- src/client/util/LinkFollower.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index c670d0ab8..f2c554866 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -286,7 +286,7 @@ export class DocumentManager { docView.rootDoc.layoutKey === 'layout_icon' ? await new Promise(res => docView.iconify(res)) : undefined; docView.props.focus(docView.rootDoc, options); // focus the view within its container const { childDocView, viewSpec } = await iterator(docView); - if (!childDocView) return { viewSpec: viewSpec ?? docView.rootDoc, docView, contextView }; + if (!childDocView) return { viewSpec: options.anchorDoc ?? viewSpec ?? docView.rootDoc, docView, contextView }; contextView = docView; docView = childDocView; } diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index c9a178db7..e28687561 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -76,7 +76,6 @@ export class LinkFollower { easeFunc: StrCast(sourceDoc.followLinkEase, 'ease') as any, openLocation: StrCast(sourceDoc.followLinkLocation, OpenWhere.lightbox), effect: sourceDoc, - originatingDoc: sourceDoc, zoomTextSelections: BoolCast(sourceDoc.followLinkZoomText), }; if (target.type === DocumentType.PRES) { -- cgit v1.2.3-70-g09d2 From 096371e683b3f91edfadb2aaf9f8da3309b86014 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 9 Mar 2023 21:39:21 -0500 Subject: added reordering of progressivized docs --- src/client/documents/Documents.ts | 1 + src/client/util/CurrentUserUtils.ts | 2 +- .../views/collections/CollectionTreeView.tsx | 4 +-- src/client/views/collections/CollectionView.tsx | 4 +-- src/client/views/collections/TreeView.tsx | 12 ++++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/trails/PresBox.tsx | 41 ++++++++++++---------- 7 files changed, 37 insertions(+), 29 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a7378c431..6296b0df4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -335,6 +335,7 @@ export class DocumentOptions { strokeWidth?: number; freezeChildren?: string; // whether children are now allowed to be added and or removed from a collection treeViewHideTitle?: boolean; // whether to hide the top document title of a tree view + treeViewHideUnrendered?: boolean; // tells tree view not to display documents that have an 'unrendered' tag unless they also have a treeViewFieldKey tag (presBox) treeViewHideHeaderIfTemplate?: boolean; // whether to hide the header for a document in a tree view only if a childLayoutTemplate is provided (presBox) treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items. diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index a7b2c3d04..2b0a2cdac 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -269,7 +269,7 @@ export class CurrentUserUtils { {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, _isLinkButton: true }}, {key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }}, // {key: "DataViz", creator: opts => Docs.Create.DataVizDocument(opts), opts: { _width: 300, _height: 300 }}, - {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true,}}, + {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, treeViewHideUnrendered: true}}, {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _viewType: CollectionViewType.Stacking, targetDropAction: "alias" as any, treeViewHideTitle: true, _fitWidth:true, _chromeHidden: true, boxShadow: "0 0" }}, {key: "Tab", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 500, _height: 800, _fitWidth: true, _backgroundGridShow: true, }}, {key: "Slide", creator: opts => Docs.Create.TreeDocument([], opts), opts: { _width: 300, _height: 200, _viewType: CollectionViewType.Tree, diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 330e116d1..553967b95 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -38,8 +38,8 @@ export type collectionTreeViewProps = { onCheckedClick?: () => ScriptField; onChildClick?: () => ScriptField; // TODO: [AL] add these fields - AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[]; - RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[]; + AddToMap?: (treeViewDoc: Doc, index: number[]) => void; + RemFromMap?: (treeViewDoc: Doc, index: number[]) => void; hierarchyIndex?: number[]; }; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 316f1e4e9..48e5748a0 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -64,8 +64,8 @@ interface CollectionViewProps_ extends FieldViewProps { childClickScript?: ScriptField; childDoubleClickScript?: ScriptField; //TODO: [AL] add these fields - AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[]; - RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[]; + AddToMap?: (treeViewDoc: Doc, index: number[]) => void; + RemFromMap?: (treeViewDoc: Doc, index: number[]) => void; hierarchyIndex?: number[]; // hierarchical index of a document up to the rendering root (primarily used for tree views) } export interface CollectionViewProps extends React.PropsWithChildren {} diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 0d2968ba1..c8dc68eae 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -66,8 +66,8 @@ export interface TreeViewProps { skipFields?: string[]; firstLevel: boolean; // TODO: [AL] add these - AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[]; - RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[]; + AddToMap?: (treeViewDoc: Doc, index: number[]) => void; + RemFromMap?: (treeViewDoc: Doc, index: number[]) => void; hierarchyIndex?: number[]; } @@ -1028,7 +1028,9 @@ export class TreeView extends React.Component { // renders the text version of a document as the header. This is used in the file system mode and in other vanilla tree views. @computed get renderTitleAsHeader() { - return ( + return this.props.treeView.Document.treeViewHideUnrendered && this.doc.unrendered && !this.doc.treeViewFieldKey ? ( +
+ ) : ( <> {this.renderBullet} {this.renderTitle} @@ -1146,8 +1148,8 @@ export class TreeView extends React.Component { unobserveHeight: (ref: any) => void, contextMenuItems: { script: ScriptField; filter: ScriptField; label: string; icon: string }[], // TODO: [AL] add these - AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[], - RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[], + AddToMap?: (treeViewDoc: Doc, index: number[]) => void, + RemFromMap?: (treeViewDoc: Doc, index: number[]) => void, hierarchyIndex?: number[], renderCount?: number ) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b92a8ec4d..87177cd74 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -519,7 +519,7 @@ export class CollectionFreeFormView extends CollectionSubView() { this.activeItem.presIndexed = presIndexed + 1; } return true; - } else { - console.log(this.activeItem.presIndexed); } } }; @@ -359,8 +358,6 @@ export class PresBox extends ViewBoxBaseComponent() { Doc.UnBrushAllDocs(); if (index >= 0 && index < this.childDocs.length) { this.rootDoc._itemIndex = index; - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; if (from?.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) { DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia); } @@ -368,8 +365,8 @@ export class PresBox extends ViewBoxBaseComponent() { this.stopTempMedia(from.presentationTargetDoc); } // If next slide is audio / video 'Play automatically' then the next slide should be played - if (this.layoutDoc.presStatus !== PresStatus.Edit && (targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && activeItem.mediaStart === 'auto') { - this.startTempMedia(targetDoc, activeItem); + if (this.layoutDoc.presStatus !== PresStatus.Edit && (this.targetDoc.type === DocumentType.AUDIO || this.targetDoc.type === DocumentType.VID) && this.activeItem.mediaStart === 'auto') { + this.startTempMedia(this.targetDoc, this.activeItem); } if (!group) this.clearSelectedArray(); this.childDocs[index] && this.addToSelectedArray(this.childDocs[index]); //Update selected array @@ -755,17 +752,18 @@ export class PresBox extends ViewBoxBaseComponent() { const curDoc = Cast(doc, Doc, null); const tagDoc = PresBox.targetRenderedDoc(curDoc); const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc); + let opacity: Opt = index === this.itemIndex ? 1 : undefined; if (curDoc.presHide) { if (index !== this.itemIndex) { - tagDoc.opacity = 1; + opacity = 1; } } const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex) ?? itemIndexes.slice().reverse().lastElement(); if (curDoc.presHideBefore && index === hidingIndBef) { if (index > this.itemIndex) { - tagDoc.opacity = 0; + opacity = 0; } else if (index === this.itemIndex || !curDoc.presHideAfter) { - tagDoc.opacity = 1; + opacity = 1; setTimeout(() => (tagDoc._dataTransition = undefined), 1000); } } @@ -776,17 +774,18 @@ export class PresBox extends ViewBoxBaseComponent() { .find(item => item <= this.itemIndex) ?? itemIndexes.lastElement(); if (curDoc.presHideAfter && index === hidingIndAft) { if (index < this.itemIndex) { - tagDoc.opacity = 0; + opacity = 0; } else if (index === this.itemIndex || !curDoc.presHideBefore) { - tagDoc.opacity = 1; + opacity = 1; } } const hidingInd = itemIndexes.find(item => item === this.itemIndex); if (curDoc.presHide && index === hidingInd) { if (index === this.itemIndex) { - tagDoc.opacity = 0; + opacity = 0; } } + opacity !== undefined && (tagDoc.opacity = opacity); }); }; @@ -882,7 +881,6 @@ export class PresBox extends ViewBoxBaseComponent() { if (doc.presHideBefore && index > startIndex) tagDoc.opacity = 0; if (doc.presHideAfter && index < startIndex) tagDoc.opacity = 0; if (doc.presIndexed !== undefined && index >= startIndex) { - const type = DocCast(tagDoc.annotationOn)?.type ?? tagDoc.type; const startInd = NumCast(doc.presIndexedStart); this.progressivizedItems(doc) ?.slice(startInd) @@ -1552,6 +1550,13 @@ export class PresBox extends ViewBoxBaseComponent() { const tagDoc = PresBox.targetRenderedDoc(this.activeItem); const type = DocCast(tagDoc?.annotationOn)?.type ?? tagDoc.type; activeItem.presIndexedStart = type === DocumentType.COL ? 1 : 0; + // a progressivized slide doesn't have sub-slides, but rather iterates over the data list of the target being progressivized. + // to avoid creating a new slide to correspond to each of the target's data list, we simply reference the target's data list. + let dataField = Doc.LayoutFieldKey(tagDoc); + if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '-annotations'; + + if (DocCast(activeItem.presentationTargetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`self.presentationTargetDoc.annotationOn["${dataField}"]`); + else activeItem.data = ComputedField.MakeFunction(`self.presentationTargetDoc["${dataField}"]`); }} checked={Cast(activeItem.presIndexed, 'number', null) !== undefined ? true : false} /> @@ -2364,7 +2369,8 @@ export class PresBox extends ViewBoxBaseComponent() { clearTimeout(this._presTimer); }; - AddToMap = (treeViewDoc: Doc, index: number[]): Doc[] => { + AddToMap = (treeViewDoc: Doc, index: number[]) => { + if (!treeViewDoc.presentationTargetDoc) return this.childDocs; // if treeViewDoc is not a pres elements, then it's a sub-bullet of a progressivized slide which isn't added to the linearized list of pres elements since it's not really a pres element. var indexNum = 0; for (let i = 0; i < index.length; i++) { indexNum += index[i] * 10 ** -i; @@ -2377,15 +2383,14 @@ export class PresBox extends ViewBoxBaseComponent() { this.dataDoc[this.presFieldKey] = new List(sorted); // this is a flat array of Docs } } - return this.childDocs; }; - RemFromMap = (treeViewDoc: Doc, index: number[]): Doc[] => { + RemFromMap = (treeViewDoc: Doc, index: number[]) => { + if (!treeViewDoc.presentationTargetDoc) return this.childDocs; // if treeViewDoc is not a pres elements, then it's a sub-bullet of a progressivized slide which isn't added to the linearized list of pres elements since it's not really a pres element. if (!this._unmounting && this.isTree) { this._treeViewMap.delete(treeViewDoc); this.dataDoc[this.presFieldKey] = new List(this.sort(this._treeViewMap)); } - return this.childDocs; }; sort = (treeViewMap: Map) => [...treeViewMap.entries()].sort((a: [Doc, number], b: [Doc, number]) => (a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0)).map(kv => kv[0]); -- cgit v1.2.3-70-g09d2 From b599afe5fdcf8afc38432e34b1e175f82499d25b Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 11 Mar 2023 14:05:18 -0500 Subject: changed links from being a computed field on documents to being accessed by a function call on LinkManager --- src/client/documents/Documents.ts | 101 ++++++--------------- src/client/util/CaptureManager.tsx | 23 +++-- src/client/util/CurrentUserUtils.ts | 3 +- src/client/util/LinkFollower.ts | 3 +- src/client/util/LinkManager.ts | 17 +++- src/client/util/SelectionManager.ts | 4 +- src/client/views/LightboxView.tsx | 9 +- src/client/views/PropertiesButtons.tsx | 10 +- src/client/views/PropertiesView.tsx | 8 +- src/client/views/SidebarAnnos.tsx | 7 +- src/client/views/StyleProvider.tsx | 11 ++- .../collections/CollectionStackedTimeline.tsx | 7 +- src/client/views/collections/CollectionSubView.tsx | 1 - src/client/views/collections/TreeView.tsx | 3 +- src/client/views/linking/LinkMenu.tsx | 1 - src/client/views/linking/LinkMenuItem.tsx | 6 +- src/client/views/nodes/AudioBox.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 6 +- src/client/views/nodes/LinkDocPreview.tsx | 9 +- src/client/views/nodes/VideoBox.tsx | 5 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 18 ++-- src/client/views/nodes/trails/PresBox.tsx | 3 +- src/client/views/search/SearchBox.tsx | 3 +- src/fields/util.ts | 23 ++--- 24 files changed, 121 insertions(+), 163 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4dc1c3b71..251e432ef 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { action, runInAction } from 'mobx'; import { basename } from 'path'; import { DateField } from '../../fields/DateField'; -import { Doc, DocListCast, DocListCastAsync, Field, Initializing, Opt, updateCachedAcls } from '../../fields/Doc'; +import { Doc, DocListCast, Field, Initializing, Opt, updateCachedAcls } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { HtmlField } from '../../fields/HtmlField'; import { InkField, PointData } from '../../fields/InkField'; @@ -403,7 +403,6 @@ export namespace Docs { nativeDimModifiable: true, nativeHeightUnfrozen: true, forceReflow: true, - links: '@links(self)', }, }, ], @@ -411,35 +410,35 @@ export namespace Docs { DocumentType.SEARCH, { layout: { view: SearchBox, dataField: defaultDataKey }, - options: { _width: 400, links: '@links(self)' }, + options: { _width: 400 }, }, ], [ DocumentType.COLOR, { layout: { view: ColorBox, dataField: defaultDataKey }, - options: { _nativeWidth: 220, _nativeHeight: 300, links: '@links(self)' }, + options: { _nativeWidth: 220, _nativeHeight: 300 }, }, ], [ DocumentType.IMG, { layout: { view: ImageBox, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ DocumentType.WEB, { layout: { view: WebBox, dataField: defaultDataKey }, - options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: '@links(self)' }, + options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true }, }, ], [ DocumentType.COL, { layout: { view: CollectionView, dataField: defaultDataKey }, - options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: '@links(self)' }, + options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1 }, }, ], [ @@ -453,35 +452,35 @@ export namespace Docs { DocumentType.VID, { layout: { view: VideoBox, dataField: defaultDataKey }, - options: { _currentTimecode: 0, links: '@links(self)' }, + options: { _currentTimecode: 0 }, }, ], [ DocumentType.AUDIO, { layout: { view: AudioBox, dataField: defaultDataKey }, - options: { _height: 100, backgroundColor: 'lightGray', _fitWidth: true, forceReflow: true, nativeDimModifiable: true, links: '@links(self)' }, + options: { _height: 100, forceReflow: true, nativeDimModifiable: true }, }, ], [ DocumentType.REC, { layout: { view: VideoBox, dataField: defaultDataKey }, - options: { _height: 100, backgroundColor: 'pink', links: '@links(self)' }, + options: { _height: 100, backgroundColor: 'pink' }, }, ], [ DocumentType.PDF, { layout: { view: PDFBox, dataField: defaultDataKey }, - options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: '@links(self)' }, + options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true }, }, ], [ DocumentType.MAP, { layout: { view: MapBox, dataField: defaultDataKey }, - options: { _height: 600, _width: 800, nativeDimModifiable: true, links: '@links(self)' }, + options: { _height: 600, _width: 800, nativeDimModifiable: true }, }, ], [ @@ -502,7 +501,6 @@ export namespace Docs { description: '', showCaption: 'description', backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area - links: '@links(self)', _removeDropProperties: new List(['isLinkButton']), }, }, @@ -527,7 +525,7 @@ export namespace Docs { DocumentType.SCRIPTING, { layout: { view: ScriptingBox, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ @@ -540,56 +538,56 @@ export namespace Docs { DocumentType.LABEL, { layout: { view: LabelBox, dataField: defaultDataKey }, - options: { links: '@links(self)', _singleLine: true }, + options: { _singleLine: true }, }, ], [ DocumentType.EQUATION, { layout: { view: EquationBox, dataField: defaultDataKey }, - options: { links: '@links(self)', nativeDimModifiable: true, fontSize: '14px', hideResizeHandles: true, hideDecorationTitle: true }, + options: { nativeDimModifiable: true, fontSize: '14px', hideResizeHandles: true, hideDecorationTitle: true }, }, ], [ DocumentType.FUNCPLOT, { layout: { view: FunctionPlotBox, dataField: defaultDataKey }, - options: { nativeDimModifiable: true, links: '@links(self)' }, + options: { nativeDimModifiable: true }, }, ], [ DocumentType.BUTTON, { layout: { view: LabelBox, dataField: 'onClick' }, - options: { links: '@links(self)' }, + options: {}, }, ], [ DocumentType.SLIDER, { layout: { view: SliderBox, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ DocumentType.PRES, { layout: { view: PresBox, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ DocumentType.FONTICON, { layout: { view: FontIconBox, dataField: defaultDataKey }, - options: { allowClickBeforeDoubleClick: true, hideLinkButton: true, _width: 40, _height: 40, borderRounding: '100%', links: '@links(self)' }, + options: { allowClickBeforeDoubleClick: true, hideLinkButton: true, _width: 40, _height: 40, borderRounding: '100%' }, }, ], [ DocumentType.WEBCAM, { layout: { view: RecordingBox, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ @@ -603,7 +601,7 @@ export namespace Docs { DocumentType.MARKER, { layout: { view: CollectionView, dataField: defaultDataKey }, - options: { links: '@links(self)', hideLinkButton: true, pointerEvents: 'none' }, + options: { hideLinkButton: true, pointerEvents: 'none' }, }, ], [ @@ -611,21 +609,21 @@ export namespace Docs { { // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method layout: { view: InkingStroke, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ DocumentType.SCREENSHOT, { layout: { view: ScreenshotBox, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ DocumentType.COMPARISON, { layout: { view: ComparisonBox, dataField: defaultDataKey }, - options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'alias', links: '@links(self)' }, + options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'alias' }, }, ], [ @@ -640,21 +638,21 @@ export namespace Docs { DocumentType.GROUP, { layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { links: '@links(self)' }, + options: {}, }, ], [ DocumentType.DATAVIZ, { layout: { view: DataVizBox, dataField: defaultDataKey }, - options: { _fitWidth: true, nativeDimModifiable: true, links: '@links(self)' }, + options: { _fitWidth: true, nativeDimModifiable: true }, }, ], [ DocumentType.LOADING, { layout: { view: LoadingBox, dataField: '' }, - options: { _fitWidth: true, _fitHeight: true, nativeDimModifiable: true, links: '@links(self)' }, + options: { _fitWidth: true, _fitHeight: true, nativeDimModifiable: true }, }, ], ]); @@ -881,15 +879,7 @@ export namespace Docs { } export function AudioDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) { - return InstanceFromProto( - Prototypes.get(DocumentType.AUDIO), - new AudioField(url), - { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any }, - undefined, - undefined, - undefined, - overwriteDoc - ); + return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url), options, undefined, undefined, undefined, overwriteDoc); } export function RecordingDocument(url: string, options: DocumentOptions = {}) { @@ -992,7 +982,6 @@ export namespace Docs { I.creationDate = new DateField(); I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; //I['acl-Override'] = SharingPermissions.Unset; - I.links = ComputedField.MakeFunction('links(self)'); I[Initializing] = false; return I; } @@ -1282,38 +1271,6 @@ export namespace DocUtils { return rangeFilteredDocs; } - export function Publish(promoteDoc: Doc, targetID: string, addDoc: any, remDoc: any) { - targetID = targetID.replace(/^-/, '').replace(/\([0-9]*\)$/, ''); - DocServer.GetRefField(targetID).then(doc => { - if (promoteDoc !== doc) { - let copy = doc as Doc; - if (copy) { - Doc.Overwrite(promoteDoc, copy, true); - } else { - copy = Doc.MakeCopy(promoteDoc, true, targetID); - } - !doc && (copy.title = undefined) && (Doc.GetProto(copy).title = targetID); - addDoc && addDoc(copy); - remDoc && remDoc(promoteDoc); - if (!doc) { - DocListCastAsync(promoteDoc.links).then(links => { - links && - links.map(async link => { - if (link) { - const a1 = await Cast(link.anchor1, Doc); - if (a1 && Doc.AreProtosEqual(a1, promoteDoc)) link.anchor1 = copy; - const a2 = await Cast(link.anchor2, Doc); - if (a2 && Doc.AreProtosEqual(a2, promoteDoc)) link.anchor2 = copy; - LinkManager.Instance.deleteLink(link); - LinkManager.Instance.addLink(link); - } - }); - }); - } - } - }); - } - export function DefaultFocus(doc: Doc, options: DocFocusOptions) { return undefined; } @@ -1711,7 +1668,7 @@ export namespace DocUtils { export function LeavePushpin(doc: Doc, annotationField: string) { if (doc.followLinkToggle) return undefined; const context = Cast(doc.context, Doc, null) ?? Cast(doc.annotationOn, Doc, null); - const hasContextAnchor = DocListCast(doc.links).some(l => (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) || (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context)); + const hasContextAnchor = LinkManager.Links(doc).some(l => (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) || (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context)); if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { const pushpin = Docs.Create.FontIconDocument({ title: 'pushpin', diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx index 735b06f6d..c9fcc84a3 100644 --- a/src/client/util/CaptureManager.tsx +++ b/src/client/util/CaptureManager.tsx @@ -2,12 +2,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCast } from '../../fields/Doc'; -import { StrCast } from '../../fields/Types'; +import { Doc } from '../../fields/Doc'; +import { DocCast, StrCast } from '../../fields/Types'; import { addStyleSheet } from '../../Utils'; import { LightboxView } from '../views/LightboxView'; import { MainViewModal } from '../views/MainViewModal'; import './CaptureManager.scss'; +import { LinkManager } from './LinkManager'; import { SelectionManager } from './SelectionManager'; @observer @@ -48,16 +49,14 @@ export class CaptureManager extends React.Component<{}> { const doc = this._document; const order: JSX.Element[] = []; if (doc) { - DocListCast(doc.links).forEach((l, i) => { - if (l) { - order.push( -
-
{i}
- {StrCast((l.anchor1 as Doc).title)} -
- ); - } - }); + LinkManager.Links(doc).forEach((l, i) => + order.push( +
+
{i}
+ {StrCast(DocCast(l.anchor1)?.title)} +
+ ) + ); } return ( diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 2b0a2cdac..c4fb4788c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -22,7 +22,7 @@ import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; import { OverlayView } from "../views/OverlayView"; -import { DragManager, dropActionType } from "./DragManager"; +import { DragManager } from "./DragManager"; import { MakeTemplate } from "./DropConverter"; import { LinkManager } from "./LinkManager"; import { ScriptingGlobals } from "./ScriptingGlobals"; @@ -949,6 +949,5 @@ ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Do ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, "creates a new presentation when called"); ScriptingGlobals.add(function openPresentation(pres:Doc) { return MainView.Instance.openPresentation(pres); }, "creates a new presentation when called"); ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called"); -ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)"); ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); \ No newline at end of file diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index e28687561..eacbcc0e3 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -6,6 +6,7 @@ import { DocumentDecorations } from '../views/DocumentDecorations'; import { DocFocusOptions, DocumentViewSharedProps, OpenWhere } from '../views/nodes/DocumentView'; import { PresBox } from '../views/nodes/trails'; import { DocumentManager } from './DocumentManager'; +import { LinkManager } from './LinkManager'; import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; @@ -41,7 +42,7 @@ export class LinkFollower { }; public static traverseLink(link: Opt, sourceDoc: Doc, finished?: () => void, traverseBacklink?: boolean) { - const linkDocs = link ? [link] : DocListCast(sourceDoc.links); + const linkDocs = link ? [link] : LinkManager.Links(sourceDoc); const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1 const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2 const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews((d.anchor2 as Doc).type === DocumentType.MARKER ? DocCast((d.anchor2 as Doc).annotationOn) : (d.anchor2 as Doc)).length === 0); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 46d44fea4..555215417 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -4,6 +4,7 @@ import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from ' import { List } from '../../fields/List'; import { ProxyField } from '../../fields/Proxy'; import { Cast, DocCast, PromiseValue, StrCast } from '../../fields/Types'; +import { ScriptingGlobals } from './ScriptingGlobals'; /* * link doc: * - anchor1: doc @@ -24,6 +25,10 @@ export class LinkManager { public static get Instance() { return LinkManager._instance; } + + 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 => @@ -34,7 +39,7 @@ export class LinkManager { LinkManager.userLinkDBs.push(linkDb); }; public static AutoKeywords = 'keywords:Usages'; - static links: Doc[] = []; + static _links: Doc[] = []; constructor() { LinkManager._instance = this; this.createLinkrelationshipLists(); @@ -72,7 +77,7 @@ export class LinkManager { ); }; const watchUserLinkDB = (userLinkDBDoc: Doc) => { - LinkManager.links.push(...DocListCast(userLinkDBDoc.data)); + 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( @@ -193,3 +198,11 @@ export class LinkManager { 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)' +); diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 646942569..c0fc25376 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,6 +1,6 @@ import { action, observable, ObservableMap } from 'mobx'; import { computedFn } from 'mobx-utils'; -import { Doc, DocListCast, Opt } from '../../fields/Doc'; +import { Doc, Opt } from '../../fields/Doc'; import { DocCast } from '../../fields/Types'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { DocumentView } from '../views/nodes/DocumentView'; @@ -22,7 +22,7 @@ export namespace SelectionManager { // if doc is not in SelectedDocuments, add it if (!manager.SelectedViews.get(docView) && docView.props.Document.type !== DocumentType.MARKER) { if (!ctrlPressed) { - if (LinkManager.currentLink && !DocListCast(docView.rootDoc.links).includes(LinkManager.currentLink) && docView.rootDoc !== LinkManager.currentLink) { + if (LinkManager.currentLink && !LinkManager.Links(docView.rootDoc).includes(LinkManager.currentLink) && docView.rootDoc !== LinkManager.currentLink) { LinkManager.currentLink = undefined; } this.DeselectAll(); diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 2567d44bb..976c8763e 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -4,9 +4,8 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { InkTool } from '../../fields/InkField'; -import { List } from '../../fields/List'; -import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils'; +import { Cast, NumCast, StrCast } from '../../fields/Types'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../Utils'; import { DocUtils } from '../documents/Documents'; import { DocumentManager } from '../util/DocumentManager'; import { LinkManager } from '../util/LinkManager'; @@ -88,7 +87,7 @@ export class LightboxView extends React.Component { ...future .slice() .sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)) - .sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length), + .sort((a, b) => LinkManager.Links(a).length - LinkManager.Links(b).length), ]; } this._doc = doc; @@ -214,7 +213,7 @@ export class LightboxView extends React.Component { if (coll) { const fieldKey = Doc.LayoutFieldKey(coll); const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '-annotations'])]; - const links = DocListCast(coll.links) + const links = LinkManager.Links(coll) .map(link => LinkManager.getOppositeAnchor(link, coll)) .filter(doc => doc) .map(doc => doc!); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index ac1b66013..ebbe20077 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -9,8 +9,10 @@ import { RichTextField } from '../../fields/RichTextField'; import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, ScriptCast, StrCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; +import { Utils } from '../../Utils'; import { DocUtils } from '../documents/Documents'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; +import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; import { undoBatch } from '../util/UndoManager'; import { Colors } from './global/globalEnums'; @@ -19,8 +21,6 @@ import { DocumentView, OpenWhere } from './nodes/DocumentView'; import { pasteImageBitmap } from './nodes/WebBoxRenderer'; import './PropertiesButtons.scss'; import React = require('react'); -import { LinkManager } from '../util/LinkManager'; -import { Utils } from '../../Utils'; const higflyout = require('@hig/flyout'); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -139,9 +139,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined; const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]); dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' }); - containerContents.forEach(doc => { - DocListCast(doc.links).forEach(link => (link.linkDisplay = false)); - }); + containerContents.forEach(doc => LinkManager.Links(doc).forEach(link => (link.linkDisplay = false))); }); } ); @@ -330,7 +328,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const click = () => this.handleOptionChange(value[0]); const linkButton = BoolCast(this.selectedDoc._isLinkButton); const followLoc = this.selectedDoc._followLinkLocation; - const linkedToLightboxView = () => DocListCast(this.selectedDoc.links).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); + const linkedToLightboxView = () => LinkManager.Links(this.selectedDoc).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); let active = false; // prettier-ignore diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 7a985628f..03b4100a7 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -4,10 +4,10 @@ import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-soli import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox, Tooltip } from '@material-ui/core'; import { intersection } from 'lodash'; -import { action, autorun, computed, Lambda, observable } from 'mobx'; +import { action, computed, Lambda, observable } from 'mobx'; import { observer } from 'mobx-react'; import { ColorState, SketchPicker } from 'react-color'; -import { AclAdmin, AclSym, HierarchyMapping, DataSym, Doc, DocListCast, Field, HeightSym, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc'; +import { AclAdmin, AclSym, DataSym, Doc, Field, HeightSym, HierarchyMapping, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; import { List } from '../../fields/List'; @@ -23,10 +23,10 @@ import { SharingManager } from '../util/SharingManager'; import { Transform } from '../util/Transform'; import { undoBatch, UndoManager } from '../util/UndoManager'; import { EditableView } from './EditableView'; +import { FilterPanel } from './FilterPanel'; import { Colors } from './global/globalEnums'; import { InkStrokeProperties } from './InkStrokeProperties'; import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView'; -import { FilterPanel } from './FilterPanel'; import { KeyValueBox } from './nodes/KeyValueBox'; import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails'; import { PropertiesButtons } from './PropertiesButtons'; @@ -1486,7 +1486,7 @@ export class PropertiesView extends React.Component { const zoom = Number((NumCast(this.sourceAnchor?.followLinkZoomScale, 1) * 100).toPrecision(3)); const targZoom = this.sourceAnchor?.followLinkZoom; const indent = 30; - const hasSelectedAnchor = SelectionManager.Views().some(dv => DocListCast(this.sourceAnchor?.links).includes(LinkManager.currentLink!)); + const hasSelectedAnchor = LinkManager.Links(this.sourceAnchor).includes(LinkManager.currentLink!); if (!this.selectedDoc && !this.isPres) { return (
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 74ea624a6..7519cbb05 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -3,10 +3,12 @@ import { observer } from 'mobx-react'; import { Doc, DocListCast, Field, FieldResult, StrListCast } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { List } from '../../fields/List'; +import { RichTextField } from '../../fields/RichTextField'; import { DocCast, NumCast, StrCast } from '../../fields/Types'; import { emptyFunction, OmitKeys, returnAll, returnOne, returnTrue, returnZero } from '../../Utils'; import { Docs, DocUtils } from '../documents/Documents'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; +import { LinkManager } from '../util/LinkManager'; import { Transform } from '../util/Transform'; import { CollectionStackingView } from './collections/CollectionStackingView'; import { FieldViewProps } from './nodes/FieldView'; @@ -15,7 +17,6 @@ import { SearchBox } from './search/SearchBox'; import './SidebarAnnos.scss'; import { StyleProp } from './StyleProvider'; import React = require('react'); -import { RichTextField } from '../../fields/RichTextField'; interface ExtraProps { fieldKey: string; @@ -174,8 +175,8 @@ export class SidebarAnnos extends React.Component { showTitle = () => 'title'; setHeightCallback = (height: number) => this.props.setHeight?.(height + this.filtersHeight()); sortByLinkAnchorY = (a: Doc, b: Doc) => { - const ay = DocListCast(a.links).length && DocCast(DocListCast(a.links)[0].anchor1).y; - const by = DocListCast(b.links).length && DocCast(DocListCast(b.links)[0].anchor1).y; + const ay = LinkManager.Links(a).length && DocCast(LinkManager.Links(a)[0].anchor1).y; + const by = LinkManager.Links(b).length && DocCast(LinkManager.Links(b)[0].anchor1).y; return NumCast(ay) - NumCast(by); }; render() { diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 153c30052..ce764c7bf 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -1,12 +1,15 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Shadows } from 'browndash-components'; import { action, runInAction } from 'mobx'; import { extname } from 'path'; -import { Doc, DocListCast, Opt } from '../../fields/Doc'; -import { BoolCast, Cast, DocCast, ImageCast, NumCast, StrCast } from '../../fields/Types'; +import { Doc, Opt } from '../../fields/Doc'; +import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../fields/Types'; import { DashColor, emptyFunction, lightOrDark } from '../../Utils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { DocFocusOrOpen } from '../util/DocumentManager'; +import { LinkManager } from '../util/LinkManager'; +import { SelectionManager } from '../util/SelectionManager'; import { ColorScheme } from '../util/SettingsManager'; import { undoBatch, UndoManager } from '../util/UndoManager'; import { TreeSort } from './collections/TreeView'; @@ -18,8 +21,6 @@ import { FieldViewProps } from './nodes/FieldView'; import { SliderBox } from './nodes/SliderBox'; import './StyleProvider.scss'; import React = require('react'); -import { Shadows } from 'browndash-components'; -import { SelectionManager } from '../util/SelectionManager'; export enum StyleProp { TreeViewIcon = 'treeViewIcon', @@ -261,7 +262,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt () => this.props.currentTimecode(), time => { const dictationDoc = Cast(this.props.layoutDoc['data-dictation'], Doc, null); - const isDictation = dictationDoc && DocListCast(this.props.mark.links).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc); + const isDictation = dictationDoc && LinkManager.Links(this.props.mark).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc); if ( !LightboxView.LightboxDoc && // bcz: when should links be followed? we don't want to move away from the video to follow a link but we can open it in a sidebar/etc. But we don't know that upfront. // for now, we won't follow any links when the lightbox is oepn to avoid "losing" the video. /*(isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this.props.layoutDoc))*/ !this.props.layoutDoc.dontAutoFollowLinks && - DocListCast(this.props.mark.links).length && + LinkManager.Links(this.props.mark).length && time > NumCast(this.props.mark[this.props.startTag]) && time < NumCast(this.props.mark[this.props.endTag]) && this._lastTimecode < NumCast(this.props.mark[this.props.startTag]) - 1e-5 diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index c88ae314e..e46220f02 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -1,5 +1,4 @@ import { action, computed, observable } from 'mobx'; -import ReactLoading from 'react-loading'; import * as rp from 'request-promise'; import CursorField from '../../../fields/CursorField'; import { AclPrivate, Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc'; diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 08ffa1466..2bdcf472f 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -15,6 +15,7 @@ import { Docs, DocUtils } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType } from '../../util/DragManager'; +import { LinkManager } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; @@ -708,7 +709,7 @@ export class TreeView extends React.Component { @computed get validExpandViewTypes() { const annos = () => (DocListCast(this.doc[this.fieldKey + '-annotations']).length && !this.props.treeView.dashboardMode ? 'annotations' : ''); - const links = () => (DocListCast(this.doc.links).length && !this.props.treeView.dashboardMode ? 'links' : ''); + const links = () => (LinkManager.Links(this.doc).length && !this.props.treeView.dashboardMode ? 'links' : ''); const data = () => (this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : ''); const aliases = () => (this.props.treeView.dashboardMode ? '' : 'aliases'); const fields = () => (Doc.noviceMode ? '' : 'fields'); diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index c9112eec3..3f6369898 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -1,7 +1,6 @@ import { action, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc } from '../../../fields/Doc'; -import { DocCast } from '../../../fields/Types'; import { LinkManager } from '../../util/LinkManager'; import { DocumentView } from '../nodes/DocumentView'; import { LinkDocPreview } from '../nodes/LinkDocPreview'; diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index cdac91a62..4741fc6f2 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, DocListCast } from '../../../fields/Doc'; +import { Doc } from '../../../fields/Doc'; import { Cast, DocCast, StrCast } from '../../../fields/Types'; import { WebField } from '../../../fields/URLField'; import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils'; @@ -15,11 +15,11 @@ import { LinkManager } from '../../util/LinkManager'; import { SelectionManager } from '../../util/SelectionManager'; import { SettingsManager } from '../../util/SettingsManager'; import { undoBatch } from '../../util/UndoManager'; +import { MainView } from '../MainView'; import { DocumentView, OpenWhere } from '../nodes/DocumentView'; import { LinkDocPreview } from '../nodes/LinkDocPreview'; import './LinkMenuItem.scss'; import React = require('react'); -import { MainView } from '../MainView'; interface LinkMenuItemProps { groupType: string; @@ -34,7 +34,7 @@ interface LinkMenuItemProps { // drag links and drop link targets (aliasing them if needed) export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: DocumentView, downX: number, downY: number, sourceDoc: Doc, specificLinks?: Doc[]) { - const draggedDocs = (specificLinks ? specificLinks : DocListCast(sourceDoc.links)).map(link => LinkManager.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[]; + const draggedDocs = (specificLinks ? specificLinks : LinkManager.Links(sourceDoc)).map(link => LinkManager.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[]; if (draggedDocs.length) { const moddrag: Doc[] = []; diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 4c26468b9..890ecc1b2 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -11,6 +11,7 @@ import { emptyFunction, formatTime, OmitKeys, returnFalse, setupMoveUpEvents } f import { DocUtils } from '../../documents/Documents'; import { Networking } from '../../Network'; import { DragManager } from '../../util/DragManager'; +import { LinkManager } from '../../util/LinkManager'; import { undoBatch } from '../../util/UndoManager'; import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline'; import { ContextMenu } from '../ContextMenu'; @@ -84,7 +85,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent (this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton)), icon: 'eye' }); !appearance && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' }); @@ -969,7 +969,7 @@ export class DocumentViewInternal extends DocComponent this.toggleFollowLink(false, true), icon: 'map-pin' }); onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' }); !existingOnClick && cm.addItem({ description: 'OnClick...', addDivider: true, noexpand: true, subitems: onClicks, icon: 'mouse-pointer' }); - } else if (DocListCast(this.Document.links).length) { + } else if (LinkManager.Links(this.Document).length) { onClicks.push({ description: 'Select on Click', event: () => this.selectOnClick(), icon: 'link' }); onClicks.push({ description: 'Follow Link on Click', event: () => this.followLinkOnClick(undefined, false), icon: 'link' }); onClicks.push({ description: 'Toggle Link Target on Click', event: () => this.toggleTargetOnClick(), icon: 'map-pin' }); @@ -1907,7 +1907,7 @@ ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc) { const linkSource = Cast(linkCollection.linkSource, Doc, null); const collectedLinks = DocListCast(Doc.GetProto(linkCollection).data); let wid = linkSource[WidthSym](); - const links = DocListCast(linkSource.links); + const links = LinkManager.Links(linkSource); links.forEach(link => { const other = LinkManager.getOppositeAnchor(link, linkSource); const otherdoc = !other ? undefined : other.annotationOn ? Cast(other.annotationOn, Doc, null) : other; diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index bbe9b4323..16b352e4f 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -3,7 +3,7 @@ import { Tooltip } from '@material-ui/core'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import wiki from 'wikijs'; -import { Doc, DocCastAsync, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { Doc, DocCastAsync, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils'; import { DocServer } from '../../DocServer'; @@ -14,11 +14,10 @@ import { LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { SettingsManager } from '../../util/SettingsManager'; import { Transform } from '../../util/Transform'; +import { SearchBox } from '../search/SearchBox'; import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView'; import './LinkDocPreview.scss'; import React = require('react'); -import { SearchUtil } from '../../util/SearchUtil'; -import { SearchBox } from '../search/SearchBox'; interface LinkDocPreviewProps { linkDoc?: Doc; @@ -110,8 +109,8 @@ export class LinkDocPreview extends React.Component { const anchorDoc = anchorDocId ? PromiseValue(DocCast(DocServer.GetCachedRefField(anchorDocId) ?? DocServer.GetRefField(anchorDocId))) : undefined; anchorDoc?.then?.( action(anchor => { - if (anchor instanceof Doc && DocListCast(anchor.links).length) { - this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0]; + if (anchor instanceof Doc && LinkManager.Links(anchor).length) { + this._linkDoc = this._linkDoc ?? LinkManager.Links(anchor)[0]; const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords; if (automaticLink) { // automatic links specify the target in the link info, not the source diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 7df3d5f24..dc6bf6f9e 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from 'mobx'; import { observer } from 'mobx-react'; import { basename } from 'path'; -import { Doc, DocListCast, HeightSym, WidthSym } from '../../../fields/Doc'; +import { Doc, HeightSym, WidthSym } from '../../../fields/Doc'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; @@ -14,6 +14,7 @@ import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { Networking } from '../../Network'; import { DocumentManager } from '../../util/DocumentManager'; +import { LinkManager } from '../../util/LinkManager'; import { ReplayMovements } from '../../util/ReplayMovements'; import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; @@ -85,7 +86,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { + LinkManager.Links(this.dataDoc).forEach((l, i) => { const anchor = (l.anchor1 as Doc).annotationOn ? (l.anchor1 as Doc) : (l.anchor2 as Doc).annotationOn ? (l.anchor2 as Doc) : undefined; if (anchor && (anchor.annotationOn as Doc).mediaState === 'recording') { linkTime = NumCast(anchor._timecodeToShow /* audioStart */); @@ -400,7 +400,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { const newAutoLinks = new Set(); - const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship === LinkManager.AutoKeywords); + const oldAutoLinks = LinkManager.Links(this.props.Document).filter(link => link.linkRelationship === LinkManager.AutoKeywords); if (this._editorView?.state.doc.textContent) { const isNodeSel = this._editorView.state.selection instanceof NodeSelection; const f = this._editorView.state.selection.from; @@ -453,7 +453,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { alink = alink ?? - (DocListCast(this.Document.links).find(link => Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || + (LinkManager.Links(this.Document).find(link => Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, LinkManager.AutoKeywords)!); newAutoLinks.add(alink); const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }]; @@ -687,7 +687,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - LinkManager.Instance.deleteLink(DocListCast(anchor.links)[0]); + LinkManager.Instance.deleteLink(LinkManager.Links(anchor)[0]); // const docAnnotations = DocListCast(this.props.dataDoc[this.fieldKey]); // this.props.dataDoc[this.fieldKey] = new List(docAnnotations.filter(a => a !== this.annoTextRegion)); // AnchorMenu.Instance.fadeOut(true); @@ -1014,7 +1014,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent DocumentManager.Instance.RecordingEvent, this.breakupDictation); this._disposers.autoHeight = reaction( () => this.autoHeight, @@ -1041,7 +1041,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent DocListCast(this.dataDoc.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks + () => LinkManager.Links(this.dataDoc), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks newLinks => { this._cachedLinks.forEach(l => !newLinks.includes(l) && this.RemoveLinkFromDoc(l)); this._cachedLinks = newLinks; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index dda96d8e7..be40b3592 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -35,7 +35,6 @@ import { FieldView, FieldViewProps } from '../FieldView'; import { ScriptingBox } from '../ScriptingBox'; import './PresBox.scss'; import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums'; -import { tag } from 'xregexp/types'; const { Howl } = require('howler'); export interface pinDataTypes { @@ -1551,7 +1550,7 @@ export class PresBox extends ViewBoxBaseComponent() { const type = DocCast(tagDoc?.annotationOn)?.type ?? tagDoc.type; activeItem.presIndexedStart = type === DocumentType.COL ? 1 : 0; // a progressivized slide doesn't have sub-slides, but rather iterates over the data list of the target being progressivized. - // to avoid creating a new slide to correspond to each of the target's data list, we simply reference the target's data list. + // to avoid creating a new slide to correspond to each of the target's data list, we create a computedField to refernce the target's data list. let dataField = Doc.LayoutFieldKey(tagDoc); if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '-annotations'; diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 3ba60edd7..fe9b13fbe 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -5,7 +5,6 @@ import * as React from 'react'; import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { DocCast, StrCast } from '../../../fields/Types'; -import { StopEvent } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; @@ -470,7 +469,7 @@ export class SearchBox extends ViewBoxBaseComponent() { } } style={{ - fontWeight: DocListCast(fromDoc?.links).find( + fontWeight: LinkManager.Links(fromDoc).find( link => Doc.AreProtosEqual(LinkManager.getOppositeAnchor(link, fromDoc!), result[0] as Doc) || Doc.AreProtosEqual(DocCast(LinkManager.getOppositeAnchor(link, fromDoc!)?.annotationOn), result[0] as Doc) ) ? 'bold' diff --git a/src/fields/util.ts b/src/fields/util.ts index e517e7604..6ff13d5d3 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -1,8 +1,8 @@ -import { forEach } from 'lodash'; import { $mobx, action, observable, runInAction, trace } from 'mobx'; import { computedFn } from 'mobx-utils'; import { DocServer } from '../client/DocServer'; import { CollectionViewType } from '../client/documents/DocumentTypes'; +import { LinkManager } from '../client/util/LinkManager'; import { SerializationHelper } from '../client/util/SerializationHelper'; import { UndoManager } from '../client/util/UndoManager'; import { returnZero } from '../Utils'; @@ -18,7 +18,6 @@ import { Doc, DocListCast, DocListCastAsync, - FieldResult, ForceServerWrite, HeightSym, HierarchyMapping, @@ -259,7 +258,7 @@ function getEffectiveAcl(target: any, user?: string): symbol { */ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean, visited?: Doc[], isDashboard?: boolean) { if (!visited) visited = [] as Doc[]; - if (visited.includes(target)) return; + if (!target || visited.includes(target)) return; if ((target._viewType === CollectionViewType.Docking && visited.length > 1) || Doc.GetProto(visited[0]) !== Doc.GetProto(target)) { target[key] = acl; @@ -293,26 +292,18 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc } // maps over the links of the document - DocListCast(dataDoc.links).forEach(link => distributeAcls(key, acl, link, inheritingFromCollection, visited)); + LinkManager.Links(dataDoc).forEach(link => distributeAcls(key, acl, link, inheritingFromCollection, visited)); // maps over the children of the document - DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + (isDashboard ? '-all' : '')]).map(d => { + DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => { distributeAcls(key, acl, d, inheritingFromCollection, visited); - // } - const data = d[DataSym]; - if (data) { - distributeAcls(key, acl, data, inheritingFromCollection, visited); - } + distributeAcls(key, acl, d[DataSym], inheritingFromCollection, visited); }); // maps over the annotations of the document - DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '-annotations']).map(d => { + DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '-annotations']).forEach(d => { distributeAcls(key, acl, d, inheritingFromCollection, visited); - // } - const data = d[DataSym]; - if (data) { - distributeAcls(key, acl, data, inheritingFromCollection, visited); - } + distributeAcls(key, acl, d[DataSym], inheritingFromCollection, visited); }); } -- cgit v1.2.3-70-g09d2