From 3c834ee55899b91f5fff8cfe5c808607f942b9f3 Mon Sep 17 00:00:00 2001 From: dinhanhtruong <70963346+dinhanhtruong@users.noreply.github.com> Date: Thu, 8 Jul 2021 15:51:26 -0400 Subject: created front-end link popup on text highlight added button to AnchorMenu marquee selector for showing the link popup after highlighting text. created the front end of the popup. need to fix visibility when clicking out --- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 911ec1560..5180732b9 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1398,7 +1398,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp SelectionManager.DeselectAll(); RichTextMenu.Instance.updateMenu(undefined, undefined, undefined); return; - case "Enter": this.insertTime(); + case "Enter": this.insertTime(); case "Tab": e.preventDefault(); break; default: if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break; case " ": -- cgit v1.2.3-70-g09d2 From 6ecd0222232a9fd1ac1f9c915cfb192a0da7dac1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 15 Jul 2021 10:33:54 -0400 Subject: hide sidebar handle and fix scrolling for secondary formattedTextBox views (eg when in a layout template) --- src/client/views/DocComponent.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 18 ++++++++++-------- 3 files changed, 12 insertions(+), 10 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 0b54e3cd6..da8af7cc0 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -119,7 +119,7 @@ export function ViewBoxAnnotatableComponent

{ const style: { [key: string]: any } = {}; - const divKeys = ["width", "height", "fontSize", "left", "background", "top", "pointerEvents", "position"]; + const divKeys = ["width", "height", "fontSize", "left", "background", "left", "right", "top", "bottom", "pointerEvents", "position"]; const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a property expression string: { script } into a value return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result as string || ""; }; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5646a9790..60fa462ad 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -137,7 +137,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps { hideDecorationTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings treeViewDoc?: Doc; isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events - isContentActive: () => boolean | undefined; // whether a document should handle pointer events + isContentActive: () => boolean | undefined; // whether document contents should handle pointer events contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents radialMenu?: String[]; LayoutTemplateString?: string; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 911ec1560..2070b9863 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -876,7 +876,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp var quickScroll: string | undefined = ""; this._disposers.scroll = reaction(() => NumCast(this.layoutDoc._scrollTop), pos => { - if (!this._ignoreScroll && this._scrollRef.current) { + if (!this._ignoreScroll && this._scrollRef.current && !this.props.dontSelectOnLoad) { const viewTrans = quickScroll ?? StrCast(this.Document._viewTransition); const durationMiliStr = viewTrans.match(/([0-9]*)ms/); const durationSecStr = viewTrans.match(/([0-9.]*)s/); @@ -1413,9 +1413,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } onScroll = (e: React.UIEvent) => { if (!LinkDocPreview.LinkInfo && this._scrollRef.current) { - this._ignoreScroll = true; - this.layoutDoc._scrollTop = this._scrollRef.current.scrollTop; - this._ignoreScroll = false; + if (this.props.dontSelectOnLoad) { + console.log("here"); + } else { + this._ignoreScroll = true; + this.layoutDoc._scrollTop = this._scrollRef.current.scrollTop; + this._ignoreScroll = false; + } } } tryUpdateScrollHeight() { @@ -1517,8 +1521,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const selPad = Math.min(margins, 10); const padding = Math.max(margins + ((selected && !this.layoutDoc._singleLine) || minimal ? -selPad : 0), 0); const selPaddingClass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : ""; - const col = this.props.color ? this.props.color : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color); - const back = this.props.background ? this.props.background : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor); return (

this.isContentActive() && e.stopPropagation()} @@ -1566,8 +1568,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }} />
- {(this.props.noSidebar || this.Document._noSidebar) || !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) : this.sidebarCollection} - {(this.props.noSidebar || this.Document._noSidebar) || this.Document._singleLine ? (null) : this.sidebarHandle} + {(this.props.noSidebar || this.Document._noSidebar) || this.props.dontSelectOnLoad || !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) : this.sidebarCollection} + {(this.props.noSidebar || this.Document._noSidebar) || this.props.dontSelectOnLoad || this.Document._singleLine ? (null) : this.sidebarHandle} {!this.layoutDoc._showAudio ? (null) : this.audioHandle} -- cgit v1.2.3-70-g09d2 From cd5cecb339044e65ab016d8962eee8341c5f9dd8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 15 Jul 2021 11:18:00 -0400 Subject: from last --- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 2070b9863..b97b5ff01 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -71,7 +71,7 @@ export interface FormattedTextBoxProps { xPadding?: number; // used to override document's settings for xMargin --- see CollectionCarouselView yPadding?: number; noSidebar?: boolean; - dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded + dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded (and mark as not being associated with scrollTop document field) } export const GoogleRef = "googleDocId"; @@ -1413,9 +1413,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } onScroll = (e: React.UIEvent) => { if (!LinkDocPreview.LinkInfo && this._scrollRef.current) { - if (this.props.dontSelectOnLoad) { - console.log("here"); - } else { + if (!this.props.dontSelectOnLoad) { this._ignoreScroll = true; this.layoutDoc._scrollTop = this._scrollRef.current.scrollTop; this._ignoreScroll = false; -- cgit v1.2.3-70-g09d2 From 31a00f5e5d374b8a2945525a75f80f4148c143b7 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 15 Jul 2021 17:29:04 -0400 Subject: fixed onClick scripts for layout fields of HTMLtags. adjusted formatting of fieldViews/checkboxes/inputs --- src/client/views/nodes/DocumentContentsView.tsx | 2 +- src/client/views/nodes/formattedText/DashFieldView.scss | 11 +++++++---- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 34488ffbe..a0a40becb 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -180,7 +180,7 @@ export class DocumentContentsView extends React.Component { return prefix + (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document }).result as string || "") + postfix; }; - layoutFrame = layoutFrame.replace(/(>[^{]*)\{([^.'][^<}]+)\}([^}]*<)/g, replacer); + layoutFrame = layoutFrame.replace(/(>[^{]*)[^=]\{([^.'][^<}]+)\}([^}]*<)/g, replacer); // replace HTML with corresponding HTML tag as in: becomes const replacer2 = (match: any, p1: string, offset: any, string: any) => { diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss index e16036000..e7dd286a5 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.scss +++ b/src/client/views/nodes/formattedText/DashFieldView.scss @@ -1,6 +1,7 @@ .dashFieldView { position: relative; - display: inline-block; + display: inline-flex; + align-items: center; .dashFieldView-enumerables { width: 10px; @@ -13,6 +14,8 @@ min-width: 12px; position: relative; display: inline-block; + margin: 0; + transform: scale(0.7); background-color: rgba(155, 155, 155, 0.24); } .dashFieldView-labelSpan { @@ -22,11 +25,11 @@ background: rgba(0,0,0,0.1); } .dashFieldView-fieldSpan { - min-width: 20px; + min-width: 8px; margin-left: 2px; margin-right: 5px; - position: relative; - display: inline; + padding-left: 2px; + display: inline-block; background-color: rgba(155, 155, 155, 0.24); font-weight: bold; span { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index b97b5ff01..42cb02782 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1554,7 +1554,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp >
Date: Thu, 15 Jul 2021 23:59:38 -0400 Subject: cleaned up autoHeight for text in conjunction with sidebars and titles and some layout scripts --- src/client/util/CurrentUserUtils.ts | 13 +++++++--- src/client/views/DocumentDecorations.tsx | 4 +-- src/client/views/SidebarAnnos.tsx | 5 +++- .../views/nodes/formattedText/FormattedTextBox.tsx | 29 +++++++++++----------- 4 files changed, 31 insertions(+), 20 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 12733e815..5bab827d5 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -406,11 +406,18 @@ export class CurrentUserUtils { storedMarks: [] }; const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { title: "header", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, cloneFieldFilter: new List(["system"]) }, "header"); // text needs to be a space to allow templateText to be created + const headerBtnHgt = 10; headerTemplate[DataSym].layout = - "
" + - " " + - " " + + "
" + + ` ` + + " " + + ` Metadata` + "
"; + + // "
" + + // " " + + // " " + + // "
"; (headerTemplate.proto as Doc).isTemplateDoc = makeTemplate(headerTemplate.proto as Doc, true, "headerView"); doc.emptyHeader = headerTemplate; ((doc.emptyHeader as Doc).proto as Doc)["dragFactory-count"] = 0; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index bf939d57c..65a97a49d 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -235,7 +235,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b this._resizeUndo = UndoManager.StartBatch("DocDecs resize"); this._snapX = e.pageX; this._snapY = e.pageY; - DragManager.docsBeingDragged.forEach(doc => this._dragHeights.set(doc, { start: NumCast(doc._height), lowest: NumCast(doc._height) })); + SelectionManager.Views().forEach(docView => this._dragHeights.set(docView.layoutDoc, { start: NumCast(docView.rootDoc._height), lowest: NumCast(docView.rootDoc._height) })); } onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => { @@ -382,7 +382,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b SnappingManager.clearSnapLines(); // detect autoHeight gesture and apply - DragManager.docsBeingDragged.map(doc => ({ doc, hgts: this._dragHeights.get(doc) })) + SelectionManager.Views().map(docView => ({ doc: docView.layoutDoc, hgts: this._dragHeights.get(docView.layoutDoc) })) .filter(pair => pair.hgts && pair.hgts.lowest < pair.hgts.start && pair.hgts.lowest <= 20) .forEach(pair => pair.doc._autoHeight = true); //need to change points for resize, or else rotation/control points will fail. diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 59ff1c340..9c5a54574 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -78,7 +78,9 @@ export class SidebarAnnos extends React.Component { docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])]; sidebarStyleProvider = (doc: Opt, props: Opt, property: string) => { - if (property === StyleProp.ShowTitle) return StrCast(this.props.layoutDoc["sidebar-childShowTitle"], "title"); + if (property === StyleProp.ShowTitle) { + return doc === this.props.rootDoc ? 0 : StrCast(this.props.layoutDoc["sidebar-childShowTitle"], "title"); + } return this.props.styleProvider?.(doc, props, property); } render() { @@ -117,6 +119,7 @@ export class SidebarAnnos extends React.Component { styleProvider={this.sidebarStyleProvider} docFilters={this.docFilters} scaleField={this.sidebarKey() + "-scale"} + setHeight={(height) => this.props.setHeight(height + this.filtersHeight())} isAnnotationOverlay={false} select={emptyFunction} scaling={returnOne} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 42cb02782..95d8f555c 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -124,8 +124,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @computed get autoHeight() { return this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight; } @computed get textHeight() { return NumCast(this.rootDoc[this.fieldKey + "-height"]); } @computed get scrollHeight() { return NumCast(this.rootDoc[this.fieldKey + "-scrollHeight"]); } - @computed get sidebarHeight() { return NumCast(this.rootDoc[this.SidebarKey + "-height"]); } + @computed get sidebarHeight() { return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + "-height"]); } @computed get titleHeight() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0; } + @computed get autoHeightMargins() { return this.titleHeight + (this.layoutDoc._autoHeightMargins && !this.props.dontSelectOnLoad ? NumCast(this.layoutDoc._autoHeightMargins) : 0); } @computed get _recording() { return this.dataDoc?.mediaState === "recording"; } set _recording(value) { !this.dataDoc.recordingSource && (this.dataDoc.mediaState = value ? "recording" : undefined); @@ -780,7 +781,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp // Since we also monitor all component height changes, this will update the document's height. resetNativeHeight = (scrollHeight: number) => { const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.layoutDoc._nativeHeight); - this.rootDoc[this.fieldKey + "-height"] = scrollHeight + this.titleHeight; + this.rootDoc[this.fieldKey + "-height"] = scrollHeight; if (nh) this.layoutDoc._nativeHeight = scrollHeight; } @@ -793,8 +794,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp ({ width, scrollHeight, autoHeight }) => width && autoHeight && this.resetNativeHeight(scrollHeight) ); this._disposers.componentHeights = reaction( // set the document height when one of the component heights changes and autoHeight is on - () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, autoHeight: this.autoHeight }), - ({ sidebarHeight, textHeight, autoHeight }) => autoHeight && this.props.setHeight(Math.max(sidebarHeight, textHeight))); + () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, autoHeight: this.autoHeight, marginsHeight: this.autoHeightMargins }), + ({ sidebarHeight, textHeight, autoHeight, marginsHeight }) => autoHeight && this.props.setHeight(marginsHeight + Math.max(sidebarHeight, textHeight))); this._disposers.links = reaction(() => DocListCast(this.Document.links), // 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)); @@ -1422,16 +1423,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } tryUpdateScrollHeight() { if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) { - setTimeout(() => { // bcz: don't know why this is needed, but without it, the size of the textbox is too big as it includes the size of the title header. after the timeout, the size seems to get computed correctly. - const proseHeight = this.ProseRef?.scrollHeight || 0; - const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); - if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation - const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; - if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { - setScrollHeight(); - } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... - } - }); + const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0); + const proseHeight = !this.ProseRef ? 0 : Array.from(this.ProseRef.children[0].children).reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); + const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); + if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation + const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; + if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { + setScrollHeight(); + } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... + } } } fitToBox = () => this.props.Document._fitToBox; @@ -1471,6 +1471,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp layoutDoc={this.layoutDoc} dataDoc={this.dataDoc} PanelWidth={this.sidebarWidth} + setHeight={this.setSidebarHeight} sidebarAddDocument={this.sidebarAddDocument} moveDocument={this.moveDocument} removeDocument={this.removeDocument} -- cgit v1.2.3-70-g09d2 From 14e66ac5bcdaa5e244be68ccb8cbb0c495917910 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 23 Jul 2021 14:41:18 -0400 Subject: fixed issues with layoutString templates (eg customView): scale properly when in a time view, have a data doc, scripts are called with 'scale' parmeter --- src/client/util/CurrentUserUtils.ts | 16 +++++++++------ src/client/views/DocComponent.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 11 +++++----- .../views/nodes/formattedText/FormattedTextBox.tsx | 11 +++++----- src/fields/Doc.ts | 24 ++++++++++++++++++++-- 5 files changed, 45 insertions(+), 19 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 8a98304b2..22504f102 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -405,14 +405,18 @@ export class CurrentUserUtils { selection: { type: "text", anchor: 1, head: 1 }, storedMarks: [] }; - const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { title: "text", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, cloneFieldFilter: new List(["system"]) }, "header"); // text needs to be a space to allow templateText to be created + const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { + title: "text", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all", + _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, _fitWidth: true, + cloneFieldFilter: new List(["system"]) + }, "header"); const headerBtnHgt = 10; headerTemplate[DataSym].layout = - "
" + - ` ` + - " " + - ` Metadata` + - "
"; + "" + + ` ` + + " " + + ` Metadata` + + ""; // "
" + // " " + diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index da8af7cc0..0b70ce68d 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -119,7 +119,7 @@ export function ViewBoxAnnotatableComponent

{ const style: { [key: string]: any } = {}; - const divKeys = ["width", "height", "fontSize", "left", "background", "left", "right", "top", "bottom", "pointerEvents", "position"]; + const divKeys = ["width", "height", "fontSize", "transform", "left", "background", "left", "right", "top", "bottom", "pointerEvents", "position"]; const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a property expression string: { script } into a value return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result as string || ""; }; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index a0a40becb..9b75cd8f9 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -64,6 +64,7 @@ interface HTMLtagProps { htmltag: string; onClick?: ScriptField; onInput?: ScriptField; + scaling: number; } //" {this.title}" @@ -82,7 +83,7 @@ interface HTMLtagProps { export class HTMLtag extends React.Component { click = (e: React.MouseEvent) => { const clickScript = (this.props as any).onClick as Opt; - clickScript?.script.run({ this: this.props.Document, self: this.props.RootDoc }); + clickScript?.script.run({ this: this.props.Document, self: this.props.RootDoc, scale: this.props.scaling }); } onInput = (e: React.FormEvent) => { const onInputScript = (this.props as any).onInput as Opt; @@ -90,9 +91,9 @@ export class HTMLtag extends React.Component { } render() { const style: { [key: string]: any } = {}; - const divKeys = OmitKeys(this.props, ["children", "htmltag", "RootDoc", "Document", "key", "onInput", "onClick", "__proto__"]).omit; + const divKeys = OmitKeys(this.props, ["children", "htmltag", "RootDoc", "scaling", "Document", "key", "onInput", "onClick", "__proto__"]).omit; const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a propery expression string: { script } into a value - return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ self: this.props.RootDoc, this: this.props.Document }).result as string || ""; + return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.props.RootDoc, this: this.props.Document, scale: this.props.scaling }).result as string || ""; }; Object.keys(divKeys).map((prop: string) => { const p = (this.props as any)[prop] as string; @@ -184,7 +185,7 @@ export class DocumentContentsView extends React.Component with corresponding HTML tag as in: becomes const replacer2 = (match: any, p1: string, offset: any, string: any) => { - return ` 1) { const code = XRegExp.matchRecursive(splits[1], "{", "}", "", { valueNames: ["between", "left", "match", "right", "between"] }); layoutFrame = splits[0] + ` ${func}={props.${func}} ` + splits[1].substring(code[1].end + 1); - return ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name, value: "string" }); + return ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name, scale: "number", value: "string" }); } return undefined; // add input function to props diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 95d8f555c..6dd63fb47 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -71,6 +71,7 @@ export interface FormattedTextBoxProps { xPadding?: number; // used to override document's settings for xMargin --- see CollectionCarouselView yPadding?: number; noSidebar?: boolean; + dontScale?: boolean; dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded (and mark as not being associated with scrollTop document field) } export const GoogleRef = "googleDocId"; @@ -126,7 +127,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @computed get scrollHeight() { return NumCast(this.rootDoc[this.fieldKey + "-scrollHeight"]); } @computed get sidebarHeight() { return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + "-height"]); } @computed get titleHeight() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0; } - @computed get autoHeightMargins() { return this.titleHeight + (this.layoutDoc._autoHeightMargins && !this.props.dontSelectOnLoad ? NumCast(this.layoutDoc._autoHeightMargins) : 0); } + @computed get autoHeightMargins() { return this.titleHeight + NumCast(this.layoutDoc._autoHeightMargins); } @computed get _recording() { return this.dataDoc?.mediaState === "recording"; } set _recording(value) { !this.dataDoc.recordingSource && (this.dataDoc.mediaState = value ? "recording" : undefined); @@ -1524,10 +1525,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp

this.isContentActive() && e.stopPropagation()} style={{ - transform: `scale(${scale})`, - transformOrigin: "top left", - width: `${100 / scale}%`, - height: `${100 / scale}%`, + transform: this.props.dontScale ? undefined : `scale(${scale})`, + transformOrigin: this.props.dontScale ? undefined : "top left", + width: this.props.dontScale ? undefined : `${100 / scale}%`, + height: this.props.dontScale ? undefined : `${100 / scale}%`, // overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined, ...this.styleFromLayoutString(scale) // this converts any expressions in the format string to style props. e.g., }}> diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index bd0ba3ad7..464a8ad05 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -803,6 +803,27 @@ export namespace Doc { return undefined; } + // Makes a delegate of a document by first creating a delegate where data should be stored + // (ie, the 'data' doc), and then creates another delegate of that (ie, the 'layout' doc). + // This is appropriate if you're trying to create a document that behaves like all + // regularly created documents (e.g, text docs, pdfs, etc which all have data/layout docs) + export function MakeDelegateWithProto(doc: Doc, id?: string, title?: string): Doc { + const delegateProto = new Doc(); + delegateProto[Initializing] = true; + delegateProto.proto = doc; + delegateProto.author = Doc.CurrentUserEmail; + delegateProto.isPrototype = true; + title && (delegateProto.title = title); + const delegate = new Doc(id, true); + delegate[Initializing] = true; + delegate.proto = delegateProto; + delegate.author = Doc.CurrentUserEmail; + Doc.AddDocToList(delegateProto[DataSym], "aliases", delegate); + delegate[Initializing] = false; + delegateProto[Initializing] = false; + return delegate; + } + let _applyCount: number = 0; export function ApplyTemplate(templateDoc: Doc) { if (templateDoc) { @@ -1150,8 +1171,7 @@ export namespace Doc { return ndoc; } export function delegateDragFactory(dragFactory: Doc) { - const ndoc = Doc.MakeDelegate(dragFactory); - ndoc.isPrototype = true; + const ndoc = Doc.MakeDelegateWithProto(dragFactory); if (ndoc && dragFactory["dragFactory-count"] !== undefined) { dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; Doc.GetProto(ndoc).title = ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(); -- cgit v1.2.3-70-g09d2 From 7b6de64a1244ba0944edf8301a7adc2cb1445f8a Mon Sep 17 00:00:00 2001 From: dinhanhtruong <70963346+dinhanhtruong@users.noreply.github.com> Date: Sat, 24 Jul 2021 15:21:23 -0400 Subject: In-progress link popup --- .../views/collections/CollectionSchemaCells.tsx | 1 + src/client/views/linking/LinkPopup.tsx | 26 +++++++++++++++++++--- .../views/nodes/formattedText/FormattedTextBox.tsx | 3 ++- .../views/nodes/formattedText/RichTextMenu.tsx | 1 + 4 files changed, 27 insertions(+), 4 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 2e6186680..c19c6efb1 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -102,6 +102,7 @@ export class CollectionSchemaCell extends React.Component { this.props.changeFocusedCellByIndex(this.props.row, this.props.col); this.props.setPreviewDoc(this.props.rowProps.original); + console.log("click cell"); let url: string; if (url = StrCast(this.props.rowProps.row.href)) { try { diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx index 5c4640fdf..89d5b7272 100644 --- a/src/client/views/linking/LinkPopup.tsx +++ b/src/client/views/linking/LinkPopup.tsx @@ -23,6 +23,8 @@ import { DefaultStyleProvider } from '../StyleProvider'; import { Transform } from '../../util/Transform'; import { DocUtils } from '../../documents/Documents'; import { SearchBox } from '../search/SearchBox'; +import { EditorView } from 'prosemirror-view'; +import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; interface LinkPopupProps { showPopup: boolean; @@ -38,6 +40,24 @@ interface LinkPopupProps { @observer export class LinkPopup extends React.Component { + @observable private linkURL: string = ""; + @observable public view?: EditorView; + private Instance: LinkPopup = this; + + + + // TODO: should check for valid URL + @undoBatch + makeLinkToURL = (target: string, lcoation: string) => { + ((this as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, "onRadd:rightight", target, target); + } + + @action + onLinkChange = (e: React.ChangeEvent) => { + this.linkURL = e.target.value; + console.log(this.linkURL) + } + getPWidth = () => 500; getPHeight = () => 500; @@ -47,9 +67,9 @@ export class LinkPopup extends React.Component { return (
- - + +
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 5180732b9..182a40dcb 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -213,6 +213,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp AnchorMenu.Instance.Status = "marquee"; AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => { this._editorView?.state && RichTextMenu.Instance.insertHighlight(color, this._editorView.state, this._editorView?.dispatch); + console.log("highlight") return undefined; }); /** @@ -1398,7 +1399,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp SelectionManager.DeselectAll(); RichTextMenu.Instance.updateMenu(undefined, undefined, undefined); return; - case "Enter": this.insertTime(); + case "Enter": this.insertTime(); case "Tab": e.preventDefault(); break; default: if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break; case " ": diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 071491463..01de0fbe7 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -852,6 +852,7 @@ export class RichTextMenu extends AntimodeMenu { @undoBatch makeLinkToURL = (target: string, lcoation: string) => { ((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, "onRadd:rightight", target, target); + console.log((this.view as any)?.TextView); } @undoBatch -- cgit v1.2.3-70-g09d2 From 93c06962c18c916518616ab8f53944487e59fc2a Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 29 Jul 2021 12:32:19 -0400 Subject: fixed linking to text in custom text view. --- src/client/views/nodes/DocumentView.scss | 2 +- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 8f86417d6..281f25fb3 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -147,7 +147,7 @@ .documentView-titleWrapper, .documentView-titleWrapper-hover { overflow: hidden; - color: white; + color: gray; transform-origin: top left; top: 0; width: 100%; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 6dd63fb47..8cac21927 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -787,7 +787,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } componentDidMount() { - this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. + !this.props.dontSelectOnLoad && this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. this._cachedLinks = DocListCast(this.Document.links); this._disposers.breakupDictation = reaction(() => DocumentManager.Instance.RecordingEvent, this.breakupDictation); this._disposers.autoHeight = reaction(() => this.autoHeight, autoHeight => autoHeight && this.tryUpdateScrollHeight()); -- cgit v1.2.3-70-g09d2 From 0546ecf205b7d2b76f341a7157beebf95fb888a8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 1 Aug 2021 22:43:46 -0400 Subject: made url server references relative. --- src/Utils.ts | 1 - .../apis/google_docs/GooglePhotosClientUtils.ts | 2 +- src/client/documents/Documents.ts | 90 ++-------------------- src/client/util/HypothesisUtils.ts | 2 +- src/client/views/collections/CollectionSubView.tsx | 2 +- .../views/collections/CollectionTimeView.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 2 +- src/client/views/nodes/DocumentLinksButton.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 6 +- src/client/views/nodes/FieldView.tsx | 6 +- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/LinkDocPreview.tsx | 4 +- src/client/views/nodes/PDFBox.tsx | 24 ------ src/client/views/nodes/ScreenshotBox.tsx | 4 +- src/client/views/nodes/VideoBox.tsx | 8 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 6 +- .../views/nodes/formattedText/RichTextMenu.tsx | 8 +- src/client/views/pdf/AnchorMenu.tsx | 1 - src/fields/Doc.ts | 12 ++- src/fields/URLField.ts | 15 +++- src/mobile/ImageUpload.tsx | 2 +- src/server/server_Initialization.ts | 3 +- 22 files changed, 58 insertions(+), 146 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index d87c3cc6b..194c38a6f 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -67,7 +67,6 @@ export namespace Utils { export function prepend(extension: string): string { return window.location.origin + extension; } - export function fileUrl(filename: string): string { return prepend(`/files/${filename}`); } diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts index 899e65a16..ff9460b62 100644 --- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts +++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts @@ -285,7 +285,7 @@ export namespace GooglePhotos { const photos = await endpoint(); const albumId = StrCast(collection.albumId); if (albumId && albumId.length) { - const enrichment = new photos.TextEnrichment(content || Utils.prepend("/doc/" + collection[Id])); + const enrichment = new photos.TextEnrichment(content || Doc.globalServerPath(collection)); const position = new photos.AlbumPosition(photos.AlbumPosition.POSITIONS.FIRST_IN_ALBUM); const enrichmentItem = await photos.albums.addEnrichment(albumId, enrichment, position); if (enrichmentItem) { diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e863b4198..ac52b0acf 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -549,84 +549,6 @@ export namespace Docs { */ export namespace Create { - /** - * Synchronously returns a collection into which - * the device documents will be put. This is initially empty, - * but gets populated by updates from the web socket. When everything is over, - * this function cleans up after itself. - * s - * Look at Websocket.ts for the server-side counterpart to this - * function. - */ - export function Buxton() { - let responded = false; - const loading = new Doc; - loading.title = "Please wait for the import script..."; - const parent = TreeDocument([loading], { - title: "The Buxton Collection", - _width: 400, - _height: 400 - }); - const parentProto = Doc.GetProto(parent); - const { _socket } = DocServer; - - // just in case, clean up - _socket.off(MessageStore.BuxtonDocumentResult.Message); - _socket.off(MessageStore.BuxtonImportComplete.Message); - - // this is where the client handles the receipt of a new valid parsed document - Utils.AddServerHandler(_socket, MessageStore.BuxtonDocumentResult, ({ device, invalid: errors }) => { - if (!responded) { - responded = true; - parentProto.data = new List(); - } - if (device) { - const { title, __images, additionalMedia } = device; - delete device.__images; - delete device.additionalMedia; - const { ImageDocument, StackingDocument } = Docs.Create; - const constructed = __images.map(({ url, nativeWidth, nativeHeight }) => ({ url: Utils.prepend(url), nativeWidth, nativeHeight })); - const deviceImages = constructed.map(({ url, nativeWidth, nativeHeight }, i) => { - const imageDoc = ImageDocument(url, { - title: `image${i}.${extname(url)}`, - _nativeWidth: nativeWidth, - _nativeHeight: nativeHeight - }); - const media = additionalMedia[i]; - if (media) { - for (const key of Object.keys(media)) { - imageDoc[`additionalMedia_${key}`] = Utils.prepend(`/files/${key}/buxton/${media[key]}`); - } - } - return imageDoc; - }); - // the main document we create - const doc = StackingDocument(deviceImages, { title, hero: new ImageField(constructed[0].url) }); - doc.nameAliases = new List([title.toLowerCase()]); - // add the parsed attributes to this main document - Doc.Get.FromJson({ data: device, appendToExisting: { targetDoc: Doc.GetProto(doc) } }); - Doc.AddDocToList(parentProto, "data", doc); - } else if (errors) { - console.log("Documents:" + errors); - } else { - alert("A Buxton document import was completely empty (??)"); - } - }); - - // when the import is complete, we stop listening for these creation - // and termination events and alert the user - Utils.AddServerHandler(_socket, MessageStore.BuxtonImportComplete, ({ deviceCount, errorCount }) => { - _socket.off(MessageStore.BuxtonDocumentResult.Message); - _socket.off(MessageStore.BuxtonImportComplete.Message); - alert(`Successfully imported ${deviceCount} device${deviceCount === 1 ? "" : "s"}, with ${errorCount} error${errorCount === 1 ? "" : "s"}, in ${(Date.now() - startTime) / 1000} seconds.`); - }); - const startTime = Date.now(); - Utils.Emit(_socket, MessageStore.BeginBuxtonImport, ""); // signal the server to start importing - return parent; // synchronously return the collection, to be populateds - } - - Scripting.addGlobal(Buxton); - /** * This function receives the relevant document prototype and uses * it to create a new of that base-level prototype, or the @@ -675,7 +597,7 @@ export namespace Docs { } export function ImageDocument(url: string, options: DocumentOptions = {}) { - const imgField = new ImageField(new URL(url)); + const imgField = new ImageField(url); return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: path.basename(url), ...options }); } @@ -689,11 +611,11 @@ export namespace Docs { } export function VideoDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(new URL(url)), options); + return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(url), options); } export function YoutubeDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(new URL(url)), options); + return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(url), options); } export function WebCamDocument(url: string, options: DocumentOptions = {}) { @@ -709,7 +631,7 @@ export namespace Docs { } export function AudioDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(new URL(url)), + return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url), { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any }); } @@ -782,11 +704,11 @@ export namespace Docs { } export function PdfDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(new URL(url)), options); + return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(url), options); } export function WebDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.WEB), url ? new WebField(new URL(url)) : undefined, options); + return InstanceFromProto(Prototypes.get(DocumentType.WEB), url ? new WebField(url) : undefined, options); } export function HtmlDocument(html: string, options: DocumentOptions = {}) { diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts index 8ddfce772..635673025 100644 --- a/src/client/util/HypothesisUtils.ts +++ b/src/client/util/HypothesisUtils.ts @@ -126,7 +126,7 @@ export namespace Hypothesis { }); const annotationId = StrCast(linkDoc.annotationId); - const linkUrl = Utils.prepend("/doc/" + sourceDoc[Id]); + const linkUrl = Doc.globalServerPath(sourceDoc); const interval = setInterval(() => {// keep trying to edit until annotations have loaded and editing is successful !success && document.dispatchEvent(new CustomEvent<{ targetUrl: string, id: string }>("deleteLink", { detail: { targetUrl: linkUrl, id: annotationId }, diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index a5d27f038..0d9b64d24 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -303,7 +303,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: } else { const path = window.location.origin + "/doc/"; if (text.startsWith(path)) { - const docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0]; + const docid = text.replace(Doc.globalServerPath(), "").split("?")[0]; DocServer.GetRefField(docid).then(f => { if (f instanceof Doc) { if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 339163510..08b5e6bac 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -37,7 +37,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { @observable _focusRangeFilters: Opt; getAnchor = () => { - const anchor = Docs.Create.HTMLAnchorDocument({ + const anchor = Docs.Create.HTMLAnchorDocument([], { title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as any, annotationOn: this.rootDoc }); diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index a2e36f12e..82bad971d 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -196,7 +196,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent { const [{ result }] = await Networking.UploadFilesToServer(e.data); if (!(result instanceof Error)) { - this.props.Document[this.props.fieldKey] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client)); + this.props.Document[this.props.fieldKey] = new AudioField(result.accessPaths.agnostic.client); } }; this._recordStart = new Date().getTime(); diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index ddc36daa1..aa3f10188 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -194,7 +194,7 @@ export class DocumentLinksButton extends React.Component GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" }); moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" }); } - moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" }); + moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: "fingerprint" }); } } @@ -760,7 +760,7 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" }); - helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" }); + helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" }); !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" }); cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" }); } @@ -885,7 +885,7 @@ export class DocumentViewInternal extends DocComponent { const [{ result }] = await Networking.UploadFilesToServer(e.data); if (!(result instanceof Error)) { - const audioDoc = Docs.Create.AudioDocument(Utils.prepend(result.accessPaths.agnostic.client), { title: "audio test", _width: 200, _height: 32 }); + const audioDoc = Docs.Create.AudioDocument(result.accessPaths.agnostic.client, { title: "audio test", _width: 200, _height: 32 }); audioDoc.treeViewExpandedView = "layout"; const audioAnnos = Cast(self.dataDoc[self.LayoutFieldKey + "-audioAnnotations"], listSpec(Doc)); if (audioAnnos === undefined) { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 86250c9d1..ebbc1138a 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -64,9 +64,9 @@ export class FieldView extends React.Component { // else if (field instaceof PresBox) { // return ; // } - else if (field instanceof VideoField) { - return ; - } + // else if (field instanceof VideoField) { + // return ; + // } // else if (field instanceof AudioField) { // return ; //} diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index cfd43bb62..2c0106960 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -238,7 +238,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent { @computed get href() { if (this.props.hrefs?.length) { const href = this.props.hrefs[this._hrefInd]; - if (href.indexOf(Utils.prepend("/doc/")) !== 0) { // link to a web page URL -- try to show a preview + if (href.indexOf(Doc.localServerPath()) !== 0) { // link to a web page URL -- try to show a preview if (href.startsWith("https://en.wikipedia.org/wiki/")) { wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(action(summary => this._toolTipText = summary.substring(0, 500)))); } else { setTimeout(action(() => this._toolTipText = "url => " + href)); } } else { // hyperlink to a document .. decode doc id and retrieve from the server. this will trigger vals() being invalidated - const anchorDoc = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; + const anchorDoc = href.replace(Doc.localServerPath(), "").split("?")[0]; anchorDoc && DocServer.GetRefField(anchorDoc).then(action(anchor => { if (anchor instanceof Doc && DocListCast(anchor.links).length) { this._linkDoc = DocListCast(anchor.links)[0]; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 8f61e252b..0b451e2b4 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -53,30 +53,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href)); else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action(pdf => this._pdf = pdf)); } - - const backup = "oldPath"; - const href = this.pdfUrl?.url.href; - if (href) { - const pathCorrectionTest = /upload\_[a-z0-9]{32}.(.*)/g; - const matches = pathCorrectionTest.exec(href); - // console.log("\nHere's the { url } being fed into the outer regex:"); - // console.log(href); - // console.log("And here's the 'properPath' build from the captured filename:\n"); - if (matches !== null && href.startsWith(window.location.origin)) { - const properPath = Utils.prepend(`/files/pdfs/${matches[0]}`); - //console.log(properPath); - if (!properPath.includes(href)) { - console.log(`The two (url and proper path) were not equal`); - const proto = Doc.GetProto(this.props.Document); - proto[this.props.fieldKey] = new PdfField(properPath); - proto[backup] = href; - } else { - //console.log(`The two (url and proper path) were equal`); - } - } else { - console.log("Outer matches was null!"); - } - } } componentWillUnmount() { this._selectReactionDisposer?.(); } diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 700f8a7d3..0e235a62d 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -227,7 +227,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent { const [{ result }] = await Networking.UploadFilesToServer(aud_chunks); if (!(result instanceof Error)) { - this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client)); + this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(result.accessPaths.agnostic.client); } }; this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true }); @@ -244,7 +244,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent { const aspect = this.player!.videoWidth / this.player!.videoHeight; Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth); @@ -182,8 +178,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - const url = this.choosePath(Utils.prepend(relative)); + private createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => { + const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath; const width = this.layoutDoc._width || 1; const height = this.layoutDoc._height || 0; const imageSummary = Docs.Create.ImageDocument(url, { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 140d39929..f7e9ee028 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -371,7 +371,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; const anchor = Docs.Create.TextanchorDocument(); const alink = DocUtils.MakeLink({ doc: anchor }, { doc: target }, "automatic")!; - const allAnchors = [{ href: Utils.prepend("/doc/" + anchor[Id]), title: "a link", anchorId: anchor[Id] }]; + const allAnchors = [{ href: Doc.localServerPath(anchor), title: "a link", anchorId: anchor[Id] }]; const link = this._editorView!.state.schema.marks.linkAnchor.create({ allAnchors, title: "auto link", location }); tr = tr.addMark(flattened[i].from, flattened[i].to, link); }); @@ -705,7 +705,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp let tr = state.tr.addMark(sel.from, sel.to, splitter); if (sel.from !== sel.to) { const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: this._editorView?.state.doc.textBetween(sel.from, sel.to) }); - const href = targetHref ?? Utils.prepend("/doc/" + anchor[Id]); + const href = targetHref ?? Doc.localServerPath(anchor); if (anchor !== anchorDoc) this.addDocument(anchor); tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => { if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { @@ -1042,7 +1042,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } const marks = [...node.marks]; const linkIndex = marks.findIndex(mark => mark.type.name === "link"); - const allLinks = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }]; + const allLinks = [{ href: Doc.globalServerPath(linkId), title, linkId }]; const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "add:right", title, docref: true }); marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link); return node.mark(marks); diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index a6f8ff2e2..fb4114023 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -821,8 +821,8 @@ export class RichTextMenu extends AntimodeMenu { if (link) { const href = link.attrs.allAnchors.length > 0 ? link.attrs.allAnchors[0].href : undefined; if (href) { - if (href.indexOf(Utils.prepend("/doc/")) === 0) { - const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; + if (href.indexOf(Doc.localServerPath()) === 0) { + const linkclicked = href.replace(Doc.localServerPath(), "").split("?")[0]; if (linkclicked) { const linkDoc = await DocServer.GetRefField(linkclicked); if (linkDoc instanceof Doc) { @@ -864,8 +864,8 @@ export class RichTextMenu extends AntimodeMenu { const allAnchors = linkAnchor.attrs.allAnchors.slice(); this.TextView.RemoveAnchorFromSelection(allAnchors); // bcz: Argh ... this will remove the link from the document even it's anchored somewhere else in the text which happens if only part of the anchor text was selected. - allAnchors.filter((aref: any) => aref?.href.indexOf(Utils.prepend("/doc/")) === 0).forEach((aref: any) => { - const anchorId = aref.href.replace(Utils.prepend("/doc/"), "").split("?")[0]; + allAnchors.filter((aref: any) => aref?.href.indexOf(Doc.localServerPath()) === 0).forEach((aref: any) => { + const anchorId = aref.href.replace(Doc.localServerPath(), "").split("?")[0]; anchorId && DocServer.GetRefField(anchorId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc)); }); } diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 70ca19842..55816ed52 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -85,7 +85,6 @@ export class AnchorMenu extends AntimodeMenu { @action toggleLinkPopup = (e: React.MouseEvent) => { //ignore the potential null type error because this method cannot be called unless the user selects text and clicks the link button - console.log(window.getSelection().toString()) //change popup visibility field to visible this._showLinkPopup = !this._showLinkPopup; } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 111fd3f0d..a7e5d8541 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -596,7 +596,7 @@ export namespace Doc { const mapped = cloneMap.get(id); return href + (mapped ? mapped[Id] : id); }; - const regex = `(${Utils.prepend("/doc/")})([^"]*)`; + const regex = `(${Doc.localServerPath()})([^"]*)`; const re = new RegExp(regex, "g"); copy[key] = new RichTextField(field.Data.replace(/("textId":|"audioId":|"anchorId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text); }); @@ -896,6 +896,16 @@ export namespace Doc { return true; } + + // converts a document id to a url path on the server + export function globalServerPath(doc: Doc | string = ""): string { + return Utils.prepend("/doc/" + (doc instanceof Doc ? doc[Id] : doc)); + } + // converts a document id to a url path on the server + export function localServerPath(doc?: Doc): string { + return "/doc/" + (doc ? doc[Id] : ""); + } + export function overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) { const doc2Layout = Doc.Layout(doc2); const doc1Layout = Doc.Layout(doc1); diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts index fb71160ca..d96e8a70a 100644 --- a/src/fields/URLField.ts +++ b/src/fields/URLField.ts @@ -3,14 +3,17 @@ import { serializable, custom } from "serializr"; import { ObjectField } from "./ObjectField"; import { ToScriptString, ToString, Copy } from "./FieldSymbols"; import { Scripting, scriptingGlobal } from "../client/util/Scripting"; +import { Utils } from "../Utils"; function url() { return custom( function (value: URL) { - return value.href; + return value.origin === window.location.origin ? + value.pathname : + value.href; }, function (jsonValue: string) { - return new URL(jsonValue); + return new URL(jsonValue, window.location.origin); } ); } @@ -24,15 +27,21 @@ export abstract class URLField extends ObjectField { constructor(url: URL | string) { super(); if (typeof url === "string") { - url = new URL(url); + url = url.startsWith("http") ? new URL(url) : new URL(url, window.location.origin); } this.url = url; } [ToScriptString]() { + if (Utils.prepend(this.url.pathname) === this.url.href) { + return `new ${this.constructor.name}("${this.url.pathname}")`; + } return `new ${this.constructor.name}("${this.url.href}")`; } [ToString]() { + if (Utils.prepend(this.url.pathname) === this.url.href) { + return this.url.pathname; + } return this.url.href; } diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index 98696496f..f910d765e 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -50,7 +50,7 @@ export class Uploader extends React.Component { if (result instanceof Error) { return; } - const path = Utils.prepend(result.accessPaths.agnostic.client); + const path = result.accessPaths.agnostic.client; let doc = null; // Case 1: File is a video if (file.type === "video/mp4") { diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index e40f2b8e5..0f4a067fc 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -142,8 +142,9 @@ function registerCorsProxy(server: express.Express) { const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/; server.use("/corsProxy", async (req, res) => { - const requrl = decodeURIComponent(req.url.substring(1)); const referer = req.headers.referer ? decodeURIComponent(req.headers.referer) : ""; + const requrlraw = decodeURIComponent(req.url.substring(1)); + const requrl = requrlraw.startsWith("/") ? referer + requrlraw : requrlraw; // cors weirdness here... // if the referer is a cors page and the cors() route (I think) redirected to /corsProxy/ and the requested url path was relative, // then we redirect again to the cors referer and just add the relative path. -- cgit v1.2.3-70-g09d2 From 6d9c65bf95091b0d5ee7e3097995fc9e2ead4415 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 5 Aug 2021 11:21:04 -0400 Subject: improved opening sidebar when following a link to a sidebar annotation. Still needs work when link is from outside of the sidebar document and on first click. --- src/client/views/SidebarAnnos.tsx | 5 +++-- src/client/views/nodes/PDFBox.tsx | 26 +++++++++++++++------- src/client/views/nodes/WebBox.tsx | 24 ++++++++++++++------ .../views/nodes/formattedText/FormattedTextBox.tsx | 21 ++++++++++++----- 4 files changed, 53 insertions(+), 23 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 7b11723c3..f1d168a22 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -23,6 +23,7 @@ interface ExtraProps { layoutDoc: Doc; rootDoc: Doc; dataDoc: Doc; + showSidebar: boolean; whenChildContentsActiveChanged: (isActive: boolean) => void; ScreenToLocalTransform: () => Transform; sidebarAddDocument: (doc: (Doc | Doc[]), suffix: string) => boolean; @@ -75,7 +76,7 @@ export class SidebarAnnos extends React.Component { get sidebarKey() { return this.props.fieldKey + "-sidebar"; } filtersHeight = () => 38; screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(Doc.NativeWidth(this.props.dataDoc), 0).scale(this.props.scaling?.() || 1); - panelWidth = () => !this.props.layoutDoc._showSidebar ? 0 : this.props.layoutDoc.type === DocumentType.RTF ? this.props.PanelWidth() : (NumCast(this.props.layoutDoc.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.layoutDoc.nativeWidth); + panelWidth = () => !this.props.showSidebar ? 0 : this.props.layoutDoc.type === DocumentType.RTF ? this.props.PanelWidth() : (NumCast(this.props.layoutDoc.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.layoutDoc.nativeWidth); panelHeight = () => this.props.PanelHeight() - this.filtersHeight(); addDocument = (doc: Doc | Doc[]) => this.props.sidebarAddDocument(doc, this.sidebarKey); moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey); @@ -103,7 +104,7 @@ export class SidebarAnnos extends React.Component { {user}
; }; - return !this.props.layoutDoc._showSidebar ? (null) : + return !this.props.showSidebar ? (null) :
{ + if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) { + this.toggleSidebar(!smooth); + } if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1; this._initialScrollTarget = doc; return this._pdfViewer?.scrollFocus(doc, smooth); @@ -141,15 +144,18 @@ export class PDFBox extends ViewBoxAnnotatableComponent this.toggleSidebar()); } - toggleSidebar = action(() => { + toggleSidebar = action((preview: boolean = false) => { const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? 250 : 0) + nativeWidth) / nativeWidth; const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); - this.layoutDoc.nativeWidth = nativeWidth * ratio; - this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; - this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; + if (preview) this._showSidebar = true; + else { + this.layoutDoc.nativeWidth = nativeWidth * ratio; + this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; + this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; + } }); settingsPanel() { const pageBtns = <> @@ -202,7 +208,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent
; } - sidebarWidth = () => !this.layoutDoc._showSidebar ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); + sidebarWidth = () => !this.SidebarShown ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); specificContextMenu = (e: React.MouseEvent): void => { const funcs: ContextMenuProps[] = []; @@ -223,6 +229,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._sidebarRef.current?.anchorMenuClick; + @observable _showSidebar = false; + @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; } + @computed get renderPdfView() { TraceMobx(); @@ -255,6 +264,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent this.urlEditor; // controls to be added to the top bar when a document of this type is selected scrollFocus = (doc: Doc, smooth: boolean) => { + if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) { + this.toggleSidebar(!smooth); + } if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1; if (doc !== this.rootDoc && this._outerRef.current) { const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1); @@ -449,17 +452,20 @@ export class WebBox extends ViewBoxAnnotatableComponent this.toggleSidebar()); } - toggleSidebar = action(() => { + toggleSidebar = action((preview: boolean = false) => { const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? 250 : 0) + nativeWidth) / nativeWidth; const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); - this.layoutDoc.nativeWidth = nativeWidth * ratio; - this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; - this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; + if (preview) this._showSidebar; + else { + this.layoutDoc.nativeWidth = nativeWidth * ratio; + this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; + this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; + } }); - sidebarWidth = () => !this.layoutDoc._showSidebar ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); + sidebarWidth = () => !this.SidebarShown ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); @computed get content() { return
) }
; + } + @observable _showSidebar = false; + @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; } showInfo = action((anno: Opt) => this._overlayAnnoInfo = anno); setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func; @@ -548,10 +557,11 @@ export class WebBox extends ViewBoxAnnotatableComponent { + toggleSidebar = (preview: boolean = false) => { const prevWidth = this.sidebarWidth(); - this.layoutDoc._showSidebar = ((this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "50%" : "0%")) !== "0%"; - this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); + if (preview) this._showSidebar = true; + else this.layoutDoc._showSidebar = ((this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "50%" : "0%")) !== "0%"; + + this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); } sidebarDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), false); @@ -726,6 +731,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } scrollFocus = (textAnchor: Doc, smooth: boolean) => { + if (DocListCast(this.Document[this.fieldKey + "-sidebar"]).includes(textAnchor) && !this.SidebarShown) { + this.toggleSidebar(!smooth); + } const textAnchorId = textAnchor[Id]; const findAnchorFrag = (frag: Fragment, editor: EditorView) => { const nodes: Node[] = []; @@ -1468,10 +1476,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp return ComponentTag === CollectionStackingView ?
- {(this.props.noSidebar || this.Document._noSidebar) || this.props.dontSelectOnLoad || !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) : this.sidebarCollection} + {(this.props.noSidebar || this.Document._noSidebar) || this.props.dontSelectOnLoad || !this.SidebarShown || this.sidebarWidthPercent === "0%" ? (null) : this.sidebarCollection} {(this.props.noSidebar || this.Document._noSidebar) || this.props.dontSelectOnLoad || this.Document._singleLine ? (null) : this.sidebarHandle} {!this.layoutDoc._showAudio ? (null) : this.audioHandle}
-- cgit v1.2.3-70-g09d2 From 5550d58f16a0df090cde5a3605f6307b17e70758 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 5 Aug 2021 17:22:42 -0400 Subject: fixed so that links to sidebar annos show the sidebar in a LinkPreview --- src/client/views/SidebarAnnos.tsx | 3 +- src/client/views/collections/CollectionView.tsx | 5 +- src/client/views/nodes/PDFBox.tsx | 68 ++++++++++++++-------- src/client/views/nodes/WebBox.tsx | 35 ++++++----- .../views/nodes/formattedText/FormattedTextBox.tsx | 1 + src/client/views/pdf/PDFViewer.tsx | 2 - 6 files changed, 70 insertions(+), 44 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index f1d168a22..1f9763d18 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -24,6 +24,7 @@ interface ExtraProps { rootDoc: Doc; dataDoc: Doc; showSidebar: boolean; + nativeWidth: number; whenChildContentsActiveChanged: (isActive: boolean) => void; ScreenToLocalTransform: () => Transform; sidebarAddDocument: (doc: (Doc | Doc[]), suffix: string) => boolean; @@ -76,7 +77,7 @@ export class SidebarAnnos extends React.Component { get sidebarKey() { return this.props.fieldKey + "-sidebar"; } filtersHeight = () => 38; screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(Doc.NativeWidth(this.props.dataDoc), 0).scale(this.props.scaling?.() || 1); - panelWidth = () => !this.props.showSidebar ? 0 : this.props.layoutDoc.type === DocumentType.RTF ? this.props.PanelWidth() : (NumCast(this.props.layoutDoc.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.layoutDoc.nativeWidth); + panelWidth = () => !this.props.showSidebar ? 0 : this.props.layoutDoc.type === DocumentType.RTF ? this.props.PanelWidth() : (NumCast(this.props.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.nativeWidth); panelHeight = () => this.props.PanelHeight() - this.filtersHeight(); addDocument = (doc: Doc | Doc[]) => this.props.sidebarAddDocument(doc, this.sidebarKey); moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 2718cbbbf..2ae06d2f4 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -245,10 +245,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent { - console.log("Setting sub", subView); - return this._subView = subView - }), + SetSubView: action((subView: any) => this._subView = subView), addDocument: this.addDocument, moveDocument: this.moveDocument, removeDocument: this.removeDocument, diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index fc8fa4eef..b1f2070f8 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -9,7 +9,7 @@ import { makeInterface } from "../../../fields/Schema"; import { Cast, NumCast, StrCast, BoolCast } from '../../../fields/Types'; import { PdfField } from "../../../fields/URLField"; import { TraceMobx } from '../../../fields/util'; -import { Utils, setupMoveUpEvents, emptyFunction } from '../../../Utils'; +import { Utils, setupMoveUpEvents, emptyFunction, returnOne } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { KeyCodes } from '../../util/KeyCodes'; import { undoBatch } from '../../util/UndoManager'; @@ -31,6 +31,7 @@ const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @observer export class PDFBox extends ViewBoxAnnotatableComponent(PdfDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); } + public static openSidebarWidth = 250; private _searchString: string = ""; private _initialScrollTarget: Opt; private _pdfViewer: PDFViewer | undefined; @@ -146,11 +147,17 @@ export class PDFBox extends ViewBoxAnnotatableComponent this.toggleSidebar()); } + @observable _previewNativeWidth: Opt = undefined; + @observable _previewWidth: Opt = undefined; toggleSidebar = action((preview: boolean = false) => { const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); - const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? 250 : 0) + nativeWidth) / nativeWidth; + const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth; const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); - if (preview) this._showSidebar = true; + if (preview) { + this._previewNativeWidth = nativeWidth * ratio; + this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; + this._showSidebar = true; + } else { this.layoutDoc.nativeWidth = nativeWidth * ratio; this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; @@ -208,7 +215,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent
; } - sidebarWidth = () => !this.SidebarShown ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); + sidebarWidth = () => !this.SidebarShown ? 0 : + this._previewWidth ? PDFBox.openSidebarWidth : + (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); specificContextMenu = (e: React.MouseEvent): void => { const funcs: ContextMenuProps[] = []; @@ -232,38 +241,51 @@ export class PDFBox extends ViewBoxAnnotatableComponent { + return 1; + } @computed get renderPdfView() { TraceMobx(); + const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; + const scale = previewScale * (this.props.scaling?.() || 1); return
600) ? NumCast(this.Document._height) * this.props.PanelWidth() / NumCast(this.Document._width) : undefined }}>
- +
+ +
(WebDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } + public static openSidebarWidth = 250; private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); private _mainCont: React.RefObject = React.createRef(); private _outerRef: React.RefObject = React.createRef(); @@ -454,18 +455,27 @@ export class WebBox extends ViewBoxAnnotatableComponent this.toggleSidebar()); } + @observable _previewNativeWidth: Opt = undefined; + @observable _previewWidth: Opt = undefined; toggleSidebar = action((preview: boolean = false) => { const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); - const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? 250 : 0) + nativeWidth) / nativeWidth; + const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth; const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth); - if (preview) this._showSidebar; + if (preview) { + this._previewNativeWidth = nativeWidth * ratio; + this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; + this._showSidebar = true; + } else { this.layoutDoc.nativeWidth = nativeWidth * ratio; this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth; this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth; } }); - sidebarWidth = () => !this.SidebarShown ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); + sidebarWidth = () => !this.SidebarShown ? 0 : + this._previewWidth ? WebBox.openSidebarWidth : + (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / + NumCast(this.layoutDoc.nativeWidth); @computed get content() { return
this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop)); anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick; render() { - const inactiveLayer = this.props.layerProvider?.(this.layoutDoc) === false; - const scale = this.props.scaling?.() || 1; + const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined; + const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; + const scale = previewScale * (this.props.scaling?.() || 1); return (
-
+
{ e.stopPropagation(); e.preventDefault(); }} // block wheel events from propagating since they're handled by the iframe onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)} onPointerDown={this.onMarqueeDown} > -
+
{this.content} void; setPdfViewer: (view: PDFViewer) => void; ContentScaling?: () => number; - sidebarWidth: () => number; anchorMenuClick?: () => undefined | ((anchor: Doc) => void); } @@ -550,7 +549,6 @@ export class PDFViewer extends React.Component { onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick} style={{ overflowX: this._zoomed !== 1 ? "scroll" : undefined, - width: !this.props.Document._fitWidth && (window.screen.width > 600) ? Doc.NativeWidth(this.props.Document) - this.props.sidebarWidth() / this.contentScaling : `calc(${100 / this.contentScaling}% - ${this.props.sidebarWidth() / this.contentScaling}px)`, height: !this.props.Document._fitWidth && (window.screen.width > 600) ? Doc.NativeHeight(this.props.Document) : `${100 / this.contentScaling}%`, transform: `scale(${this.contentScaling})` }} > -- cgit v1.2.3-70-g09d2 From d756ffd3daa24270361648454062e98f190ae964 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 6 Aug 2021 10:34:57 -0400 Subject: fixed webboxes so that you can link to an annotation and it will restore the url as well. a bunch of warnings cleanup. --- src/client/util/DocumentManager.ts | 6 ++---- src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/InkStrokeProperties.ts | 2 +- src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 4 ++-- src/client/views/linking/LinkPopup.tsx | 1 - src/client/views/nodes/DocumentLinksButton.tsx | 4 ++-- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 15 ++++++++++----- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 1 - src/fields/Doc.ts | 2 +- 12 files changed, 22 insertions(+), 21 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 27ae0447a..cb0ee411c 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -163,7 +163,8 @@ export class DocumentManager { const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined; const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined; const targetDocContext = contextDoc || annotatedDoc; - var targetDocContextView = targetDocContext && getFirstDocView(targetDocContext); + const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) || + (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView; if (!docView && annoContainerView) { annoContainerView.focus(targetDoc); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below @@ -181,9 +182,6 @@ export class DocumentManager { if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc))); } else { // otherwise try to get a view of the context of the target - if (annoContainerView && wasHidden) { // if we have an annotation container and the target was hidden, then try again because we just un-hid the document above - targetDocContextView = annoContainerView; - } if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first.. targetDocContext._viewTransition = "transform 500ms"; targetDocContextView.props.focus(targetDocContextView.rootDoc, { diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index df1e6899d..5f09a322c 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -355,7 +355,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
- {(DocumentLinksButton.StartLink || Doc.UserDoc()["documentLinksButton-fullMenu"]) && DocumentLinksButton.StartLink != doc ?
+ {(DocumentLinksButton.StartLink || Doc.UserDoc()["documentLinksButton-fullMenu"]) && DocumentLinksButton.StartLink !== doc ?
: (null)} {/*!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) :
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 76ca5b5ec..6444e4451 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -283,7 +283,7 @@ export class InkStrokeProperties { */ @action rotatePoint = (target: PointData, origin: PointData, angle: number) => { - let rotatedTarget = { X: target.X - origin.X, Y: target.Y - origin.Y }; + const rotatedTarget = { X: target.X - origin.X, Y: target.Y - origin.Y }; const newX = Math.cos(angle) * rotatedTarget.X - Math.sin(angle) * rotatedTarget.Y; const newY = Math.sin(angle) * rotatedTarget.X + Math.cos(angle) * rotatedTarget.Y; rotatedTarget.X = newX + origin.X; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index a9b5ce465..227635c9b 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -93,7 +93,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: childDocFilters = () => [...this.props.docFilters(), ...this.collectionFilters()]; childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()]; IsFiltered = () => this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" : - this.props.docFilters().length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined; + this.props.docFilters().length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs); @computed.struct get childDocs() { TraceMobx(); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 2ae06d2f4..e65ebf075 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -236,7 +236,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent { const stack: HTMLDivElement = tab.contentItem.parent; const header: HTMLDivElement = tab; stack.onscroll = action((e: any) => { - console.log('scrolling...') - }) + console.log('scrolling...'); + }); const moreInfoDrag = document.createElement("div"); moreInfoDrag.className = "lm_iconWrap"; tab._disposers.buttonDisposer = reaction(() => this.view, view => diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx index 2c4b718f4..df469c53b 100644 --- a/src/client/views/linking/LinkPopup.tsx +++ b/src/client/views/linking/LinkPopup.tsx @@ -54,7 +54,6 @@ export class LinkPopup extends React.Component { @action onLinkChange = (e: React.ChangeEvent) => { this.linkURL = e.target.value; - console.log(this.linkURL) } diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index b63174e54..7648e866e 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -114,7 +114,7 @@ export class DocumentLinksButton extends React.Component - ) + ); } render() { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index b1f2070f8..23236cf20 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -217,7 +217,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent !this.SidebarShown ? 0 : this._previewWidth ? PDFBox.openSidebarWidth : - (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); + (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth) specificContextMenu = (e: React.MouseEvent): void => { const funcs: ContextMenuProps[] = []; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 751d63711..ca281d68f 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -164,7 +164,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this.urlEditor; // controls to be added to the top bar when a document of this type is selected scrollFocus = (doc: Doc, smooth: boolean) => { - if (StrCast(doc.data) !== this._url) this.submitURL(StrCast(doc.data)); + if (StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl)); if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) { this.toggleSidebar(!smooth); } @@ -191,7 +191,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { + (doc instanceof Doc ? [doc] : doc).forEach(doc => doc.webUrl = this._url); + return this.addDocument(doc, annotationKey); + } + sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => { if (!this.layoutDoc._showSidebar) this.toggleSidebar(); - return this.addDocument(doc, sidebarKey); + return this.addDocumentWrapper(doc, sidebarKey); } sidebarBtnDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, (e, down, delta) => { @@ -482,7 +487,7 @@ export class WebBox extends ViewBoxAnnotatableComponent !this.SidebarShown ? 0 : this._previewWidth ? WebBox.openSidebarWidth : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / - NumCast(this.layoutDoc.nativeWidth); + NumCast(this.layoutDoc.nativeWidth) @computed get content() { return
{ this._editorView?.state && RichTextMenu.Instance.insertHighlight(color, this._editorView.state, this._editorView?.dispatch); - console.log("highlight") return undefined; }); /** diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 6dcf34a3a..0cbfaf067 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1214,7 +1214,7 @@ export namespace Doc { case DocumentType.RTF: return "sticky-note"; case DocumentType.COL: const folder: IconProp = isOpen ? "folder-open" : "folder"; - const chevron: IconProp = isOpen ? "chevron-down" : "chevron-right" + const chevron: IconProp = isOpen ? "chevron-down" : "chevron-right"; return !doc?.isFolder ? folder : chevron; case DocumentType.WEB: return "globe-asia"; case DocumentType.SCREENSHOT: return "photo-video"; -- cgit v1.2.3-70-g09d2 From e7bbdd3b489fea1c508af53345cd0d1f31685cb9 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 6 Aug 2021 18:03:38 -0400 Subject: collabortion fixes: added new acl for allowing people to edit their own text within the same note, fixed playground fields to write to the server without updating other clients. --- src/client/DocServer.ts | 5 +- src/client/documents/Documents.ts | 10 ++-- src/client/util/CurrentUserUtils.ts | 6 +- src/client/util/SharingManager.tsx | 13 ++-- src/client/views/DocComponent.tsx | 6 +- src/client/views/MainView.tsx | 2 +- src/client/views/MarqueeAnnotator.tsx | 4 +- src/client/views/PropertiesView.tsx | 14 ++--- .../collections/collectionFreeForm/MarqueeView.tsx | 4 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 12 +++- .../formattedText/ProsemirrorExampleTransfer.ts | 70 ++++++++++++++-------- src/fields/Doc.ts | 6 +- src/fields/util.ts | 22 ++++--- 13 files changed, 105 insertions(+), 69 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 59278d2af..d9ae7d64c 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -59,7 +59,10 @@ export namespace DocServer { export var PlaygroundFields: string[]; export function setPlaygroundFields(livePlaygroundFields: string[]) { DocServer.PlaygroundFields = livePlaygroundFields; - livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.Playground)); + livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground)); + } + export function IsPlaygroundField(field: string) { + return DocServer.PlaygroundFields?.includes(field.replace(/^_/, "")); } export function setFieldWriteMode(field: string, writeMode: WriteMode) { diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 24f777e88..93f0880a4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -577,7 +577,7 @@ export namespace Docs { dataProps.creationDate = new DateField; dataProps[`${fieldKey}-lastModified`] = new DateField; dataProps["acl-Override"] = "None"; - dataProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; + dataProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; dataProps[fieldKey] = data; @@ -588,7 +588,7 @@ export namespace Docs { viewProps.author = Doc.CurrentUserEmail; viewProps["acl-Override"] = "None"; - viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; + viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewProps, true, true); ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc); @@ -699,7 +699,7 @@ export namespace Docs { I.author = Doc.CurrentUserEmail; I.rotation = 0; I.data = new InkField(points); - I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; + I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; I["acl-Override"] = "None"; I[Initializing] = false; return I; @@ -1038,8 +1038,8 @@ export namespace DocUtils { title: ComputedField.MakeFunction("generateLinkTitle(self)") as any, "anchor1-useLinkSmallAnchor": source.doc.useLinkSmallAnchor ? true : undefined, "anchor2-useLinkSmallAnchor": target.doc.useLinkSmallAnchor ? true : undefined, - "acl-Public": SharingPermissions.Add, - "_acl-Public": SharingPermissions.Add, + "acl-Public": SharingPermissions.Augment, + "_acl-Public": SharingPermissions.Augment, layout_linkView: Cast(Cast(Doc.UserDoc()["template-button-link"], Doc, null).dragFactory, Doc, null), linkDisplay: true, hidden: true, linkRelationship, diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 1af6607a7..14c43fb1c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -917,7 +917,7 @@ export class CurrentUserUtils { linkDocs = new Doc(linkDatabaseId, true); (linkDocs as Doc).author = Doc.CurrentUserEmail; (linkDocs as Doc).data = new List([]); - (linkDocs as Doc)["acl-Public"] = SharingPermissions.Add; + (linkDocs as Doc)["acl-Public"] = SharingPermissions.Augment; } doc.myLinkDatabase = new PrefetchProxy(linkDocs); } @@ -926,10 +926,10 @@ export class CurrentUserUtils { if (!sharedDocs) { sharedDocs = Docs.Create.TreeDocument([], { title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, - _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Add, "_acl-Public": SharingPermissions.Add, + _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, _chromeHidden: true, boxShadow: "0 0", }, sharingDocumentId + "outer", sharingDocumentId); - (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Add; + (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment; } if (sharedDocs instanceof Doc) { Doc.GetProto(sharedDocs).userColor = sharedDocs.userColor || "rgb(202, 202, 202)"; diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index d283510b7..a8972b988 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -5,7 +5,7 @@ import { observer } from "mobx-react"; import * as React from "react"; import Select from "react-select"; import * as RequestPromise from "request-promise"; -import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc"; +import { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt, AclSelfEdit } from "../../fields/Doc"; import { List } from "../../fields/List"; import { Cast, NumCast, StrCast } from "../../fields/Types"; import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from "../../fields/util"; @@ -85,7 +85,8 @@ export class SharingManager extends React.Component<{}> { private AclMap = new Map([ [AclPrivate, SharingPermissions.None], [AclReadonly, SharingPermissions.View], - [AclAddonly, SharingPermissions.Add], + [AclAugment, SharingPermissions.Augment], + [AclSelfEdit, SharingPermissions.SelfEdit], [AclEdit, SharingPermissions.Edit], [AclAdmin, SharingPermissions.Admin] ]); @@ -101,7 +102,7 @@ export class SharingManager extends React.Component<{}> { this.targetDoc = target_doc || target?.props.Document; DictationOverlay.Instance.hasActiveModal = true; this.isOpen = this.targetDoc !== undefined; - this.permissions = SharingPermissions.Add; + this.permissions = SharingPermissions.Augment; }); } @@ -366,10 +367,10 @@ export class SharingManager extends React.Component<{}> { const dropdownValues: string[] = Object.values(SharingPermissions); if (!uniform) dropdownValues.unshift("-multiple-"); if (override) dropdownValues.unshift("None"); - return dropdownValues.filter(permission => permission !== SharingPermissions.View).map(permission => + return dropdownValues.filter(permission => !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission => ( ) ); @@ -546,7 +547,7 @@ export class SharingManager extends React.Component<{}> { ) : (
- {permissions === SharingPermissions.Add ? "Can Augment" : permissions} + {permissions}
)}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index fc36c7e43..99c695a4a 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -1,4 +1,4 @@ -import { Doc, Opt, DataSym, AclReadonly, AclAddonly, AclPrivate, AclEdit, AclSym, DocListCastAsync, DocListCast, AclAdmin } from '../../fields/Doc'; +import { Doc, Opt, DataSym, AclReadonly, AclAugment, AclPrivate, AclEdit, AclSym, DocListCast, AclAdmin, AclSelfEdit } from '../../fields/Doc'; import { Touchable } from './Touchable'; import { computed, action, observable } from 'mobx'; import { Cast, BoolCast, ScriptCast } from '../../fields/Types'; @@ -131,7 +131,7 @@ export function ViewBoxAnnotatableComponent

effectiveAcl === AclEdit || effectiveAcl === AclAdmin || GetEffectiveAcl(doc) === AclAdmin); + const docs = indocs.filter(doc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(doc) === AclAdmin); if (docs.length) { setTimeout(() => docs.map(doc => { // this allows 'addDocument' to see the annotationOn field in order to create a pushin Doc.SetInPlace(doc, "isPushpin", undefined, true); @@ -199,7 +199,7 @@ export function ViewBoxAnnotatableComponent

{ if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); doc.context = this.props.Document; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 005e46836..8f37172a0 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -107,7 +107,7 @@ export class MainView extends React.Component { new InkStrokeProperties(); this._sidebarContent.proto = undefined; if (!MainView.Live) { - DocServer.setPlaygroundFields(["x", "y", "dataTransition", "_autoHeight", "_showSidebar", "showSidebar", "_sidebarWidthPercent", "_width", "_height", "width", "height", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeHidden", "nativeWidth", "_nativeWidth"]); // can play with these fields on someone else's + DocServer.setPlaygroundFields(["dataTransition", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition", "panX", "panY", "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's } DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg))); diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 805cda95c..a3a3bce56 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -1,6 +1,6 @@ import { action, observable, ObservableMap, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../fields/Doc"; +import { AclAugment, AclAdmin, AclEdit, DataSym, Doc, Opt, AclSelfEdit } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { List } from "../../fields/List"; import { NumCast } from "../../fields/Types"; @@ -156,7 +156,7 @@ export class MarqueeAnnotator extends React.Component { highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap) => { // creates annotation documents for current highlights const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]); - const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations); + const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations); !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc); return annotationDoc as Doc ?? undefined; } diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 8136edf04..de437e1df 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -5,7 +5,7 @@ import { intersection } from "lodash"; import { action, autorun, computed, Lambda, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { ColorState, SketchPicker } from "react-color"; -import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, Opt, WidthSym } from "../../fields/Doc"; +import { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, Opt, WidthSym, AclSelfEdit } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { InkField } from "../../fields/InkField"; import { ComputedField } from "../../fields/ScriptField"; @@ -342,12 +342,9 @@ export class PropertiesView extends React.Component { return ; } @@ -402,7 +399,8 @@ export class PropertiesView extends React.Component { [AclUnset, "None"], [AclPrivate, SharingPermissions.None], [AclReadonly, SharingPermissions.View], - [AclAddonly, SharingPermissions.Add], + [AclAugment, SharingPermissions.Augment], + [AclSelfEdit, SharingPermissions.SelfEdit], [AclEdit, SharingPermissions.Edit], [AclAdmin, SharingPermissions.Admin] ]); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 846d28214..19da7ea00 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -1,6 +1,6 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../../../fields/Doc"; +import { AclAugment, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../../../fields/Doc"; import { Id } from "../../../../fields/FieldSymbols"; import { InkData, InkField, InkTool } from "../../../../fields/InkField"; import { List } from "../../../../fields/List"; @@ -298,7 +298,7 @@ export class MarqueeView extends React.Component json?.indexOf("\"storedMarks\"") === -1 ? json?.replace(/"selection":.*/, "") : json?.replace(/"selection":"\"storedMarks\""/, "\"storedMarks\""); - if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { + if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin || effectiveAcl === AclSelfEdit) { const accumTags = [] as string[]; state.tr.doc.nodesBetween(0, state.doc.content.size, (node: any, pos: number, parent: any) => { if (node.type === schema.nodes.dashField && node.attrs.fieldKey.startsWith("#")) { @@ -1401,6 +1401,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._rules!.EnteringStyle = false; } e.stopPropagation(); + for (var i = state.selection.from; i < state.selection.to; i++) { + const node = state.doc.resolve(i); + if (node?.marks?.().some(mark => mark.type === schema.marks.user_mark && + mark.attrs.userid !== Doc.CurrentUserEmail) && + [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) { + e.preventDefault(); + } + } switch (e.key) { case "Escape": this._editorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from))); diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index d5c77786c..1f78b2204 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -7,13 +7,14 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list"; import { EditorState, Transaction, TextSelection } from "prosemirror-state"; import { SelectionManager } from "../../../util/SelectionManager"; import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types"; -import { Doc, DataSym, DocListCast } from "../../../../fields/Doc"; +import { Doc, DataSym, DocListCast, AclAugment } from "../../../../fields/Doc"; import { FormattedTextBox } from "./FormattedTextBox"; import { Id } from "../../../../fields/FieldSymbols"; import { Docs } from "../../../documents/Documents"; import { Utils } from "../../../../Utils"; import { listSpec } from "../../../../fields/Schema"; import { List } from "../../../../fields/List"; +import { GetEffectiveAcl } from "../../../../fields/util"; const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false; @@ -70,25 +71,39 @@ export function buildKeymap>(schema: S, props: any, mapKey return false; }; + const canEdit = (state: any) => { + for (var i = state.selection.from; i < state.selection.to; i++) { + const node = state.doc.resolve(i); + if (node?.marks?.().some((mark: any) => mark.type === schema.marks.user_mark && + mark.attrs.userid !== Doc.CurrentUserEmail) && + GetEffectiveAcl(props.Document) === AclAugment) { + return false; + } + } + return true; + } + + const toggleEditableMark = (mark: any) => (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && toggleMark(mark)(state, dispatch); + //History commands bind("Mod-z", undo); bind("Shift-Mod-z", redo); !mac && bind("Mod-y", redo); //Commands to modify Mark - bind("Mod-b", toggleMark(schema.marks.strong)); - bind("Mod-B", toggleMark(schema.marks.strong)); + bind("Mod-b", toggleEditableMark(schema.marks.strong)); + bind("Mod-B", toggleEditableMark(schema.marks.strong)); - bind("Mod-e", toggleMark(schema.marks.em)); - bind("Mod-E", toggleMark(schema.marks.em)); + bind("Mod-e", toggleEditableMark(schema.marks.em)); + bind("Mod-E", toggleEditableMark(schema.marks.em)); - bind("Mod-*", toggleMark(schema.marks.code)); + bind("Mod-*", toggleEditableMark(schema.marks.code)); - bind("Mod-u", toggleMark(schema.marks.underline)); - bind("Mod-U", toggleMark(schema.marks.underline)); + bind("Mod-u", toggleEditableMark(schema.marks.underline)); + bind("Mod-U", toggleEditableMark(schema.marks.underline)); //Commands for lists - bind("Ctrl-i", wrapInList(schema.nodes.ordered_list)); + bind("Ctrl-i", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state, dispatch as any)); bind("Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { /// bcz; Argh!! replace layotuTEmpalteString with a onTab prop conditionally handles Tab); @@ -96,6 +111,7 @@ export function buildKeymap>(schema: S, props: any, mapKey if (!props.LayoutTemplateString) return addTextBox(false, true); return true; } + if (!canEdit(state)) return true; const ref = state.selection; const range = ref.$from.blockRange(ref.$to); const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); @@ -121,6 +137,7 @@ export function buildKeymap>(schema: S, props: any, mapKey bind("Shift-Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { /// bcz; Argh!! replace with a onShiftTab prop conditionally handles Tab); if (props.Document._singleLine) return true; + if (!canEdit(state)) return true; const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); if (!liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => { @@ -140,24 +157,19 @@ export function buildKeymap>(schema: S, props: any, mapKey }); //Commands to modify BlockType - bind("Ctrl->", wrapIn(schema.nodes.blockquote)); - bind("Alt-\\", setBlockType(schema.nodes.paragraph)); - bind("Shift-Ctrl-\\", setBlockType(schema.nodes.code_block)); + bind("Ctrl->", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit((state) && wrapIn(schema.nodes.blockquote)(state, dispatch as any))); + bind("Alt-\\", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state, dispatch as any)); + bind("Shift-Ctrl-\\", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state, dispatch as any)); - bind("Ctrl-m", (state: EditorState, dispatch: (tx: Transaction) => void) => { - dispatch(state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: "math" + Utils.GenerateGuid() }))); - }); + bind("Ctrl-m", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: "math" + Utils.GenerateGuid() })))); for (let i = 1; i <= 6; i++) { - bind("Shift-Ctrl-" + i, setBlockType(schema.nodes.heading, { level: i })); + bind("Shift-Ctrl-" + i, (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state, dispatch as any)); } //Command to create a horizontal break line const hr = schema.nodes.horizontal_rule; - bind("Mod-_", (state: EditorState, dispatch: (tx: Transaction) => void) => { - dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView()); - return true; - }); + bind("Mod-_", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView())); //Command to unselect all bind("Escape", (state: EditorState, dispatch: (tx: Transaction) => void) => { @@ -173,13 +185,15 @@ export function buildKeymap>(schema: S, props: any, mapKey }; //Command to create a text document to the right of the selected textbox - bind("Alt-Enter", (state: EditorState, dispatch: (tx: Transaction>) => void) => addTextBox(false, true)); + bind("Alt-Enter", () => addTextBox(false, true)); //Command to create a text document to the bottom of the selected textbox - bind("Ctrl-Enter", (state: EditorState, dispatch: (tx: Transaction) => void) => addTextBox(true, true)); + bind("Ctrl-Enter", () => addTextBox(true, true)); // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward); bind("Backspace", (state: EditorState, dispatch: (tx: Transaction>) => void) => { + if (!canEdit(state)) return true; + if (!deleteSelection(state, (tx: Transaction>) => { dispatch(updateBullets(tx, schema)); })) { @@ -200,6 +214,9 @@ export function buildKeymap>(schema: S, props: any, mapKey //command to break line bind("Enter", (state: EditorState, dispatch: (tx: Transaction>) => void) => { if (addTextBox(true, false)) return true; + + if (!canEdit(state)) return true; + const trange = state.selection.$from.blockRange(state.selection.$to); const path = (state.selection.$from as any).path; const depth = trange ? liftTarget(trange) : undefined; @@ -238,18 +255,19 @@ export function buildKeymap>(schema: S, props: any, mapKey //Command to create a blank space bind("Space", (state: EditorState, dispatch: (tx: Transaction) => void) => { + if (!canEdit(state)) return true; const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); dispatch(splitMetadata(marks, state.tr)); return false; }); - bind("Alt-ArrowUp", joinUp); - bind("Alt-ArrowDown", joinDown); - bind("Mod-BracketLeft", lift); + bind("Alt-ArrowUp", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && joinUp(state, dispatch as any)); + bind("Alt-ArrowDown", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && joinDown(state, dispatch as any)); + bind("Mod-BracketLeft", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && lift(state, dispatch as any)); const cmd = chainCommands(exitCode, (state, dispatch) => { if (dispatch) { - dispatch(state.tr.replaceSelectionWith(schema.nodes.hard_break.create()).scrollIntoView()); + canEdit(state) && dispatch(state.tr.replaceSelectionWith(schema.nodes.hard_break.create()).scrollIntoView()); return true; } return false; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 0cbfaf067..85ea3cfa9 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -90,7 +90,8 @@ export const DirectLinksSym = Symbol("DirectLinks"); export const AclUnset = Symbol("AclUnset"); export const AclPrivate = Symbol("AclOwnerOnly"); export const AclReadonly = Symbol("AclReadOnly"); -export const AclAddonly = Symbol("AclAddonly"); +export const AclAugment = Symbol("AclAugment"); +export const AclSelfEdit = Symbol("AclSelfEdit"); export const AclEdit = Symbol("AclEdit"); export const AclAdmin = Symbol("AclAdmin"); export const UpdatingFromServer = Symbol("UpdatingFromServer"); @@ -102,7 +103,8 @@ const AclMap = new Map([ ["None", AclUnset], [SharingPermissions.None, AclPrivate], [SharingPermissions.View, AclReadonly], - [SharingPermissions.Add, AclAddonly], + [SharingPermissions.Augment, AclAugment], + [SharingPermissions.SelfEdit, AclSelfEdit], [SharingPermissions.Edit, AclEdit], [SharingPermissions.Admin, AclAdmin] ]); diff --git a/src/fields/util.ts b/src/fields/util.ts index 526e5af72..2bb6b45c2 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -1,5 +1,5 @@ import { UndoManager } from "../client/util/UndoManager"; -import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, DataSym, DocListCast, AclAdmin, HeightSym, WidthSym, updateCachedAcls, AclUnset, DocListCastAsync, ForceServerWrite, Initializing } from "./Doc"; +import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAugment, AclSym, DataSym, DocListCast, AclAdmin, HeightSym, WidthSym, updateCachedAcls, AclUnset, DocListCastAsync, ForceServerWrite, Initializing, AclSelfEdit } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; @@ -14,6 +14,7 @@ import CursorField from "./CursorField"; import { List } from "./List"; import { SnappingManager } from "../client/util/SnappingManager"; import { computedFn } from "mobx-utils"; +import { RichTextField } from "./RichTextField"; function _readOnlySetter(): never { throw new Error("Documents can't be modified in read-only mode"); @@ -77,7 +78,9 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number const fromServer = target[UpdatingFromServer]; const sameAuthor = fromServer || (receiver.author === Doc.CurrentUserEmail); const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (writeMode !== DocServer.WriteMode.LiveReadonly); - const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode === DocServer.WriteMode.Default) && !DocServer.Control.isReadOnly();// && !playgroundMode; + const writeToServer = + (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && (value instanceof RichTextField))) && + !DocServer.Control.isReadOnly(); if (writeToDoc) { if (value === undefined) { @@ -157,9 +160,10 @@ export function inheritParentAcls(parent: Doc, child: Doc) { */ export enum SharingPermissions { Admin = "Admin", - Edit = "Can Edit", - Add = "Can Augment", - View = "Can View", + Edit = "Edit", + SelfEdit = "Self Edit", + Augment = "Augment", + View = "View", None = "Not Shared" } @@ -176,7 +180,7 @@ export function GetEffectiveAcl(target: any, user?: string): symbol { function getPropAcl(target: any, prop: string | symbol | number) { if (prop === UpdatingFromServer || prop === Initializing || target[UpdatingFromServer] || prop === AclSym) return AclAdmin; // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent - if (prop && DocServer.PlaygroundFields?.includes(prop.toString())) return AclEdit; // playground props are always editable + if (prop && DocServer.IsPlaygroundField(prop.toString())) return AclEdit; // playground props are always editable return GetEffectiveAcl(target); } @@ -192,7 +196,8 @@ function getEffectiveAcl(target: any, user?: string): symbol { HierarchyMapping = HierarchyMapping || new Map([ [AclPrivate, 0], [AclReadonly, 1], - [AclAddonly, 2], + [AclAugment, 2], + [AclSelfEdit, 2.5], [AclEdit, 3], [AclAdmin, 4] ]); @@ -235,6 +240,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc ["Not Shared", 0], ["Can View", 1], ["Can Augment", 2], + ["Self Edit", 2.5], ["Can Edit", 3], ["Admin", 4] ]); @@ -294,7 +300,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean { let prop = in_prop; const effectiveAcl = getPropAcl(target, prop); - if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin) return true; + if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) return true; // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't if (typeof prop === "string" && prop.startsWith("acl") && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined, "None"].includes(value))) return true; // if (typeof prop === "string" && prop.startsWith("acl") && !["Can Edit", "Can Augment", "Can View", "Not Shared", undefined].includes(value)) return true; -- cgit v1.2.3-70-g09d2 From 81ae5175aebf7e14a781a73d4a7cbaec0b8f3e4a Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 9 Aug 2021 14:26:14 -0400 Subject: fixed augment only text to resize correctly with autoheight. fixed acls to cache correctly for new documents. fixed links to be shared across users. fixed links to always default to public. --- src/client/DocServer.ts | 2 +- src/client/documents/Documents.ts | 8 ++- src/client/util/CurrentUserUtils.ts | 16 +++--- src/client/util/LinkManager.ts | 67 ++++++++++++++-------- src/client/util/SettingsManager.tsx | 3 +- src/client/util/SharingManager.tsx | 4 +- src/client/views/MainView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 20 ++++--- src/fields/util.ts | 4 +- 9 files changed, 79 insertions(+), 47 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 5712a1d64..f4e3a22cf 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,6 +1,6 @@ import * as io from 'socket.io-client'; import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "./../server/Message"; -import { Opt, Doc, UpdatingFromServer } from '../fields/Doc'; +import { Opt, Doc, UpdatingFromServer, updateCachedAcls } from '../fields/Doc'; import { Utils, emptyFunction } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; import { RefField } from '../fields/RefField'; diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 93f0880a4..48886aa3b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,7 +1,7 @@ import { action, runInAction } from "mobx"; import { basename, extname } from "path"; import { DateField } from "../../fields/DateField"; -import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, Initializing } from "../../fields/Doc"; +import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, Initializing, updateCachedAcls } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { HtmlField } from "../../fields/HtmlField"; import { InkField } from "../../fields/InkField"; @@ -577,7 +577,7 @@ export namespace Docs { dataProps.creationDate = new DateField; dataProps[`${fieldKey}-lastModified`] = new DateField; dataProps["acl-Override"] = "None"; - dataProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; dataProps[fieldKey] = data; @@ -588,13 +588,15 @@ export namespace Docs { viewProps.author = Doc.CurrentUserEmail; viewProps["acl-Override"] = "None"; - viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + viewProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewProps, true, true); ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc); !Doc.IsSystem(dataDoc) && ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) && !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", dataDoc); + updateCachedAcls(dataDoc); + updateCachedAcls(viewDoc); return viewDoc; } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 14c43fb1c..34990e121 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -2,13 +2,14 @@ import { computed, observable, reaction } from "mobx"; import * as rp from 'request-promise'; import { DataSym, Doc, DocListCast, DocListCastAsync } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; +import { InkTool } from "../../fields/InkField"; import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; -import { BoolCast, Cast, NumCast, PromiseValue, StrCast, DateCast } from "../../fields/Types"; +import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; import { nullAudio } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { Utils } from "../../Utils"; @@ -19,6 +20,7 @@ import { Networking } from "../Network"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; +import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; import { LabelBox } from "../views/nodes/LabelBox"; @@ -31,13 +33,10 @@ import { LinkManager } from "./LinkManager"; import { Scripting } from "./Scripting"; import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; -import { UndoManager } from "./UndoManager"; -import { SnappingManager } from "./SnappingManager"; -import { InkTool } from "../../fields/InkField"; -import { SharingManager } from "./SharingManager"; -import { computedFn } from "mobx-utils"; import { ColorScheme } from "./SettingsManager"; -import { Colors } from "../views/global/globalEnums"; +import { SharingManager } from "./SharingManager"; +import { SnappingManager } from "./SnappingManager"; +import { UndoManager } from "./UndoManager"; export let resolvedPorts: { server: number, socket: number }; @@ -915,6 +914,7 @@ export class CurrentUserUtils { let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId); if (!linkDocs) { linkDocs = new Doc(linkDatabaseId, true); + (linkDocs as Doc).title = "LINK DATABASE: " + Doc.CurrentUserEmail; (linkDocs as Doc).author = Doc.CurrentUserEmail; (linkDocs as Doc).data = new List([]); (linkDocs as Doc)["acl-Public"] = SharingPermissions.Augment; @@ -1029,7 +1029,7 @@ export class CurrentUserUtils { doc.fontFamily = StrCast(doc.fontFamily, "Arial"); doc.fontColor = StrCast(doc.fontColor, "black"); doc.fontHighlight = StrCast(doc.fontHighlight, ""); - doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, true); + doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false); doc.activeCollectionBackground = StrCast(doc.activeCollectionBackground, "white"); doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null); doc.noviceMode = BoolCast(doc.noviceMode, true); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 8abfd740c..933b98a8c 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,4 +1,4 @@ -import { observable, observe, action } from "mobx"; +import { observable, observe, action, reaction, computed } from "mobx"; import { computedFn } from "mobx-utils"; import { DirectLinksSym, Doc, DocListCast, Field, Opt } from "../../fields/Doc"; import { List } from "../../fields/List"; @@ -26,36 +26,41 @@ type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => vo export class LinkManager { @observable static _instance: LinkManager; - @observable static userDocs: Doc[] = []; + @observable static userLinkDBs: Doc[] = []; public static currentLink: Opt; public static get Instance() { return LinkManager._instance; } + public static addLinkDB = (linkDb: any) => LinkManager.userLinkDBs.push(linkDb); + static links: Doc[] = []; constructor() { LinkManager._instance = this; setTimeout(() => { - LinkManager.userDocs = [Doc.LinkDBDoc().data as Doc, ...SharingManager.Instance.users.map(user => user.linkDatabase)]; - const addLinkToDoc = action((link: Doc): any => { + LinkManager.userLinkDBs = []; + const addLinkToDoc = (link: Doc) => { const a1 = link?.anchor1; const a2 = link?.anchor2; - if (a1 instanceof Promise || a2 instanceof Promise) return PromiseValue(a1).then(a1 => PromiseValue(a2).then(a2 => addLinkToDoc(link))); - 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); - } - }); - const remLinkFromDoc = action((link: Doc): any => { + 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].add(link); + Doc.GetProto(a2)[DirectLinksSym].add(link); + Doc.GetProto(link)[DirectLinksSym].add(link); + } + })); + } + const remLinkFromDoc = (link: Doc) => { const a1 = link?.anchor1; const a2 = link?.anchor2; - if (a1 instanceof Promise || a2 instanceof Promise) return PromiseValue(a1).then(a1 => PromiseValue(a2).then(a2 => remLinkFromDoc(link))); - 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); - } - }); - const watchUserLinks = (userLinks: List) => { + 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); + } + })); + } + 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(userLinks, change => { + 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))); @@ -64,13 +69,29 @@ export class LinkManager { 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); }; - observe(LinkManager.userDocs, change => { + observe(LinkManager.userLinkDBs, change => { switch (change.type as any) { - case "splice": (change as any).added.forEach(watchUserLinks); break; + case "splice": (change as any).added.forEach(watchUserLinkDB); break; case "update": //let oldValue = change.oldValue; } }, true); + LinkManager.addLinkDB(Doc.LinkDBDoc()); }); } diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 3987497b8..bd91db779 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -268,7 +268,8 @@ export class SettingsManager extends React.Component<{}> {

- Doc.UserDoc().defaultAclPrivate = !Doc.UserDoc().defaultAclPrivate)} /> + Doc.UserDoc().defaultAclPrivate = !Doc.UserDoc().defaultAclPrivate)} />
Default access private
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index a8972b988..6c4556250 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -23,6 +23,7 @@ import { GroupManager, UserOptions } from "./GroupManager"; import { GroupMemberView } from "./GroupMemberView"; import { SelectionManager } from "./SelectionManager"; import "./SharingManager.scss"; +import { LinkManager } from "./LinkManager"; export interface User { email: string; @@ -154,10 +155,11 @@ export class SharingManager extends React.Component<{}> { } }); return Promise.all(evaluating).then(() => { - runInAction(() => { + runInAction(async () => { for (const sharer of sharingDocs) { if (!this.users.find(user => user.user.email === sharer.user.email)) { this.users.push(sharer); + LinkManager.addLinkDB(sharer.linkDatabase); } } }); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 2be3c3135..3d7446cbe 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -108,7 +108,7 @@ export class MainView extends React.Component { this._sidebarContent.proto = undefined; if (!MainView.Live) { DocServer.setPlaygroundFields(["dataTransition", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition", - "panX", "panY", "width", "height", "nativeWidth", "nativeHeight", + "panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height", "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's } DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg))); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index f7bed2fa1..9bba15b28 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -431,6 +431,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.ProseRef = ele; this._dropDisposer?.(); ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc)); + // if (this.autoHeight) this.tryUpdateScrollHeight(); } @undoBatch @@ -1438,16 +1439,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } } - tryUpdateScrollHeight() { + tryUpdateScrollHeight = () => { if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) { const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0); - const proseHeight = !this.ProseRef ? 0 : Array.from(this.ProseRef.children[0].children).reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); - const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); - if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation - const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; - if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { - setScrollHeight(); - } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... + const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined; + if (children) { + const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); + const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); + if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation + const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; + if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { + setScrollHeight(); + } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... + } } } } diff --git a/src/fields/util.ts b/src/fields/util.ts index 2bb6b45c2..439c4d333 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -141,7 +141,9 @@ export function denormalizeEmail(email: string) { export function inheritParentAcls(parent: Doc, child: Doc) { const dataDoc = parent[DataSym]; for (const key of Object.keys(dataDoc)) { - key.startsWith("acl") && distributeAcls(key, dataDoc[key], child); + // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private. + const permission = (key === "acl-Public" && Doc.UserDoc().defaultAclPrivate) ? AclPrivate : dataDoc[key]; + key.startsWith("acl") && distributeAcls(key, permission, child); } } -- cgit v1.2.3-70-g09d2 From 136b900ed0b939c2a10b601470f764dcb50809ad Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 12 Aug 2021 17:07:41 -0400 Subject: fixed textboxes which were generally a line too short when opened in contentfitting view (e.g. stackingView or pivotView) --- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 9bba15b28..1058070f8 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1444,12 +1444,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0); const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined; if (children) { - const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); - const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); + var proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); + var scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { setScrollHeight(); + setTimeout(() => { + proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); + scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); + setScrollHeight(); + }, 10); } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... } } -- cgit v1.2.3-70-g09d2 From bd41980b6d8849c5ae875fbe5325f88373b3640b Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 17 Aug 2021 11:58:21 -0400 Subject: fixed canEdit func in prosemirrorExampleTransfer to prevent error when hitting enter at end of uneditable doc. fixed scrollheight setting error in formattedtextbox caused by assigning null --- .../views/nodes/formattedText/FormattedTextBox.tsx | 6 +++--- .../nodes/formattedText/ProsemirrorExampleTransfer.ts | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 1058070f8..b0b8ce75d 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -535,7 +535,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" }); const min = Math.round(Date.now() / 1000 / 60); numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-min-" + (min - i), { opacity: ((10 - i - 1) / 10).toString() })); - setTimeout(() => this.updateHighlights()); + setTimeout(this.updateHighlights); } if (FormattedTextBox._highlights.indexOf("By Recent Hour") !== -1) { addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" }); @@ -882,7 +882,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } }, ); - if (this._recording) setTimeout(() => this.recordDictation()); + if (this._recording) setTimeout(this.recordDictation); } var quickScroll: string | undefined = ""; this._disposers.scroll = reaction(() => NumCast(this.layoutDoc._scrollTop), @@ -1453,7 +1453,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp setTimeout(() => { proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); - setScrollHeight(); + scrollHeight && setScrollHeight(); }, 10); } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... } diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index 1f78b2204..eff400a98 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -7,7 +7,7 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list"; import { EditorState, Transaction, TextSelection } from "prosemirror-state"; import { SelectionManager } from "../../../util/SelectionManager"; import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types"; -import { Doc, DataSym, DocListCast, AclAugment } from "../../../../fields/Doc"; +import { Doc, DataSym, DocListCast, AclAugment, AclSelfEdit } from "../../../../fields/Doc"; import { FormattedTextBox } from "./FormattedTextBox"; import { Id } from "../../../../fields/FieldSymbols"; import { Docs } from "../../../documents/Documents"; @@ -72,13 +72,16 @@ export function buildKeymap>(schema: S, props: any, mapKey }; const canEdit = (state: any) => { - for (var i = state.selection.from; i < state.selection.to; i++) { - const node = state.doc.resolve(i); - if (node?.marks?.().some((mark: any) => mark.type === schema.marks.user_mark && - mark.attrs.userid !== Doc.CurrentUserEmail) && - GetEffectiveAcl(props.Document) === AclAugment) { - return false; - } + switch (GetEffectiveAcl(props.Document)) { + case AclAugment: return false; + case AclSelfEdit: + for (var i = state.selection.from; i < state.selection.to; i++) { + const marks = state.doc.resolve(i)?.marks?.(); + if (marks?.some((mark: any) => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail)) { + return false; + } + } + break; } return true; } -- cgit v1.2.3-70-g09d2 From cc89a0c16246afde8bb3d891a4ede492b6644b1f Mon Sep 17 00:00:00 2001 From: geireann Date: Wed, 18 Aug 2021 11:01:57 -0400 Subject: added search linking --- src/Utils.ts | 1 + src/client/views/MarqueeAnnotator.tsx | 2 ++ src/client/views/linking/LinkPopup.tsx | 5 ++++- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 1 + src/client/views/pdf/AnchorMenu.tsx | 12 +++++------- src/client/views/search/SearchBox.tsx | 14 +++++++++++--- src/client/views/topbar/TopBar.scss | 1 + 7 files changed, 25 insertions(+), 11 deletions(-) (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index 194c38a6f..f9ca8646f 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -439,6 +439,7 @@ export function emptyFunction() { } export function unimplementedFunction() { throw new Error("This function is not implemented, but should be."); } + export type Without = Pick>; export type Predicate = (entry: [K, V]) => boolean; diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index a3a3bce56..fa1a4cfea 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -68,6 +68,8 @@ export class MarqueeAnnotator extends React.Component { AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight("rgba(173, 216, 230, 0.75)", true)); AnchorMenu.Instance.Highlight = this.highlight; AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap) => this.highlight("rgba(173, 216, 230, 0.75)", true, savedAnnotations); + AnchorMenu.Instance.onMakeAnchor = AnchorMenu.Instance.GetAnchor; + /** * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation. * It also initiates a Drag/Drop interaction to place the text annotation. diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx index 85fd91d9f..c8be9069c 100644 --- a/src/client/views/linking/LinkPopup.tsx +++ b/src/client/views/linking/LinkPopup.tsx @@ -11,9 +11,11 @@ import { SearchBox } from '../search/SearchBox'; import { DefaultStyleProvider } from '../StyleProvider'; import './LinkPopup.scss'; import React = require("react"); +import { Doc, Opt } from '../../../fields/Doc'; interface LinkPopupProps { showPopup: boolean; + linkFrom?: () => Doc | undefined; // groupType: string; // linkDoc: Doc; // docView: DocumentView; @@ -48,6 +50,7 @@ export class LinkPopup extends React.Component { render() { const popupVisibility = this.props.showPopup ? "block" : "none"; + const linkDoc = this.props.linkFrom ? this.props.linkFrom : undefined; return (
{/*
@@ -67,7 +70,7 @@ export class LinkPopup extends React.Component { { @observable public Highlighting: boolean = false; @observable public Status: "marquee" | "annotation" | "" = ""; + public onMakeAnchor: () => Opt = () => undefined; // Method to get anchor from text search + public OnClick: (e: PointerEvent) => void = unimplementedFunction; - public OnLink: (e: PointerEvent) => void = unimplementedFunction; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; public Highlight: (color: string, isPushpin: boolean) => Opt = (color: string, isPushpin: boolean) => undefined; public GetAnchor: (savedAnnotations?: ObservableMap) => Opt = () => undefined; @@ -67,15 +68,12 @@ export class AnchorMenu extends AntimodeMenu { componentDidMount() { this._disposer = reaction(() => SelectionManager.Views(), selected => { - AnchorMenu.Instance.fadeOut(true) this._showLinkPopup = false; + console.log("unmount"); + AnchorMenu.Instance.fadeOut(true) }); } - componentWillUnmount() { - console.log("unmount"); - } - pointerDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, (e: PointerEvent) => { this.StartDrag(e, this._commentCont.current!); @@ -159,7 +157,7 @@ export class AnchorMenu extends AntimodeMenu { , - + ] : [ {"Remove Link Anchor"}
}>
: -
+
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 092823603..9cc4b1f9a 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -17,8 +17,8 @@ import { InkingStroke } from "../InkingStroke"; import { StyleProp } from "../StyleProvider"; import "./CollectionFreeFormDocumentView.scss"; import { DocumentView, DocumentViewProps } from "./DocumentView"; -import { FieldViewProps } from "./FieldView"; import React = require("react"); +import Color = require("color"); export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined; @@ -164,6 +164,8 @@ export class CollectionFreeFormDocumentView extends DocComponent this._contentView = r)} /> diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index 153176afc..6708a08ee 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -1,17 +1,18 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, observable } from 'mobx'; import { observer } from "mobx-react"; -import { Doc } from '../../../fields/Doc'; +import { Doc, Opt } from '../../../fields/Doc'; import { documentSchema } from '../../../fields/documentSchemas'; import { createSchema, makeInterface } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; -import { emptyFunction, OmitKeys, setupMoveUpEvents } from '../../../Utils'; +import { emptyFunction, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; +import { StyleProp } from '../StyleProvider'; import "./ComparisonBox.scss"; -import { DocumentView } from './DocumentView'; +import { DocumentView, DocumentViewProps } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import React = require("react"); @@ -71,6 +72,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent, props: Opt, property: string): any => { + if (property === StyleProp.PointerEvents) return "none"; + return this.props.styleProvider?.(doc, props, property); + } + render() { const clipWidth = NumCast(this.layoutDoc._clipWidth) + "%"; const clearButton = (which: string) => { @@ -84,6 +90,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent @@ -102,7 +111,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent +
{displayBox("after", 1, this.props.PanelWidth() - 3)}
{displayBox("before", 0, 0)} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index bb259da3e..11fb5cdb3 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -84,6 +84,7 @@ export interface DocComponentView { reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. shrinkWrap?: () => void; // requests a document to display all of its contents with no white space. currently only implemented (needed?) for freeform views menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected. + isAnyChildContentActive?: () => boolean; // is any child content of the document active getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) playFrom?: (time: number, endTime?: number) => void; @@ -182,7 +183,7 @@ export class DocumentViewInternal extends DocComponent; // needs to be accessed from DocumentView wrapper class + @observable _componentView: Opt; // needs to be accessed from DocumentView wrapper class private get topMost() { return this.props.renderDepth === 0 && !LightboxView.LightboxDoc; } public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive @@ -778,8 +779,14 @@ export class DocumentViewInternal extends DocComponent this.ContentScale; onClickFunc = () => this.onClickHandler; setHeight = (height: number) => this.layoutDoc._height = height; - setContentView = (view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view; - isContentActive = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || this.props.isContentActive() ? true : false; + setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view); + isContentActive = (outsideReaction?: boolean) => { + return CurrentUserUtils.SelectedTool !== InkTool.None || + this.props.Document.forceActive || + this.props.isSelected(outsideReaction) || + this._componentView?.isAnyChildContentActive?.() || + this.props.isContentActive() ? true : false; + } @computed get contents() { TraceMobx(); const audioView = !this.layoutDoc._showAudio ? (null) : @@ -794,7 +801,7 @@ export class DocumentViewInternal extends DocComponent; return
; } marqueeDown = (e: React.PointerEvent) => { - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations) this._marqueeing = [e.clientX, e.clientY]; @@ -368,7 +368,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent; const searchTitle = `${!this._searching ? "Open" : "Close"} Search Bar`; const curPage = this.Document._curPage || 1; - return !this.isContentActive() ? (null) : + return !this.props.isContentActive() ? (null) :
[KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true} - onPointerDown={e => e.stopPropagation()} style={{ display: this.isContentActive() ? "flex" : "none" }}> + onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? "flex" : "none" }}>
e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}> @@ -229,7 +229,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent
{this.props.Document.title} @@ -268,7 +268,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent {this.settingsPanel()}
; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index d4df30b48..484dec7e2 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -307,7 +307,7 @@ export class VideoBox extends ViewBoxAnnotatableComponentLoading
: -
+
} placement="bottom"> -
+ {"playback"}
} key="play" placement="bottom"> +
, - {"timecode"}
} placement="bottom"> + {"timecode"}
} key="time" placement="bottom">
{formatTime(curTime)} {" " + Math.floor((curTime - Math.trunc(curTime)) * 100).toString().padStart(2, "0")}
, - {"view full screen"}
} placement="bottom"> + {"view full screen"}
} key="full" placement="bottom">
]; return
{[...(VideoBox._nativeControls ? [] : nonNativeControls), - {"snapshot current frame"}
} placement="bottom"> + {"snapshot current frame"}
} key="snap" placement="bottom">
, - {"show annotation timeline"}
} placement="bottom"> + {"show annotation timeline"}
} key="timeline" placement="bottom">
@@ -429,7 +429,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { this._clicking = false; - if (this.isContentActive()) { + if (this.props.isContentActive()) { const local = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformPoint(e.clientX, e.clientY); this.layoutDoc._timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100)); } @@ -438,7 +438,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { this.layoutDoc._timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent; setTimeout(action(() => this._clicking = false), 500); - }, this.isContentActive(), this.isContentActive()); + }, this.props.isContentActive(), this.props.isContentActive()); }); onResetDown = (e: React.PointerEvent) => { @@ -529,12 +529,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent @@ -546,7 +546,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; @@ -570,7 +570,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent (this.props.scaling?.() || 1) * this.heightPercent / 100; marqueeOffset = () => [this.panelWidth() / 2 * (1 - this.heightPercent / 100) / (this.heightPercent / 100), 0]; - timelineDocFilter = () => ["_timelineLabel:true:x"]; + timelineDocFilter = () => [`_timelineLabel:true,${Utils.noRecursionHack}:x`]; render() { const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding); const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad; @@ -592,7 +592,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; @@ -484,7 +484,7 @@ export class WebBox extends ViewBoxAnnotatableComponent {this.urlContent}
; @@ -529,7 +529,6 @@ export class WebBox extends ViewBoxAnnotatableComponent; return ( -
+
{renderAnnotations(this.opaqueFilter)} - {renderAnnotations()} + {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()} {this.annotationLayer}
@@ -588,10 +587,9 @@ export class WebBox extends ViewBoxAnnotatableComponent diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index d1027dfd7..4b1d76d00 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1211,7 +1211,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if ((e.nativeEvent as any).formattedHandled) { console.log("handled"); } - if (!(e.nativeEvent as any).formattedHandled && this.isContentActive(true)) { + if (!(e.nativeEvent as any).formattedHandled && this.props.isContentActive(true)) { const editor = this._editorView!; const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY }); !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0)))); @@ -1481,7 +1481,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @computed get sidebarHandle() { TraceMobx(); const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length; - return (!annotated && !this.isContentActive()) ? (null) :
: = 10 ? "-selected" : ""; return (
this.isContentActive() && e.stopPropagation()} + onWheel={e => this.props.isContentActive() && e.stopPropagation()} style={{ transform: this.props.dontScale ? undefined : `scale(${scale})`, transformOrigin: this.props.dontScale ? undefined : "top left", diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 75e3f81fb..42bec38da 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -69,7 +69,6 @@ export class AnchorMenu extends AntimodeMenu { this._disposer = reaction(() => SelectionManager.Views(), selected => { this._showLinkPopup = false; - console.log("unmount"); AnchorMenu.Instance.fadeOut(true) }); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 734d9127c..bc35d2126 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -512,7 +512,6 @@ export class PDFViewer extends React.Component { const renderAnnotations = (docFilters?: () => string[]) => { transform: `scale(${this._zoomed})` }}> {renderAnnotations(this.opaqueFilter)} - {renderAnnotations()} + {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()}
; } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 17f41fac8..b09ff93d0 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1092,6 +1092,9 @@ export namespace Doc { const isTransparent = (color: string) => color !== "" && (Color(color).alpha() !== 1); return isTransparent(StrCast(doc[key])); } + if (typeof value === "string") { + value = value.replace(`,${Utils.noRecursionHack}`, ""); + } const fieldVal = doc[key]; if (Cast(fieldVal, listSpec("string"), []).length) { const vals = Cast(fieldVal, listSpec("string"), []); -- cgit v1.2.3-70-g09d2