From 4574b7f03ccc85c4bebdbfd9475788456086704f Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 8 Aug 2024 12:27:40 -0400 Subject: many changes to add typing in place of 'any's etc --- src/client/views/nodes/DocumentContentsView.tsx | 46 ++++++++++++++++--------- 1 file changed, 29 insertions(+), 17 deletions(-) (limited to 'src/client/views/nodes/DocumentContentsView.tsx') diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 192c7875e..8a2c4e530 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -43,26 +43,37 @@ interface HTMLtagProps { @observer export class HTMLtag extends React.Component { click = () => { - const clickScript = (this.props as any).onClick as Opt; + const clickScript = this.props.onClick as Opt; clickScript?.script.run({ this: this.props.Document, scale: this.props.scaling }); }; - onInput = (e: React.FormEvent) => { - const onInputScript = (this.props as any).onInput as Opt; - onInputScript?.script.run({ this: this.props.Document, value: (e.target as any).textContent }); + onInput = (e: React.FormEvent) => { + const onInputScript = this.props.onInput as Opt; + onInputScript?.script.run({ this: this.props.Document, value: (e.target as HTMLElement).textContent }); }; render() { - const style: { [key: string]: any } = {}; - const divKeys = OmitKeys(this.props, ['children', 'dragStarting', 'dragEnding', 'htmltag', 'scaling', 'Document', 'key', 'onInput', 'onClick', '__proto__']).omit; - const replacer = (match: any, expr: string) => + const style: { [key: string]: unknown } = {}; + const divKeys = OmitKeys(this.props, [ + 'children', // + 'dragStarting', + 'dragEnding', + 'htmltag', + 'scaling', + 'Document', + 'key', + 'onInput', + 'onClick', + '__proto__', + ]).omit; + const replacer = (match: string, expr: string) => // bcz: this executes a script to convert a property expression string: { script } into a value (ScriptField.MakeFunction(expr, { this: Doc.name, scale: 'number' })?.script.run({ this: this.props.Document, scale: this.props.scaling }).result as string) || ''; Object.keys(divKeys).forEach((prop: string) => { - const p = (this.props as any)[prop] as string; + const p = (this.props as unknown as { [key: string]: string })[prop] as string; style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer); }); const Tag = this.props.htmltag as keyof JSX.IntrinsicElements; return ( - + {this.props.children} ); @@ -78,12 +89,12 @@ export class DocumentContentsView extends ObservableReactComponent{content}< as in {this.title} - const replacer = (match: any, prefix: string, expr: string, postfix: string) => prefix + ((ScriptField.MakeFunction(expr, { this: Doc.name })?.script.run({ this: this._props.Document }).result as string) || '') + postfix; + const replacer = (match: string, prefix: string, expr: string, postfix: string) => prefix + ((ScriptField.MakeFunction(expr, { this: Doc.name })?.script.run({ this: this._props.Document }).result as string) || '') + postfix; layoutFrame = layoutFrame.replace(/(>[^{]*)[^=]\{([^.'][^<}]+)\}([^}]*<)/g, replacer); // replace HTML with corresponding HTML tag as in: becomes - const replacer2 = (match: any, p1: string) => ` ` with as in: becomes @@ -194,6 +205,7 @@ export class DocumentContentsView extends ObservableReactComponent { console.log('DocumentContentsView:' + test, bindings, layoutFrame); }} -- cgit v1.2.3-70-g09d2 From 762ac2bf354e4cc2c4b15f42502da939f5061646 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 9 Aug 2024 16:48:18 -0400 Subject: a bunch more typing fixes. --- src/client/util/BranchingTrailManager.tsx | 14 +- src/client/util/reportManager/ReportManager.scss | 12 +- src/client/views/ContextMenu.scss | 37 ++--- src/client/views/ContextMenu.tsx | 32 ++-- src/client/views/ContextMenuItem.tsx | 180 ++++++--------------- src/client/views/DictationOverlay.tsx | 2 +- src/client/views/DocViewUtils.ts | 1 + src/client/views/DocumentButtonBar.tsx | 11 +- src/client/views/DocumentDecorations.tsx | 23 +-- src/client/views/EditableView.tsx | 26 +-- src/client/views/ExtractColors.ts | 2 +- src/client/views/FieldsDropdown.tsx | 8 +- src/client/views/GlobalKeyHandler.ts | 4 +- src/client/views/KeyphraseQueryView.scss | 8 - src/client/views/KeyphraseQueryView.tsx | 32 ---- src/client/views/MainViewModal.tsx | 2 - src/client/views/ObservableReactComponent.tsx | 14 +- src/client/views/PreviewCursor.tsx | 51 +++--- src/client/views/PropertiesButtons.tsx | 19 +-- .../views/PropertiesDocBacklinksSelector.tsx | 2 +- src/client/views/PropertiesDocContextSelector.tsx | 7 +- src/client/views/PropertiesSection.tsx | 4 +- src/client/views/PropertiesView.tsx | 102 ++++++------ src/client/views/SidebarAnnos.tsx | 10 +- src/client/views/StyleProvider.tsx | 11 +- src/client/views/TemplateMenu.tsx | 3 +- src/client/views/UndoStack.tsx | 2 - .../views/collections/CollectionCarouselView.tsx | 3 +- src/client/views/collections/CollectionMenu.tsx | 44 ++--- .../views/collections/CollectionTreeView.tsx | 4 +- src/client/views/collections/CollectionView.tsx | 12 +- src/client/views/global/globalScripts.ts | 32 ++-- src/client/views/linking/LinkMenu.tsx | 4 +- src/client/views/linking/LinkMenuGroup.tsx | 2 - src/client/views/linking/LinkMenuItem.tsx | 20 ++- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 4 +- src/client/views/nodes/FunctionPlotBox.tsx | 20 +-- src/client/views/nodes/KeyValueBox.tsx | 3 +- src/client/views/nodes/KeyValuePair.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 6 +- .../views/nodes/formattedText/EquationView.tsx | 10 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 6 +- 43 files changed, 333 insertions(+), 460 deletions(-) delete mode 100644 src/client/views/KeyphraseQueryView.scss delete mode 100644 src/client/views/KeyphraseQueryView.tsx (limited to 'src/client/views/nodes/DocumentContentsView.tsx') diff --git a/src/client/util/BranchingTrailManager.tsx b/src/client/util/BranchingTrailManager.tsx index 119d103c5..65336812d 100644 --- a/src/client/util/BranchingTrailManager.tsx +++ b/src/client/util/BranchingTrailManager.tsx @@ -15,18 +15,18 @@ export class BranchingTrailManager extends React.Component { public static Instance: BranchingTrailManager; // stack of the history - @observable private slideHistoryStack: String[] = []; - @observable private containsSet: Set = new Set(); + @observable private slideHistoryStack: string[] = []; + @observable private containsSet: Set = new Set(); // docId to Doc map - @observable private docIdToDocMap: Map = new Map(); + @observable private docIdToDocMap: Map = new Map(); // prev pres to copmare with - @observable private prevPresId: String | null = null; - @action setPrevPres = action((newId: String | null) => { + @observable private prevPresId: string | null = null; + @action setPrevPres = action((newId: string | null) => { this.prevPresId = newId; }); - constructor(props: any) { + constructor(props: object) { super(props); makeObservable(this); if (!BranchingTrailManager.Instance) { @@ -48,7 +48,7 @@ export class BranchingTrailManager extends React.Component { // Doc.AddToMyOverlay(hi); }; - @action setSlideHistoryStack = action((newArr: String[]) => { + @action setSlideHistoryStack = action((newArr: string[]) => { this.slideHistoryStack = newArr; }); diff --git a/src/client/util/reportManager/ReportManager.scss b/src/client/util/reportManager/ReportManager.scss index d82d7fdeb..fd343ac8e 100644 --- a/src/client/util/reportManager/ReportManager.scss +++ b/src/client/util/reportManager/ReportManager.scss @@ -96,12 +96,12 @@ transition: all 0.2s ease; background: transparent; - &:hover { - // border-bottom-color: $text-gray; - } - &:focus { - // border-bottom-color: #4476f7; - } + // &:hover { + // // border-bottom-color: $text-gray; + // } + // &:focus { + // // border-bottom-color: #4476f7; + // } } // View issues diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index 232362c5c..4aaf2d03b 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -38,7 +38,12 @@ background: whitesmoke; } -.contextMenu-item { +.contextMenuItem-Selected { + background: lightgoldenrodyellow; + border-style: none; +} + +.contextMenuItem { // width: 11vw; //10vw height: 25px; //2vh display: flex; //comment out to allow search icon to be inline with search text @@ -59,7 +64,7 @@ text-transform: uppercase; padding-right: 30px; - .contextMenu-item-background { + .contextMenuItem-background { width: 100%; height: 100%; position: absolute; @@ -69,13 +74,7 @@ filter: opacity(0); } - &:hover { - .contextMenu-item-background { - filter: opacity(0.2) !important; - } - } - - .contextMenu-item-icon-background { + .contextMenuItem-icon { pointer-events: all; background-color: transparent; width: 35px; @@ -103,6 +102,8 @@ letter-spacing: 1px; text-transform: uppercase; padding-right: 30px; + align-items: center; + align-self: center; } .contextMenu-item:hover { @@ -115,11 +116,6 @@ cursor: pointer; } -.contextMenu-itemSelected { - background: lightgoldenrodyellow; - border-style: none; -} - .contextMenu-group { // width: 11vw; //10vw height: 30px; //2vh @@ -145,23 +141,24 @@ padding-left: 5px; } -.contextMenu-inlineMenu { - // border-top: solid 1px; //TODO:glr clean -} - .contextMenu-description { margin-left: 5px; text-align: left; display: inline; //need this? } -.search-icon { +.contextMenu-search { margin: 10px; + display: flex; + .contextMenu-searchIcon { + margin-right: 5px; + } } -.search { +.contextMenu-searchInput { margin-left: 10px; padding-left: 10px; border: solid black 1px; border-radius: 5px; + width: 100%; } diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 05634f376..5edb5fc0d 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -8,7 +8,7 @@ import * as React from 'react'; import { DivHeight, DivWidth } from '../../ClientUtils'; import { SnappingManager } from '../util/SnappingManager'; import './ContextMenu.scss'; -import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from './ContextMenuItem'; +import { ContextMenuItem, ContextMenuProps } from './ContextMenuItem'; import { ObservableReactComponent } from './ObservableReactComponent'; @observer @@ -148,13 +148,13 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean } return wasOpen; }; - @computed get filteredItems(): (OriginalMenuProps | string[])[] { + @computed get filteredItems(): (ContextMenuProps | string[])[] { const searchString = this._searchString.toLowerCase().split(' '); const matches = (descriptions: string[]) => searchString.every(s => descriptions.some(desc => desc.toLowerCase().includes(s))); const flattenItems = (items: ContextMenuProps[], groupFunc: (groupName: string) => string[]) => { - let eles: (OriginalMenuProps | string[])[] = []; + let eles: (ContextMenuProps | string[])[] = []; - const leaves: OriginalMenuProps[] = []; + const leaves: ContextMenuProps[] = []; items.forEach(item => { const { description } = item; const path = groupFunc(description); @@ -165,7 +165,7 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean } eles = eles.concat(children); } } else if (matches(path)) { - leaves.push(item as OriginalMenuProps); + leaves.push(item as ContextMenuProps); } }); @@ -176,8 +176,8 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean } return flattenItems(this._items.slice(), name => [name]); } - @computed get flatItems(): OriginalMenuProps[] { - return this.filteredItems.filter(item => !Array.isArray(item)) as OriginalMenuProps[]; + @computed get flatItems(): ContextMenuProps[] { + return this.filteredItems.filter(item => !Array.isArray(item)) as ContextMenuProps[]; } @computed get menuItems() { @@ -228,21 +228,11 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean } color: SnappingManager.userColor, }}> {!this.itemsNeedSearch ? null : ( - - + + - + )} {this.menuItems} @@ -264,7 +254,7 @@ export class ContextMenu extends ObservableReactComponent<{ noexpand?: boolean } e.preventDefault(); } else if (e.key === 'Enter' || e.key === 'Tab') { const item = this.flatItems[this._selectedIndex]; - if (item) { + if (item.event) { item.event({ x: this.pageX, y: this.pageY }); } else { // if (this._searchString.startsWith(this._defaultPrefix)) { diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index 3b87ea58b..5b4eb704b 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -8,32 +8,27 @@ import { SnappingManager } from '../util/SnappingManager'; import { UndoManager } from '../util/UndoManager'; import { ObservableReactComponent } from './ObservableReactComponent'; -export interface OriginalMenuProps { +export interface ContextMenuProps { + icon: IconProp | JSX.Element; description: string; - event: (stuff?: unknown) => void; - undoable?: boolean; - noexpand?: boolean; - subitems?: ContextMenuProps[]; - icon: IconProp | JSX.Element; // maybe should be optional (icon?) - closeMenu?: () => void; -} - -export interface SubmenuProps { - description: string; - // eslint-disable-next-line no-use-before-define - subitems: ContextMenuProps[]; - noexpand?: boolean; addDivider?: boolean; - icon: IconProp; // maybe should be optional (icon?) closeMenu?: () => void; -} -export type ContextMenuProps = OriginalMenuProps | SubmenuProps; + subitems?: ContextMenuProps[]; + noexpand?: boolean; // whether to render the submenu items as a flyout from this item, or inline in place of this item + + undoable?: boolean; // whether to wrap the event callback in an UndoBatch or not + event?: (stuff?: unknown) => void; +} @observer export class ContextMenuItem extends ObservableReactComponent { - @observable private _items: Array = []; - @observable private overItem = false; + static readonly HOVER_TIMEOUT = 100; + _hoverTimeout?: NodeJS.Timeout; + _overPosY = 0; + _overPosX = 0; + @observable _items: ContextMenuProps[] = []; + @observable _overItem = false; constructor(props: ContextMenuProps & { selected?: boolean }) { super(props); @@ -41,132 +36,63 @@ export class ContextMenuItem extends ObservableReactComponent { - this._items.length = 0; - }); - if ((this._props as SubmenuProps)?.subitems) { - (this._props as SubmenuProps).subitems?.forEach(i => runInAction(() => this._items.push(i))); - } + runInAction(() => this._items.push(...(this._props.subitems ?? []))); } handleEvent = async (e: React.MouseEvent) => { - if ('event' in this._props) { + if (this._props.event) { this._props.closeMenu?.(); - const batch = this._props.undoable !== false ? UndoManager.StartBatch(`Click Menu item: ${this._props.description}`) : undefined; + const batch = this._props.undoable ? UndoManager.StartBatch(`Click Menu item: ${this._props.description}`) : undefined; await this._props.event({ x: e.clientX, y: e.clientY }); batch?.end(); } }; - currentTimeout?: NodeJS.Timeout | undefined; - static readonly timeout = 300; - _overPosY = 0; - _overPosX = 0; + setOverItem = (over: boolean) => { + this._hoverTimeout = setTimeout( action(() => { this._overItem = over; }), ContextMenuItem.HOVER_TIMEOUT ); // prettier-ignore + }; + onPointerEnter = (e: React.MouseEvent) => { - if (this.currentTimeout) { - clearTimeout(this.currentTimeout); - this.currentTimeout = undefined; - } - if (this.overItem) { - return; - } + this._hoverTimeout && clearTimeout(this._hoverTimeout); this._overPosY = e.clientY; this._overPosX = e.clientX; - this.currentTimeout = setTimeout( - action(() => { - this.overItem = true; - }), - ContextMenuItem.timeout - ); + !this._overItem && this.setOverItem(true); }; onPointerLeave = () => { - if (this.currentTimeout) { - clearTimeout(this.currentTimeout); - this.currentTimeout = undefined; - } - if (!this.overItem) { - return; - } - this.currentTimeout = setTimeout( - action(() => { - this.overItem = false; - }), - ContextMenuItem.timeout - ); + this._hoverTimeout && clearTimeout(this._hoverTimeout); + this._overItem && this.setOverItem(false); }; - isJSXElement(val: unknown): val is JSX.Element { - return React.isValidElement(val); - } + renderItem = (submenu: JSX.Element[]) => { + const alignItems = this._overPosY < window.innerHeight / 3 ? 'flex-start' : this._overPosY > (window.innerHeight * 2) / 3 ? 'flex-end' : 'center'; + const marginTop = this._overPosY < window.innerHeight / 3 ? '20px' : this._overPosY > (window.innerHeight * 2) / 3 ? '-20px' : ''; + const marginLeft = window.innerWidth - this._overPosX - 50 > 0 ? '90%' : '20%'; - render() { - if ('event' in this._props) { - return ( -
- {this._props.icon ? {this.isJSXElement(this._props.icon) ? this._props.icon : } : null} -
{this._props.description.replace(':', '')}
-
-
- ); - } - if ('subitems' in this._props) { - const where = !this.overItem ? '' : this._overPosY < window.innerHeight / 3 ? 'flex-start' : this._overPosY > (window.innerHeight * 2) / 3 ? 'flex-end' : 'center'; - const marginTop = !this.overItem ? '' : this._overPosY < window.innerHeight / 3 ? '20px' : this._overPosY > (window.innerHeight * 2) / 3 ? '-20px' : ''; + return ( +
+
+ + {React.isValidElement(this._props.icon) ? this._props.icon : this._props.icon ? : null} + +
{this._props.description}
+ {!submenu.length ? null : ( + !this._overItem ? + : ( +
+ {submenu} +
+ ) + )} +
+ ); // prettier-ignore + }; - // here - const submenu = !this.overItem ? null : ( -
0 ? '90%' : '20%', - marginTop, - background: SnappingManager.userBackgroundColor, - }}> - {this._items.map(prop => ( - - ))} -
- ); - if (!(this._props as SubmenuProps).noexpand) { - return ( -
- {this._items.map(prop => ( - - ))} -
- ); - } - return ( -
- {this._props.icon ? ( - - - - ) : null} -
- {this._props.description} - -
-
- {submenu} -
- ); - } - return null; + render() { + const submenu = this._items.map(prop => ); + return this.props.event || this._props.noexpand ? this.renderItem(submenu) :
{submenu}
; } } diff --git a/src/client/views/DictationOverlay.tsx b/src/client/views/DictationOverlay.tsx index b242acdba..e33049d3b 100644 --- a/src/client/views/DictationOverlay.tsx +++ b/src/client/views/DictationOverlay.tsx @@ -17,7 +17,7 @@ export class DictationOverlay extends React.Component { // eslint-disable-next-line react/no-unused-class-component-methods public hasActiveModal = false; - constructor(props: any) { + constructor(props: object) { super(props); makeObservable(this); DictationOverlay.Instance = this; diff --git a/src/client/views/DocViewUtils.ts b/src/client/views/DocViewUtils.ts index 1f5f29c7e..49a30aa08 100644 --- a/src/client/views/DocViewUtils.ts +++ b/src/client/views/DocViewUtils.ts @@ -6,6 +6,7 @@ import { Doc, SetActiveAudioLinker } from '../../fields/Doc'; import { DocUtils } from '../documents/DocUtils'; import { FieldViewProps } from './nodes/FieldView'; +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace DocViewUtils { export const ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 487868169..b778a4fb9 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -1,6 +1,3 @@ -/* eslint-disable jsx-a11y/control-has-associated-label */ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import { IconLookup, IconProp } from '@fortawesome/fontawesome-svg-core'; import { faCalendarDays } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -20,7 +17,7 @@ import { DictationManager } from '../util/DictationManager'; import { DragManager } from '../util/DragManager'; import { dropActionType } from '../util/DropActionTypes'; import { SharingManager } from '../util/SharingManager'; -import { UndoManager, undoBatch } from '../util/UndoManager'; +import { UndoManager, undoable } from '../util/UndoManager'; import './DocumentButtonBar.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; import { PinProps } from './PinFuncs'; @@ -33,12 +30,12 @@ import { OpenWhere } from './nodes/OpenWhere'; import { DashFieldView } from './nodes/formattedText/DashFieldView'; @observer -export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (DocumentView | undefined)[]; stack?: any }> { +export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (DocumentView | undefined)[]; stack?: unknown }> { private _dragRef = React.createRef(); // eslint-disable-next-line no-use-before-define public static Instance: DocumentButtonBar; - constructor(props: any) { + constructor(props: { views: () => (DocumentView | undefined)[]; stack?: unknown }) { super(props); makeObservable(this); DocumentButtonBar.Instance = this; @@ -83,7 +80,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
this._props.views().map(view => view?.toggleFollowLink(undefined, false)))}> + onClick={undoable(() => this._props.views().map(view => view?.toggleFollowLink(undefined, false)), 'follow link')}>
{followBtn( true, diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 93c3e3338..68970223a 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -34,6 +34,7 @@ import { DocumentView } from './nodes/DocumentView'; import { ImageBox } from './nodes/ImageBox'; import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; +import { IconProp } from '@fortawesome/fontawesome-svg-core'; interface DocumentDecorationsProps { PanelWidth: number; @@ -145,7 +146,7 @@ export class DocumentDecorations extends ObservableReactComponent { if (e.key === 'Enter') { e.stopPropagation(); - (e.target as any).blur(); + (e.target as HTMLElement).blur?.(); } }; @@ -239,7 +240,7 @@ export class DocumentDecorations extends ObservableReactComponent DragManager.StartWindowDrag?.(e, [DocumentView.SelectedDocs().lastElement()]) ?? false, emptyFunction, this.onMaximizeClick, false, false); e.stopPropagation(); }; - onMaximizeClick = (e: any): void => { + onMaximizeClick = (e: PointerEvent): void => { const selView = DocumentView.Selected()[0]; if (selView) { if (e.ctrlKey) { @@ -349,8 +350,10 @@ export class DocumentDecorations extends ObservableReactComponent // return false to keep getting events - this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]) as any as boolean, + (moveEv: PointerEvent, down: number[], delta: number[]) => { + this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]); + return false; + }, action(() => { this._isRotating = false; }), // upEvent action(() => { seldocview.Document._rotation_centerX = seldocview.Document._rotation_centerY = 0; }), true @@ -430,7 +433,7 @@ export class DocumentDecorations extends ObservableReactComponent this.resizeView(docView, refPt, scaleAspect, { dragHdl, ctrlKey:e.ctrlKey })); // prettier-ignore - await new Promise(res => { setTimeout(() => { res(this._interactionLock = undefined)})}); + await new Promise(res => { setTimeout(() => { res(this._interactionLock = undefined)})}); }); // prettier-ignore return false; @@ -681,10 +684,10 @@ export class DocumentDecorations extends ObservableReactComponent void), click: undefined | ((e: any) => void), title: string) => ( + const topBtn = (key: string, icon: IconProp, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: PointerEvent) => void), title: string) => ( {title}
} placement="top"> -
e.preventDefault()} onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, clickEv => click!(clickEv)))}> - +
e.preventDefault()} onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, clickEv => click?.(clickEv)))}> +
); diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 684b948af..e02e39b8b 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -1,10 +1,7 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import { action, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import * as Autosuggest from 'react-autosuggest'; -import { ObjectField } from '../../fields/ObjectField'; import './EditableView.scss'; import { DocumentIconContainer } from './nodes/DocumentIcon'; import { FieldView, FieldViewProps } from './nodes/FieldView'; @@ -29,7 +26,7 @@ export interface EditableProps { /** * The contents to render when not editing */ - contents: any; + contents: string; fieldContents?: FieldViewProps; fontStyle?: string; fontSize?: number; @@ -41,8 +38,8 @@ export interface EditableProps { autosuggestProps?: { resetValue: () => void; value: string; - onChange: (e: React.ChangeEvent, { newValue }: { newValue: string }) => void; - autosuggestProps: Autosuggest.AutosuggestProps; + onChange: (e: React.FormEvent, { newValue }: { newValue: string }) => void; + autosuggestProps: Autosuggest.AutosuggestProps; }; oneLine?: boolean; // whether to display the editable view as a single input line or as a textarea allowCRs?: boolean; // can carriage returns be entered @@ -112,8 +109,8 @@ export class EditableView extends ObservableReactComponent { } onChange = (e: React.ChangeEvent) => { - const targVal = (e.target as any).value; - if (!(targVal.startsWith(':=') || targVal.startsWith('='))) { + const targVal = (e.target as HTMLSelectElement).value; + if (!(targVal?.startsWith(':=') || targVal?.startsWith('='))) { this._overlayDisposer?.(); this._overlayDisposer = undefined; } else if (!this._overlayDisposer) { @@ -230,13 +227,11 @@ export class EditableView extends ObservableReactComponent { className: 'editableView-input', onKeyDown: this.onKeyDown, autoFocus: true, - // @ts-ignore - onBlur: e => this.finalizeEdit(e.currentTarget.value, false, true, false), + onBlur: e => this.finalizeEdit((e.currentTarget as HTMLSelectElement).value, false, true, false), onPointerDown: this.stopPropagation, onClick: this.stopPropagation, onPointerUp: this.stopPropagation, value: this._props.autosuggestProps.value, - // @ts-ignore onChange: this._props.autosuggestProps.onChange, }} /> @@ -248,7 +243,6 @@ export class EditableView extends ObservableReactComponent { placeholder={this._props.placeholder} onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)} defaultValue={this._props.GetValue()} - // eslint-disable-next-line jsx-a11y/no-autofocus autoFocus onChange={this.onChange} onKeyDown={this.onKeyDown} @@ -264,7 +258,6 @@ export class EditableView extends ObservableReactComponent { placeholder={this._props.placeholder} onBlur={e => this.finalizeEdit(e.currentTarget.value, false, true, false)} defaultValue={this._props.GetValue()} - // eslint-disable-next-line jsx-a11y/no-autofocus autoFocus onChange={this.onChange} onKeyDown={this.onKeyDown} @@ -288,7 +281,7 @@ export class EditableView extends ObservableReactComponent { ); } setTimeout(() => this._props.autosuggestProps?.resetValue()); - return this._props.contents instanceof ObjectField ? null : ( + return (
{ fontStyle: this._props.fontStyle, fontSize: this._props.fontSize, }}> - { - // eslint-disable-next-line react/jsx-props-no-spreading - this._props.fieldContents ? : this.props.contents ? this._props.contents?.valueOf() : '' - } + {this._props.fieldContents ? : this._props.contents ? this._props.contents?.valueOf() : ''}
); diff --git a/src/client/views/ExtractColors.ts b/src/client/views/ExtractColors.ts index f6928c52a..eee1d3a04 100644 --- a/src/client/views/ExtractColors.ts +++ b/src/client/views/ExtractColors.ts @@ -126,7 +126,7 @@ export class ExtractColors { let hue = 0; let saturation = 0; - let lightness = intensity; + const lightness = intensity; if (area !== 0) { saturation = area / (1 - Math.abs(2 * intensity - 1)); diff --git a/src/client/views/FieldsDropdown.tsx b/src/client/views/FieldsDropdown.tsx index 0ea0ebd83..407031b40 100644 --- a/src/client/views/FieldsDropdown.tsx +++ b/src/client/views/FieldsDropdown.tsx @@ -29,7 +29,7 @@ interface fieldsDropdownProps { @observer export class FieldsDropdown extends ObservableReactComponent { @observable _newField = ''; - constructor(props: any) { + constructor(props: fieldsDropdownProps) { super(props); makeObservable(this); } @@ -101,13 +101,13 @@ export class FieldsDropdown extends ObservableReactComponent this._props.selectFunc((val as any as { value: string; label: string }).value)} + onChange={val => this._props.selectFunc((val as { value: string; label: string }).value)} onKeyDown={e => { if (e.key === 'Enter') { runInAction(() => { - this._props.selectFunc((this._newField = (e.nativeEvent.target as any)?.value)); + this._props.selectFunc((this._newField = (e.nativeEvent.target as HTMLSelectElement)?.value)); }); } e.stopPropagation(); diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 7d01bbabb..a85a03aab 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -56,7 +56,7 @@ export class KeyManager { window.addEventListener('keydown', KeyManager.Instance.handle); window.removeEventListener('keyup', KeyManager.Instance.unhandle); window.addEventListener('keyup', KeyManager.Instance.unhandle); - window.addEventListener('paste', KeyManager.Instance.paste as any); + window.addEventListener('paste', KeyManager.Instance.paste); } public unhandle = action((/* e: KeyboardEvent */) => { @@ -330,7 +330,7 @@ export class KeyManager { } break; case 'c': - if ((document.activeElement as any)?.type !== 'text' && !AnchorMenu.Instance.Active && DocumentDecorations.Instance.Bounds.r - DocumentDecorations.Instance.Bounds.x > 2) { + if (!AnchorMenu.Instance.Active && DocumentDecorations.Instance.Bounds.r - DocumentDecorations.Instance.Bounds.x > 2) { const bds = DocumentDecorations.Instance.Bounds; const pt = DocumentView.Selected()[0] .screenToViewTransform() diff --git a/src/client/views/KeyphraseQueryView.scss b/src/client/views/KeyphraseQueryView.scss deleted file mode 100644 index ac715e5e7..000000000 --- a/src/client/views/KeyphraseQueryView.scss +++ /dev/null @@ -1,8 +0,0 @@ -.fading { - animation: fanOut 1s -} - -@keyframes fanOut { - from {opacity: 0;} - to {opacity: 1;} -} \ No newline at end of file diff --git a/src/client/views/KeyphraseQueryView.tsx b/src/client/views/KeyphraseQueryView.tsx deleted file mode 100644 index 81f004010..000000000 --- a/src/client/views/KeyphraseQueryView.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/* eslint-disable jsx-a11y/label-has-associated-control */ -import { observer } from 'mobx-react'; -import * as React from 'react'; -import './KeyphraseQueryView.scss'; - -// tslint:disable-next-line: class-name -export interface KP_Props { - keyphrases: string; -} - -@observer -export class KeyphraseQueryView extends React.Component { - render() { - const keyterms = this.props.keyphrases.split(','); - return ( -
-
Select queries to send:
-
- {keyterms.map((kp: string) => ( - // return (

{"-" + kp}

); -

- -

- ))} -
-
- ); - } -} diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx index a6dc5c62b..4a35805fb 100644 --- a/src/client/views/MainViewModal.tsx +++ b/src/client/views/MainViewModal.tsx @@ -1,5 +1,3 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ /* eslint-disable react/require-default-props */ import { isDark } from 'browndash-components'; import { observer } from 'mobx-react'; diff --git a/src/client/views/ObservableReactComponent.tsx b/src/client/views/ObservableReactComponent.tsx index 34da82b6c..bb7a07f0e 100644 --- a/src/client/views/ObservableReactComponent.tsx +++ b/src/client/views/ObservableReactComponent.tsx @@ -8,27 +8,27 @@ import JsxParser from 'react-jsx-parser'; * This is an abstract class that serves as the base for a PDF-style or Marquee-style * menu. To use this class, look at PDFMenu.tsx or MarqueeOptionsMenu.tsx for an example. */ -export abstract class ObservableReactComponent extends React.Component { +export abstract class ObservableReactComponent extends React.Component { @observable _props: React.PropsWithChildren; - constructor(props: any) { + constructor(props: React.PropsWithChildren) { super(props); this._props = props; makeObservable(this); } componentDidUpdate(prevProps: Readonly): void { Object.keys(prevProps) - .filter(pkey => (prevProps as any)[pkey] !== (this.props as any)[pkey]) + .filter(pkey => (prevProps as {[key:string]: unknown})[pkey] !== (this.props as {[key:string]: unknown})[pkey]) .forEach(action(pkey => { - (this._props as any)[pkey] = (this.props as any)[pkey]; + (this._props as {[key:string]: unknown})[pkey] = (this.props as {[key:string]: unknown})[pkey]; })); // prettier-ignore } } class ObserverJsxParser1 extends JsxParser { - constructor(props: any) { + constructor(props: object) { super(props); - observer(this as any); + observer(this as typeof JsxParser); } } -export const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any; +export const ObserverJsxParser = ObserverJsxParser1 as typeof JsxParser; diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 034ade50b..7e597879d 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -7,13 +7,14 @@ import { Docs, DocumentOptions } from '../documents/Documents'; import { DocUtils } from '../documents/DocUtils'; import { ImageUtils } from '../util/Import & Export/ImageUtils'; import { Transform } from '../util/Transform'; -import { UndoManager, undoBatch } from '../util/UndoManager'; +import { UndoManager, undoable } from '../util/UndoManager'; import { ObservableReactComponent } from './ObservableReactComponent'; import './PreviewCursor.scss'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; +import { StrCast } from '../../fields/Types'; @observer -export class PreviewCursor extends ObservableReactComponent<{}> { +export class PreviewCursor extends ObservableReactComponent { // eslint-disable-next-line no-use-before-define static _instance: PreviewCursor; public static get Instance() { @@ -29,7 +30,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> { @observable _clickPoint: number[] = []; @observable public Visible = false; public Doc: Opt; - constructor(props: any) { + constructor(props: object) { super(props); makeObservable(this); PreviewCursor._instance = this; @@ -46,7 +47,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> { }); // tests for URL and makes web document - const re: any = /^https?:\/\//g; + const re = /^https?:\/\//g; const plain = e.clipboardData.getData('text/plain'); if (plain && newPoint) { // tests for youtube and makes video document @@ -64,17 +65,19 @@ export class PreviewCursor extends ObservableReactComponent<{}> { } else if (re.test(plain)) { const url = plain; if (!url.startsWith(window.location.href)) { - undoBatch(() => - this._addDocument?.( - Docs.Create.WebDocument(url, { - title: url, - _width: 500, - _height: 300, - data_useCors: true, - x: newPoint[0], - y: newPoint[1], - }) - ) + undoable( + () => + this._addDocument?.( + Docs.Create.WebDocument(url, { + title: url, + _width: 500, + _height: 300, + data_useCors: true, + x: newPoint[0], + y: newPoint[1], + }) + ), + 'paste web doc' )(); } else alert('cannot paste dash into itself'); } else if (plain.startsWith('__DashDocId(') || plain.startsWith('__DashCloneId(')) { @@ -94,11 +97,11 @@ export class PreviewCursor extends ObservableReactComponent<{}> { } // pasting in images else if (e.clipboardData.getData('text/html') !== '' && e.clipboardData.getData('text/html').includes(' { + if (newPoint && arr) { + undoable(() => { const doc = Docs.Create.ImageDocument(arr[1], { _width: 300, title: arr[1], @@ -107,7 +110,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> { }); ImageUtils.ExtractImgInfo(doc); this._addDocument?.(doc); - })(); + }, 'paste image doc')(); } } else if (e.clipboardData.items.length && newPoint) { const batch = UndoManager.StartBatch('collection view drop'); @@ -196,8 +199,12 @@ export class PreviewCursor extends ObservableReactComponent<{}> { } render() { return !this._clickPoint || !this.Visible ? null : ( - // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex -
e?.focus()} style={{ color: lightOrDark(this.Doc?.backgroundColor ?? 'white'), transform: `translate(${this._clickPoint[0]}px, ${this._clickPoint[1]}px)` }}> +
e?.focus()} + style={{ color: lightOrDark(StrCast(this.Doc?.backgroundColor, 'white')), transform: `translate(${this._clickPoint[0]}px, ${this._clickPoint[1]}px)` }}> I
); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index edf6df2b9..f346d4ba8 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -1,5 +1,3 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ /* eslint-disable react/no-unused-class-component-methods */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Dropdown, DropdownType, IListItemProps, Toggle, ToggleType, Type } from 'browndash-components'; @@ -18,7 +16,7 @@ import { TfiBarChart } from 'react-icons/tfi'; import { Doc, Opt } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { ScriptField } from '../../fields/ScriptField'; -import { BoolCast, ScriptCast } from '../../fields/Types'; +import { BoolCast, ScriptCast, StrCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; import { DocUtils, IsFollowLinkScript } from '../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; @@ -32,7 +30,7 @@ import { OpenWhere } from './nodes/OpenWhere'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; @observer -export class PropertiesButtons extends React.Component<{}, {}> { +export class PropertiesButtons extends React.Component { // eslint-disable-next-line no-use-before-define @observable public static Instance: PropertiesButtons; @@ -314,13 +312,6 @@ export class PropertiesButtons extends React.Component<{}, {}> { // ); // } - @undoBatch - handlePerspectiveChange = (e: any) => { - this.selectedDoc && (this.selectedDoc._type_collection = e.target.value); - DocumentView.Selected().forEach(docView => { - docView.layoutDoc._type_collection = e.target.value; - }); - }; @computed get onClickVal() { const linkButton = IsFollowLinkScript(this.selectedDoc.onClick); const followLoc = this.selectedDoc._followLinkLocation; @@ -452,7 +443,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { else this.selectedDoc && DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, 'onClick'); }; - propertyToggleBtn = (label: (on?: any) => string, property: string, tooltip: (on?: any) => string, icon: (on?: any) => any, onClick?: (dv: Opt, doc: Doc, property: string) => void, useUserDoc?: boolean) => { + propertyToggleBtn = (label: (on?: unknown) => string, property: string, tooltip: (on?: unknown) => string, icon: (on?: unknown) => unknown, onClick?: (dv: Opt, doc: Doc, property: string) => void, useUserDoc?: boolean) => { const targetDoc = useUserDoc ? Doc.UserDoc() : this.selectedLayoutDoc; const onPropToggle = (dv: Opt, doc: Doc, prop: string) => { (dv?.layoutDoc || doc)[prop] = !(dv?.layoutDoc || doc)[prop]; @@ -463,7 +454,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { tooltip={tooltip(BoolCast(targetDoc[property]))} text={label(targetDoc?.[property])} color={SettingsManager.userColor} - icon={icon(targetDoc?.[property] as any)} + icon={icon(targetDoc?.[property]) as string} iconPlacement="left" align="flex-start" fillWidth @@ -484,7 +475,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const isImage = layoutField instanceof ImageField; const isMap = this.selectedDoc?.type === DocumentType.MAP; const isCollection = this.selectedDoc?.type === DocumentType.COL; - const isStacking = [CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.NoteTaking].includes(this.selectedDoc?._type_collection as any); + const isStacking = [CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.NoteTaking].includes(StrCast(this.selectedDoc?._type_collection) as CollectionViewType); const isFreeForm = this.selectedDoc?._type_collection === CollectionViewType.Freeform; const isTree = this.selectedDoc?._type_collection === CollectionViewType.Tree; const toggle = (ele: JSX.Element | null, style?: React.CSSProperties) => ( diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx index edb55f341..e30d14eae 100644 --- a/src/client/views/PropertiesDocBacklinksSelector.tsx +++ b/src/client/views/PropertiesDocBacklinksSelector.tsx @@ -16,7 +16,7 @@ import { DocumentView } from './nodes/DocumentView'; type PropertiesDocBacklinksSelectorProps = { Document: Doc; - Stack?: any; + Stack?: string; hideTitle?: boolean; addDocTab(doc: Doc, location: OpenWhere): void; }; diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx index 1fea36d16..f494ff16a 100644 --- a/src/client/views/PropertiesDocContextSelector.tsx +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -1,6 +1,3 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ -/* eslint-disable jsx-a11y/anchor-is-valid */ import { computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -15,14 +12,14 @@ import { OpenWhere } from './nodes/OpenWhere'; type PropertiesDocContextSelectorProps = { DocView?: DocumentView; - Stack?: any; + Stack?: string; hideTitle?: boolean; addDocTab(doc: Doc, location: OpenWhere): void; }; @observer export class PropertiesDocContextSelector extends ObservableReactComponent { - constructor(props: any) { + constructor(props: PropertiesDocContextSelectorProps) { super(props); makeObservable(this); } diff --git a/src/client/views/PropertiesSection.tsx b/src/client/views/PropertiesSection.tsx index b9a587719..12a46c7a4 100644 --- a/src/client/views/PropertiesSection.tsx +++ b/src/client/views/PropertiesSection.tsx @@ -1,5 +1,3 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ /* eslint-disable react/require-default-props */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed } from 'mobx'; @@ -12,7 +10,7 @@ export interface PropertiesSectionProps { title: string; children?: JSX.Element | string | null; isOpen: boolean; - setIsOpen: (bool: boolean) => any; + setIsOpen: (bool: boolean) => void; onDoubleClick?: () => void; } diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 024db82a4..5952d6641 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1,7 +1,4 @@ -/* eslint-disable jsx-a11y/click-events-have-key-events */ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable prettier/prettier */ -import { IconLookup } from '@fortawesome/fontawesome-svg-core'; +import { IconLookup, IconProp } from '@fortawesome/fontawesome-svg-core'; import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox, Tooltip } from '@mui/material'; @@ -12,6 +9,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { ColorResult, SketchPicker } from 'react-color'; import * as Icons from 'react-icons/bs'; // {BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs" +import ResizeObserver from 'resize-observer-polyfill'; import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; import { Doc, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc'; @@ -44,8 +42,6 @@ import { StyleProviderFuncType } from './nodes/FieldView'; import { OpenWhere } from './nodes/OpenWhere'; import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails'; -const _global = (window /* browser */ || global) /* node */ as any; - interface PropertiesViewProps { width: number; height: number; @@ -59,7 +55,7 @@ export class PropertiesView extends ObservableReactComponent (!this.selectedLayoutDoc ? 0 : Math.min(NumCast(this.selectedLayoutDoc?._width), this._props.width - 20)); @@ -275,7 +271,7 @@ export class PropertiesView extends ObservableReactComponent this.transform; propertiesDocViewRef = (ref: HTMLDivElement) => { - const resizeObserver = new _global.ResizeObserver( + const resizeObserver = new ResizeObserver( action(() => { const cliRect = ref.getBoundingClientRect(); this.transform = new Transform(-cliRect.x, -cliRect.y, 1); @@ -357,7 +353,7 @@ export class PropertiesView extends ObservableReactComponent { + changePermissions = (e: React.ChangeEvent, user: string) => { const docs = DocumentView.Selected().length < 2 ? [this.selectedDoc] : DocumentView.Selected().map(dv => (this.layoutDocAcls ? dv.layoutDoc : dv.dataDoc)); SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs, this.layoutDocAcls); }; @@ -456,7 +452,7 @@ export class PropertiesView extends ObservableReactComponent (u1 > u2 ? -1 : u1 === u2 ? 0 : 1); + sortUsers = (u1: string, u2: string) => (u1 > u2 ? -1 : u1 === u2 ? 0 : 1); /** * Sorting algorithm to sort groups. @@ -711,7 +707,7 @@ export class PropertiesView extends ObservableReactComponent {}, title: string) => ( + inputBox = (key: string, value: string | number | undefined, setter: (val: string) => void, title: string) => (
{title}
setter(e.target.value)} onKeyDown={e => e.stopPropagation()} />
-
this.upDownButtons('up', key)))}> +
this.upDownButtons('up', key)), + 'down btn' + )}>
-
this.upDownButtons('down', key)))}> +
this.upDownButtons('down', key)), + 'up btn' + )}>
); - inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => ( + inputBoxDuo = (key: string, value: string | number | undefined, setter: (val: string) => void, title1: string, key2: string, value2: string | number | undefined, setter2: (val: string) => void, title2: string) => (
{this.inputBox(key, value, setter, title1)} {title2 === '' ? null : this.inputBox(key2, value2, setter2, title2)} @@ -841,7 +849,7 @@ export class PropertiesView extends ObservableReactComponent {}) => ( + regInput = (key: string, value: string | number | undefined, setter: (val: string) => void) => (
setter(e.target.value)} />
-
this.upDownButtons('up', key)))}> +
this.upDownButtons('up', key)), + 'up' + )}>
-
this.upDownButtons('down', key)))}> +
this.upDownButtons('down', key)), + 'down' + )}>
@@ -1002,7 +1022,7 @@ export class PropertiesView extends ObservableReactComponent { this.markHead = this.markHead ? '' : 'arrow'; }))} + onChange={undoable(action(() => { this.markHead = this.markHead ? '' : 'arrow'; }), "change arrow head")} />
@@ -1012,8 +1032,8 @@ export class PropertiesView extends ObservableReactComponent { this.markTail = this.markTail ? '' : 'arrow'; }) + onChange={undoable( + action(() => { this.markTail = this.markTail ? '' : 'arrow'; }) ,"change arrow tail" )} />
@@ -1044,7 +1064,8 @@ export class PropertiesView extends ObservableReactComponent { this._sliderBatch?.end(); }; - getNumber = (label: string, unit: string, min: number, max: number, number: number, setNumber: any, autorange?: number, autorangeMinVal?: number) => ( + + getNumber = (label: string, unit: string, min: number, max: number, number: number, setNumber: (val: number) => void, autorange?: number, autorangeMinVal?: number) => (
this.changeEffectDirection(direction)}> - {icon ? : null} + {icon ? : null}
); @@ -1368,7 +1389,7 @@ export class PropertiesView extends ObservableReactComponent { this.selectedLink && (this.selectedLink[prop] = !this.selectedLink[prop]); })) // prettier-ignore + undoable(action(() => { this.selectedLink && (this.selectedLink[prop] = !this.selectedLink[prop]); }), `toggle prop: ${prop}`) // prettier-ignore ); }; @@ -1385,17 +1406,17 @@ export class PropertiesView extends ObservableReactComponent any = val => val) => { + toggleAnchorProp = (e: React.PointerEvent, prop: string, anchor?: Doc, value: FieldType = true, ovalue: FieldType = false, cb: (val: FieldType) => void = val => val) => { anchor && setupMoveUpEvents( this, e, returnFalse, emptyFunction, - undoBatch(action(() => { + undoable(action(() => { anchor[prop] = anchor[prop] === value ? ovalue : value; - this.selectedDoc && cb(anchor[prop]); - })) // prettier-ignore + this.selectedDoc && cb(anchor[prop] as boolean); + }), `toggle anchor prop: ${prop}`) // prettier-ignore ); }; @@ -1433,7 +1454,7 @@ export class PropertiesView extends ObservableReactComponent { + setZoom = (number: string, change?: number) => { let scale = Number(number) / 100; if (change) scale += change; if (scale < 0.01) scale = 0.01; @@ -1530,7 +1551,6 @@ export class PropertiesView extends ObservableReactComponent

Play Target Audio

{ - // eslint-disable-next-line jsx-a11y/control-has-associated-label ); + const searchTitle = `${!this._searching ? 'Open' : 'Close'} Search Bar`; const curPage = NumCast(this.Document._layout_curPage) || 1; return !this._props.isContentActive() || this._pdfViewer?.isAnnotating ? null : ( @@ -473,14 +473,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent() { specificContextMenu = (): void => { const cm = ContextMenu.Instance; const options = cm.findByDescription('Options...'); - const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : []; + const optionItems = options?.subitems ?? []; !Doc.noviceMode && optionItems.push({ description: 'Toggle Sidebar Type', event: this.toggleSidebarType, icon: 'expand-arrows-alt' }); !Doc.noviceMode && optionItems.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' }); // optionItems.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" }); !options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'asterisk' }); const help = cm.findByDescription('Help...'); - const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : []; + const helpItems = help?.subitems ?? []; helpItems.push({ description: 'Copy path', event: () => this.pdfUrl && ClientUtils.CopyText(ClientUtils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' }); !help && ContextMenu.Instance.addItem({ description: 'Help...', noexpand: true, subitems: helpItems, icon: 'asterisk' }); }; diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx index 4d0e9efee..df1421a33 100644 --- a/src/client/views/nodes/formattedText/EquationView.tsx +++ b/src/client/views/nodes/formattedText/EquationView.tsx @@ -17,7 +17,7 @@ interface IEquationViewInternal { tbox: FormattedTextBox; width: number; height: number; - getPos: () => number; + getPos: () => number | undefined; setEditor: (editor: EquationEditor | undefined) => void; } @@ -47,7 +47,7 @@ export class EquationViewInternal extends React.Component className="equationView" onKeyDown={e => { if (e.key === 'Enter') { - this.props.tbox.EditorView!.dispatch(this.props.tbox.EditorView!.state.tr.setSelection(new TextSelection(this.props.tbox.EditorView!.state.doc.resolve(this.props.getPos() + 1)))); + this.props.tbox.EditorView!.dispatch(this.props.tbox.EditorView!.state.tr.setSelection(new TextSelection(this.props.tbox.EditorView!.state.doc.resolve((this.props.getPos() ?? 0) + 1)))); this.props.tbox.EditorView!.focus(); e.preventDefault(); } @@ -82,8 +82,8 @@ export class EquationView { tbox: FormattedTextBox; view: EditorView; _editor: EquationEditor | undefined; - getPos: () => number; - constructor(node: Node, view: EditorView, getPos: () => number, tbox: FormattedTextBox) { + getPos: () => number | undefined; + constructor(node: Node, view: EditorView, getPos: () => number | undefined, tbox: FormattedTextBox) { this.tbox = tbox; this.view = view; this.getPos = getPos; @@ -109,7 +109,7 @@ export class EquationView { this._editor?.mathField.focus(); } selectNode() { - this.view.dispatch(this.view.state.tr.setSelection(new TextSelection(this.view.state.doc.resolve(this.getPos())))); + this.view.dispatch(this.view.state.tr.setSelection(new TextSelection(this.view.state.doc.resolve(this.getPos() ?? 0)))); this.tbox._applyingChange = this.tbox.fieldKey; // setting focus will make prosemirror lose focus, which will cause it to change its selection to a text selection, which causes this view to get rebuilt but it's no longer node selected, so the equationview won't have focus setTimeout(() => { this._editor?.mathField.focus(); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index c8b25e184..478039ffa 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -875,7 +875,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { @@ -959,7 +959,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent RTFMarkup.Instance.setOpen(true), icon: }); !help && cm.addItem({ description: 'Help...', subitems: helpItems, icon: 'eye' }); }; -- cgit v1.2.3-70-g09d2 From 25ea424ab2e6c32272e828b98822eb32f1fe2cab Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 15 Aug 2024 11:14:04 -0400 Subject: cleaned up server list add/rem. --- src/ServerUtils.ts | 10 +- src/Utils.ts | 16 +- src/client/views/DocComponent.tsx | 4 +- src/client/views/StyleProvider.tsx | 3 +- src/client/views/collections/TabDocView.tsx | 10 +- src/client/views/collections/TreeView.tsx | 6 +- src/client/views/nodes/DocumentContentsView.tsx | 3 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 38 ++-- src/client/views/nodes/trails/PresBox.tsx | 98 ++++---- src/client/views/nodes/trails/SlideEffect.tsx | 2 +- src/fields/ObjectField.ts | 5 +- src/fields/RichTextUtils.ts | 56 +++-- src/fields/util.ts | 4 +- src/server/database.ts | 2 +- src/server/websocket.ts | 248 +++++++-------------- 15 files changed, 201 insertions(+), 304 deletions(-) (limited to 'src/client/views/nodes/DocumentContentsView.tsx') diff --git a/src/ServerUtils.ts b/src/ServerUtils.ts index 7e821cda2..904541fc7 100644 --- a/src/ServerUtils.ts +++ b/src/ServerUtils.ts @@ -9,20 +9,20 @@ export namespace ServerUtils { socket.emit(message.Message, args); } - export function AddServerHandler(socket: Socket, message: Message, handler: (args: T) => any) { + export function AddServerHandler(socket: Socket, message: Message, handler: (args: T) => void) { socket.on(message.Message, Utils.loggingCallback('Incoming', handler, message.Name)); } - export function AddServerHandlerCallback(socket: Socket, message: Message, handler: (args: [T, (res: any) => any]) => any) { - socket.on(message.Message, (arg: T, fn: (res: any) => any) => { + export function AddServerHandlerCallback(socket: Socket, message: Message, handler: (args: [T, (res: unknown) => void]) => void) { + socket.on(message.Message, (arg: T, fn: (res: unknown) => void) => { Utils.log('S receiving', message.Name, arg, true); handler([arg, Utils.loggingCallback('S sending', fn, message.Name)]); }); } - export type RoomHandler = (socket: Socket, room: string) => any; + export type RoomHandler = (socket: Socket, room: string) => void; export type UsedSockets = Socket; export type RoomMessage = 'create or join' | 'created' | 'joined'; export function AddRoomHandler(socket: Socket, message: RoomMessage, handler: RoomHandler) { - socket.on(message, (room: any) => handler(socket, room)); + socket.on(message, room => handler(socket, room)); } } diff --git a/src/Utils.ts b/src/Utils.ts index 23ae38bdb..0590c6930 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-namespace */ import * as uuid from 'uuid'; export function clamp(n: number, lower: number, upper: number) { @@ -23,6 +24,7 @@ export namespace Utils { export const loggingEnabled = false; export const logFilter: number | undefined = undefined; + // eslint-disable-next-line @typescript-eslint/no-explicit-any export function log(prefixIn: string, messageName: string, messageIn: any, receiving: boolean) { let prefix = prefixIn; let message = messageIn; @@ -38,8 +40,9 @@ export namespace Utils { console.log(`${prefix}: ${idString}, ${receiving ? 'receiving' : 'sending'} ${messageName} with data ${JSON.stringify(message)} `); } - export function loggingCallback(prefix: string, func: (args: any) => any, messageName: string) { - return (args: any) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + export function loggingCallback(prefix: string, func: (args: any) => void, messageName: string) { + return (args: unknown) => { log(prefix, messageName, args, true); func(args); }; @@ -47,7 +50,9 @@ export namespace Utils { export function TraceConsoleLog() { ['log', 'warn'].forEach(method => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any const old = (console as any)[method]; + // eslint-disable-next-line @typescript-eslint/no-explicit-any (console as any)[method] = function (...args: any[]) { let stack = new Error('').stack?.split(/\n/); // Chrome includes a single "Error" line, FF doesn't. @@ -158,7 +163,7 @@ export function timenow() { const now = new Date(); let ampm = 'am'; let h = now.getHours(); - let m: any = now.getMinutes(); + let m: string | number = now.getMinutes(); if (h >= 12) { if (h > 12) h -= 12; ampm = 'pm'; @@ -201,7 +206,7 @@ export function intersectRect(r1: { left: number; top: number; width: number; he export function stringHash(s?: string) { // eslint-disable-next-line no-bitwise - return !s ? undefined : Math.abs(s.split('').reduce((a: any, b: any) => (n => n & n)((a << 5) - a + b.charCodeAt(0)), 0)); + return !s ? undefined : Math.abs(s.split('').reduce((a, b) => (n => n & n)((a << 5) - a + b.charCodeAt(0)), 0)); } export function percent2frac(percent: string) { @@ -224,8 +229,6 @@ export function emptyFunction() { return undefined; } -export const emptyPath: any[] = []; - export function unimplementedFunction() { throw new Error('This function is not implemented, but should be.'); } @@ -246,6 +249,7 @@ export function DeepCopy(source: Map, predicate?: Predicate) { export namespace JSONUtils { export function tryParse(source: string) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any let results: any; try { results = JSON.parse(source); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index e5752dcd2..e351e2dec 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -93,7 +93,7 @@ export function ViewBoxBaseComponent

() { * This is the unique data repository for a dcoument that stores the intrinsic document data */ @computed get dataDoc() { - return this.Document.isTemplateForField || this.Document.isTemplateDoc ? this._props.TemplateDataDocument ?? this.Document[DocData] : this.Document[DocData]; + return this.Document.isTemplateForField || this.Document.isTemplateDoc ? (this._props.TemplateDataDocument ?? this.Document[DocData]) : this.Document[DocData]; } /** @@ -151,7 +151,7 @@ export function ViewBoxAnnotatableComponent

() { * This is the unique data repository for a dcoument that stores the intrinsic document data */ @computed get dataDoc() { - return this.Document.isTemplateForField || this.Document.isTemplateDoc ? this._props.TemplateDataDocument ?? this.Document[DocData] : this.Document[DocData]; + return this.Document.isTemplateForField || this.Document.isTemplateDoc ? (this._props.TemplateDataDocument ?? this.Document[DocData]) : this.Document[DocData]; } /** diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 2792955a0..374f8ca3a 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -13,7 +13,6 @@ import { Id } from '../../fields/FieldSymbols'; import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../fields/Types'; import { AudioAnnoState } from '../../server/SharedMediaTypes'; -import { emptyPath } from '../../Utils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; import { IsFollowLinkScript } from '../documents/DocUtils'; import { SnappingManager } from '../util/SnappingManager'; @@ -406,5 +405,5 @@ export function DashboardStyleProvider(doc: Opt, props: Opt } export function returnEmptyDocViewList() { - return emptyPath; + return [] as DocumentView[]; } diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index f168db81b..31b6be927 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -7,7 +7,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; import ResizeObserver from 'resize-observer-polyfill'; -import { ClientUtils, DashColor, lightOrDark, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils'; +import { ClientUtils, DashColor, lightOrDark, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; import { Doc, Opt, returnEmptyDoclist } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; @@ -157,8 +157,8 @@ export class TabMinimapView extends ObservableReactComponent { PanelWidth={this.PanelWidth} PanelHeight={this.PanelHeight} styleProvider={DefaultStyleProvider} - childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDocViewList} - childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDocViewList} + childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyFilter} + childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyFilter} searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist} addDocument={undefined} removeDocument={this.remDocTab} diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index c0fe7a894..b10a521ca 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -6,7 +6,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { ClientUtils, lightOrDark, return18, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; -import { Doc, DocListCast, Field, FieldResult, FieldType, Opt, StrListCast, returnEmptyDoclist } from '../../../fields/Doc'; +import { Doc, DocListCast, Field, FieldType, Opt, StrListCast, returnEmptyDoclist } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; @@ -249,7 +249,7 @@ export class TreeView extends ObservableReactComponent { return []; } - const runningChildren: FieldResult[] = []; + const runningChildren: Doc[] = []; childList.forEach(child => { if (child.runProcess && TreeView.GetRunningChildren.get(child)) { if (child.runProcess) { @@ -261,7 +261,7 @@ export class TreeView extends ObservableReactComponent { return runningChildren; }; - static GetRunningChildren = new Map FieldResult[]>(); + static GetRunningChildren = new Map Doc[]>(); static ToggleChildrenRun = new Map void>(); constructor(props: TreeViewProps) { super(props); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index b178d6554..15baebae1 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -192,6 +192,7 @@ export class DocumentContentsView extends ObservableReactComponent 12 || !layoutFrame || !this.layoutDoc || GetEffectiveAcl(this.layoutDoc) === AclPrivate ? null : ( new FormattedTextBoxComment() }), + ], + }; + } private static nodeViews: (self: FormattedTextBox) => { [key: string]: NodeViewConstructor }; /** * Initialize the class with all the plugin node view components @@ -108,7 +123,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent; - private _keymap: KeyMap | undefined = undefined; private _rules: RichTextRules | undefined; private _forceUncollapse = true; // if the cursor doesn't move between clicks, then the selection will disappear for some reason. This flags the 2nd click as happening on a selection which allows bullet points to toggle private _break = true; @@ -128,20 +142,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent new FormattedTextBoxComment() }), - ], - }; + return FormattedTextBox.MakeConfig(this._rules, this._props); } public get EditorView() { @@ -1380,11 +1382,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - this._props.rootSelected?.() && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView); - return new RichTextMenuPlugin({ editorProps: this._props }); + props?.rootSelected?.() && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView); + return new RichTextMenuPlugin({ editorProps: props }); }), }); } diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 0c73400a9..c403f9415 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -1,5 +1,3 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import Slider from '@mui/material/Slider'; @@ -32,7 +30,7 @@ import { dropActionType } from '../../../util/DropActionTypes'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; import { SerializationHelper } from '../../../util/SerializationHelper'; import { SnappingManager } from '../../../util/SnappingManager'; -import { undoBatch, UndoManager } from '../../../util/UndoManager'; +import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; import { CollectionFreeFormPannableContents } from '../../collections/collectionFreeForm/CollectionFreeFormPannableContents'; import { CollectionView } from '../../collections/CollectionView'; @@ -191,7 +189,7 @@ export class PresBox extends ViewBoxBaseComponent() { @computed get isTreeOrStack() { - return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._type_collection) as any); + return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._type_collection) as CollectionViewType); } @computed get isTree() { return this.layoutDoc._type_collection === CollectionViewType.Tree; @@ -304,7 +302,7 @@ export class PresBox extends ViewBoxBaseComponent() { // 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played startTempMedia = (targetDoc: Doc, activeItem: Doc) => { const duration: number = NumCast(activeItem.config_clipEnd) - NumCast(activeItem.config_clipStart); - if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as any)) { + if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as DocumentType)) { const targMedia = DocumentView.getDocumentView(targetDoc); targMedia?.ComponentView?.playFrom?.(NumCast(activeItem.config_clipStart), NumCast(activeItem.config_clipStart) + duration); } @@ -312,7 +310,7 @@ export class PresBox extends ViewBoxBaseComponent() { stopTempMedia = (targetDocField: FieldResult) => { const targetDoc = DocCast(DocCast(targetDocField).annotationOn) ?? DocCast(targetDocField); - if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as any)) { + if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as DocumentType)) { const targMedia = DocumentView.getDocumentView(targetDoc); targMedia?.ComponentView?.Pause?.(); } @@ -364,7 +362,7 @@ export class PresBox extends ViewBoxBaseComponent() { this.setIsRecording(false); this.setIsLoading(true); - const currSlideProperties: { [key: string]: any } = {}; + const currSlideProperties: { [key: string]: FieldResult } = {}; gptSlideProperties.forEach(key => { if (this.activeItem[key]) { currSlideProperties[key] = this.activeItem[key]; @@ -554,7 +552,7 @@ export class PresBox extends ViewBoxBaseComponent() { } }); static pinDataTypes(target?: Doc): dataTypes { - const targetType = target?.type as any; + const targetType = target?.type as DocumentType; const inkable = [DocumentType.INK].includes(targetType); const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetType) || target?._type_collection === CollectionViewType.Stacking; const pannable = [DocumentType.IMG, DocumentType.PDF].includes(targetType) || (targetType === DocumentType.COL && target?._type_collection === CollectionViewType.Freeform); @@ -759,8 +757,8 @@ export class PresBox extends ViewBoxBaseComponent() { const doc = DocCast(DocServer.GetCachedRefField(data.id)); if (doc) { transitioned.add(doc); - const field = !data.data ? undefined : await SerializationHelper.Deserialize(data.data); - const tfield = !data.text ? undefined : await SerializationHelper.Deserialize(data.text); + const field = !data.data ? undefined : ((await SerializationHelper.Deserialize(data.data)) as FieldType); + const tfield = !data.text ? undefined : ((await SerializationHelper.Deserialize(data.text)) as FieldType); doc._dataTransition = `all ${transTime}ms`; doc.x = data.x; doc.y = data.y; @@ -858,7 +856,7 @@ export class PresBox extends ViewBoxBaseComponent() { effect: activeItem, noSelect: true, openLocation: targetDoc.type === DocumentType.PRES ? ((OpenWhere.replace + ':' + PresBox.PanelName) as OpenWhere) : OpenWhere.addLeft, - easeFunc: StrCast(activeItem.presentation_easeFunc, 'ease') as any, + easeFunc: StrCast(activeItem.presentation_easeFunc, 'ease') as 'linear' | 'ease', zoomTextSelections: BoolCast(activeItem.presentation_zoomText), playAudio: BoolCast(activeItem.presentation_playAudio), playMedia: activeItem.presentation_mediaStart === 'auto', @@ -1101,7 +1099,7 @@ export class PresBox extends ViewBoxBaseComponent() { */ @undoBatch viewChanged = action((e: React.ChangeEvent) => { - const typeCollection = (e.target as any).selectedOptions[0].value as CollectionViewType; + const typeCollection = (e.target as HTMLSelectElement).selectedOptions[0].value as CollectionViewType; this.layoutDoc.presFieldKey = this.fieldKey + (typeCollection === CollectionViewType.Tree ? '-linearized' : ''); // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here [CollectionViewType.Tree || CollectionViewType.Stacking].includes(typeCollection) && (this.Document._pivotField = undefined); @@ -1111,30 +1109,8 @@ export class PresBox extends ViewBoxBaseComponent() { } }); - /** - * Called when the user changes the view type - * Either 'List' (stacking) or 'Slides' (carousel) - */ - // @undoBatch - mediaStopChanged = action((e: React.ChangeEvent) => { - const { activeItem } = this; - const stopDoc = (e.target as any).selectedOptions[0].value as string; - const stopDocIndex = Number(stopDoc[0]); - activeItem.mediaStopDoc = stopDocIndex; - if (this.childDocs[stopDocIndex - 1].mediaStopTriggerList) { - const list = DocListCast(this.childDocs[stopDocIndex - 1].mediaStopTriggerList); - list.push(activeItem); - // this.childDocs[stopDocIndex - 1].mediaStopTriggerList = list;\ - } else { - this.childDocs[stopDocIndex - 1].mediaStopTriggerList = new List(); - const list = DocListCast(this.childDocs[stopDocIndex - 1].mediaStopTriggerList); - list.push(activeItem); - // this.childDocs[stopDocIndex - 1].mediaStopTriggerList = list; - } - }); - movementName = action((activeItem: Doc) => { - if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Center, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presentation_movement) as any)) { + if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Center, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presentation_movement) as PresMovement)) { return PresMovement.Zoom; } return StrCast(activeItem.presentation_movement); @@ -1185,7 +1161,7 @@ export class PresBox extends ViewBoxBaseComponent() { * Method to get the list of selected items in the order in which they have been selected */ @computed get listOfSelected() { - return Array.from(this.selectedArray).map((doc: Doc, index: any) => { + return Array.from(this.selectedArray).map((doc, index) => { const curDoc = Cast(doc, Doc, null); const tagDoc = Cast(curDoc.presentation_targetDoc, Doc, null); if (curDoc && curDoc === this.activeItem) @@ -1193,7 +1169,7 @@ export class PresBox extends ViewBoxBaseComponent() { // eslint-disable-next-line react/no-array-index-key

- {index + 1}. {curDoc.title} + {index + 1}. {StrCast(curDoc.title)})
); @@ -1201,14 +1177,14 @@ export class PresBox extends ViewBoxBaseComponent() { return ( // eslint-disable-next-line react/no-array-index-key
- {index + 1}. {curDoc.title} + {index + 1}. {StrCast(curDoc.title)}
); if (curDoc) return ( // eslint-disable-next-line react/no-array-index-key
- {index + 1}. {curDoc.title} + {index + 1}. {StrCast(curDoc.title)}
); return null; @@ -1301,13 +1277,14 @@ export class PresBox extends ViewBoxBaseComponent() { switch (e.key) { case 'Backspace': if (this.layoutDoc.presentation_status === 'edit') { - undoBatch( + undoable( action(() => { Array.from(this.selectedArray).forEach(doc => this.removeDocument(doc)); this.clearSelectedArray(); this._eleArray.length = 0; this._dragArray.length = 0; - }) + }), + 'delete slides' )(); handled = true; } @@ -1488,7 +1465,7 @@ export class PresBox extends ViewBoxBaseComponent() { ); }; // Converts seconds to ms and updates presentation_transition - public static SetTransitionTime = (number: String, setter: (timeInMS: number) => void, change?: number) => { + public static SetTransitionTime = (number: string, setter: (timeInMS: number) => void, change?: number) => { let timeInMS = Number(number) * 1000; if (change) timeInMS += change; if (timeInMS < 100) timeInMS = 100; @@ -1497,7 +1474,7 @@ export class PresBox extends ViewBoxBaseComponent() { }; @undoBatch - updateTransitionTime = (number: String, change?: number) => { + updateTransitionTime = (number: string, change?: number) => { PresBox.SetTransitionTime( number, (timeInMS: number) => @@ -1510,7 +1487,7 @@ export class PresBox extends ViewBoxBaseComponent() { // Converts seconds to ms and updates presentation_transition @undoBatch - updateZoom = (number: String, change?: number) => { + updateZoom = (number: string, change?: number) => { let scale = Number(number) / 100; if (change) scale += change; if (scale < 0.01) scale = 0.01; @@ -1524,7 +1501,7 @@ export class PresBox extends ViewBoxBaseComponent() { * Converts seconds to ms and updates presentation_duration */ @undoBatch - updateDurationTime = (number: String, change?: number) => { + updateDurationTime = (number: string, change?: number) => { let timeInMS = Number(number) * 1000; if (change) timeInMS += change; if (timeInMS < 100) timeInMS = 100; @@ -1608,9 +1585,9 @@ export class PresBox extends ViewBoxBaseComponent() { }); }; - static _sliderBatch: any; + static _sliderBatch: UndoManager.Batch | undefined; static endBatch = () => { - PresBox._sliderBatch.end(); + PresBox._sliderBatch?.end(); document.removeEventListener('pointerup', PresBox.endBatch, true); }; public static inputter = (min: string, step: string, max: string, value: number, active: boolean, change: (val: string) => void, hmargin?: number) => ( @@ -1704,7 +1681,7 @@ export class PresBox extends ViewBoxBaseComponent() {
- {[DocumentType.AUDIO, DocumentType.VID].includes(targetType as any as DocumentType) ? null : ( + {[DocumentType.AUDIO, DocumentType.VID].includes(targetType as DocumentType) ? null : ( <>
Slide Duration
@@ -1847,7 +1824,7 @@ export class PresBox extends ViewBoxBaseComponent() { if (activeItem && this.targetDoc) { const transitionSpeed = activeItem.presentation_transition ? NumCast(activeItem.presentation_transition) / 1000 : 0.5; const zoom = NumCast(activeItem.config_zoom, 1) * 100; - const effect = StrCast(activeItem.presentation_effect) ? (StrCast(activeItem.presentation_effect) as any as PresEffect) : PresEffect.None; + const effect = StrCast(activeItem.presentation_effect) ? (StrCast(activeItem.presentation_effect) as PresEffect) : PresEffect.None; const direction = StrCast(activeItem.presentation_effectDirection) as PresEffectDirection; return ( @@ -2660,24 +2637,26 @@ export class PresBox extends ViewBoxBaseComponent() {
e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
{ this.enterMinimize(); this.turnOffEdit(true); this.gotoDocument(this.itemIndex, this.activeItem); - }) + }), + 'minimze presentation' )}> Mini-player
{ this.layoutDoc.presentation_status = 'manual'; this.initializePresState(this.itemIndex); this.turnOffEdit(true); this.gotoDocument(this.itemIndex, this.activeItem); - }) + }), + 'make presentation manual' )}> Sidebar player
@@ -2773,13 +2752,13 @@ export class PresBox extends ViewBoxBaseComponent() {
{ + onClick={undoable(() => { if (this.childDocs.length) { this.layoutDoc.presentation_status = 'manual'; this.initializePresState(this.itemIndex); this.gotoDocument(this.itemIndex, this.activeItem); } - })}> + }, 'start presentation')}>
200 ? 'inline-flex' : 'none' }}>  Present
@@ -2911,11 +2890,12 @@ export class PresBox extends ViewBoxBaseComponent() { {this._props.PanelWidth() > 250 ? (
{ this.layoutDoc.presentation_status = PresStatus.Edit; clearTimeout(this._presTimer); - }) + }), + 'edit presetnation' )}> EXIT
@@ -2988,7 +2968,7 @@ export class PresBox extends ViewBoxBaseComponent() { }; sort = (treeViewMap: Map) => [...treeViewMap.entries()].sort((a: [Doc, number], b: [Doc, number]) => (a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0)).map(kv => kv[0]); - + emptyHierarchy = []; render() { // needed to ensure that the childDocs are loaded for looking up fields this.childDocs.slice(); @@ -3086,7 +3066,7 @@ export class PresBox extends ViewBoxBaseComponent() { ScreenToLocalTransform={this.getTransform} AddToMap={this.AddToMap} RemFromMap={this.RemFromMap} - hierarchyIndex={emptyPath} + hierarchyIndex={this.emptyHierarchy} /> ) : null}
diff --git a/src/client/views/nodes/trails/SlideEffect.tsx b/src/client/views/nodes/trails/SlideEffect.tsx index 00039e3cb..a114c231f 100644 --- a/src/client/views/nodes/trails/SlideEffect.tsx +++ b/src/client/views/nodes/trails/SlideEffect.tsx @@ -103,7 +103,7 @@ export default function SpringAnimation({ doc, dir, springSettings, presEffect, api.start({ loop: infinite, delay: infinite ? 500 : 0 }); } }, [inView]); - const animatedDiv = (style: any) => ( + const animatedDiv = (style: object) => ( `${val}`) }}> {children} diff --git a/src/fields/ObjectField.ts b/src/fields/ObjectField.ts index 5f31208eb..c533cb596 100644 --- a/src/fields/ObjectField.ts +++ b/src/fields/ObjectField.ts @@ -12,9 +12,8 @@ export interface serializedDoctype { export type serverOpType = { $set?: serializedFieldsType; // $unset?: { [key: string]: unknown }; - $remFromSet?: { [key: string]: { fields: serializedFieldType[] } | { deleteCount: number; start: number } | undefined; hint?: { deleteCount: number; start: number } }; - $addToSet?: serializedFieldsType; - length?: number; + $remFromSet?: { [key: string]: { fields: serializedFieldType[] } | { deleteCount: number; start: number } | number | undefined; length: number; hint: { deleteCount: number; start: number } | undefined }; + $addToSet?: { [key: string]: { fields: serializedFieldType[] } | number | undefined; length: number }; }; export abstract class ObjectField { // prettier-ignore diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts index 3763dcd2c..d1316d256 100644 --- a/src/fields/RichTextUtils.ts +++ b/src/fields/RichTextUtils.ts @@ -1,9 +1,10 @@ +/* eslint-disable @typescript-eslint/no-namespace */ /* eslint-disable no-await-in-loop */ /* eslint-disable no-use-before-define */ import { AssertionError } from 'assert'; import * as Color from 'color'; import { docs_v1 as docsV1 } from 'googleapis'; -import { Fragment, Mark, Node } from 'prosemirror-model'; +import { Fragment, Mark, Node, Schema } from 'prosemirror-model'; import { sinkListItem } from 'prosemirror-schema-list'; import { EditorState, TextSelection, Transaction } from 'prosemirror-state'; import { ClientUtils, DashColor } from '../ClientUtils'; @@ -26,7 +27,7 @@ export namespace RichTextUtils { const joiner = ''; export const Initialize = (initial?: string) => { - const content: any[] = []; + const content: object[] = []; const state = { doc: { type: 'doc', @@ -80,8 +81,10 @@ export namespace RichTextUtils { // Preserve the current state, but re-write the content to be the blocks const parsed = JSON.parse(oldState ? oldState.Data : Initialize()); parsed.doc.content = elements.map(text => { - const paragraph: any = { type: 'paragraph' }; - text.length && (paragraph.content = [{ type: 'text', marks: [], text }]); // An empty paragraph gets treated as a line break + const paragraph: object = { + type: 'paragraph', + content: text.length ? [{ type: 'text', marks: [], text }] : undefined, // An empty paragraph gets treated as a line break + }; return paragraph; }); @@ -164,7 +167,7 @@ export namespace RichTextUtils { const inlineObjectMap = await parseInlineObjects(document); const title = document.title!; const { text, paragraphs } = GoogleApiClientUtils.Docs.Utils.extractText(document); - let state = EditorState.create(new FormattedTextBox({} as any).config); + let state = EditorState.create(FormattedTextBox.MakeConfig()); const structured = parseLists(paragraphs); let position = 3; @@ -253,17 +256,20 @@ export namespace RichTextUtils { return groups; }; - const listItem = (lschema: any, runs: docsV1.Schema$TextRun[]): Node => lschema.node('list_item', null, paragraphNode(lschema, runs)); + const listItem = (lschema: Schema, runs: docsV1.Schema$TextRun[]): Node => lschema.node('list_item', null, paragraphNode(lschema, runs)); - const list = (lschema: any, items: Node[]): Node => lschema.node('ordered_list', { mapStyle: 'bullet' }, items); + const list = (lschema: Schema, items: Node[]): Node => lschema.node('ordered_list', { mapStyle: 'bullet' }, items); - const paragraphNode = (lschema: any, runs: docsV1.Schema$TextRun[]): Node => { - const children = runs.map(run => textNode(lschema, run)).filter(child => child !== undefined); + const paragraphNode = (lschema: Schema, runs: docsV1.Schema$TextRun[]): Node => { + const children = runs + .map(run => textNode(lschema, run)) + .filter(child => child !== undefined) + .map(child => child!); const fragment = children.length ? Fragment.from(children) : undefined; return lschema.node('paragraph', null, fragment); }; - const imageNode = (lschema: any, image: ImageTemplate, textNote: Doc) => { + const imageNode = (lschema: Schema, image: ImageTemplate, textNote: Doc) => { const { url: src, width, agnostic } = image; let docId: string; const guid = Utils.GenerateDeterministicGuid(agnostic); @@ -279,7 +285,7 @@ export namespace RichTextUtils { return lschema.node('image', { src, agnostic, width, docId, float: null }); }; - const textNode = (lschema: any, run: docsV1.Schema$TextRun) => { + const textNode = (lschema: Schema, run: docsV1.Schema$TextRun) => { const text = run.content!.removeTrailingNewlines(); return text.length ? lschema.text(text, styleToMarks(lschema, run.textStyle)) : undefined; }; @@ -291,29 +297,33 @@ export namespace RichTextUtils { ['fontSize', 'pFontSize'], ]); - const styleToMarks = (lschema: any, textStyle?: docsV1.Schema$TextStyle) => { + const styleToMarks = (lschema: Schema, textStyle?: docsV1.Schema$TextStyle) => { if (!textStyle) { return undefined; } const marks: Mark[] = []; Object.keys(textStyle).forEach(key => { const targeted = key as keyof docsV1.Schema$TextStyle; - const value = textStyle[targeted] as any; + const value = textStyle[targeted]; if (value) { - const attributes: any = {}; + const attributes: { [key: string]: number | string } = {}; let converted = StyleToMark.get(targeted) || targeted; - value.url && (attributes.href = value.url); - if (value.color) { - const object = value.color.rgbColor; - attributes.color = Color.rgb(['red', 'green', 'blue'].map(color => object[color] * 255 || 0)).hex(); + const urlValue = value as docsV1.Schema$Link; + urlValue.url && (attributes.href = urlValue.url); + const colValue = value as docsV1.Schema$OptionalColor; + const object = colValue.color?.rgbColor; + if (object) { + attributes.color = Color.rgb(['red', 'green', 'blue'].map(color => (object as { [key: string]: number })[color] * 255 || 0)).hex(); } - if (value.magnitude) { - attributes.fontSize = value.magnitude; + const magValue = value as docsV1.Schema$Dimension; + if (magValue.magnitude) { + attributes.fontSize = magValue.magnitude; } + const fontValue = value as docsV1.Schema$WeightedFontFamily; if (converted === 'weightedFontFamily') { - converted = ImportFontFamilyMapping.get(value.fontFamily) || 'timesNewRoman'; + converted = (fontValue.fontFamily && ImportFontFamilyMapping.get(fontValue.fontFamily)) || 'timesNewRoman'; } const mapped = lschema.marks[converted]; @@ -388,7 +398,7 @@ export namespace RichTextUtils { continue; } let converted = MarkToStyle.get(markName) || (markName as keyof docsV1.Schema$TextStyle); - let value: any = true; + let value: unknown = true; if (!converted) { // eslint-disable-next-line no-continue continue; @@ -436,7 +446,7 @@ export namespace RichTextUtils { converted = 'fontSize'; value = { magnitude: parseInt(matches[1].replace('px', '')), unit: 'PT' }; } - textStyle[converted] = value; + textStyle[converted] = value as undefined; } if (Object.keys(textStyle).length) { requests.push(EncodeStyleUpdate(information)); diff --git a/src/fields/util.ts b/src/fields/util.ts index a5c56607c..60eadcdfd 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -398,9 +398,9 @@ export function containedFieldChangedHandler(container: ListImpl | Do const serializeItems = () => ({ __type: 'list', fields: diff?.items?.map((item: FieldType) => SerializationHelper.Serialize(item) as serializedFieldType) ?? [] }); // prettier-ignore const serverOp: serverOpType = diff?.op === '$addToSet' - ? { $addToSet: { ['fields.' + prop]: serializeItems() }, length: diff.length } + ? { $addToSet: { ['fields.' + prop]: serializeItems(), length: diff.length ??0 }} : diff?.op === '$remFromSet' - ? { $remFromSet: { ['fields.' + prop]: serializeItems(), hint: diff.hint}, length: diff.length } + ? { $remFromSet: { ['fields.' + prop]: serializeItems(), hint: diff.hint, length: diff.length ?? 0 } } : { $set: { ['fields.' + prop]: SerializationHelper.Serialize(liveContainedField) as {fields: serializedFieldType[]}} }; if (!(container instanceof Doc) || !container[UpdatingFromServer]) { diff --git a/src/server/database.ts b/src/server/database.ts index a93117349..975b9eb80 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -32,7 +32,7 @@ export namespace Database { try { const { connection } = mongoose; disconnect = async () => - new Promise(resolve => { + new Promise(resolve => { connection.close().then(resolve); }); if (connection.readyState === ConnectionStates.disconnected) { diff --git a/src/server/websocket.ts b/src/server/websocket.ts index f588151a5..ccbcb1c5f 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -49,12 +49,12 @@ export namespace WebSocket { DashStats.logUserLogin(userEmail); } - function GetRefFieldLocal([id, callback]: [string, (result?: serializedDoctype) => void]) { + function GetRefFieldLocal(id: string, callback: (result?: serializedDoctype | undefined) => void) { return Database.Instance.getDocument(id, callback); } function GetRefField([id, callback]: [string, (result?: serializedDoctype) => void]) { process.stdout.write(`+`); - GetRefFieldLocal([id, callback]); + GetRefFieldLocal(id, callback); } function GetRefFields([ids, callback]: [string[], (result?: serializedDoctype[]) => void]) { @@ -62,112 +62,46 @@ export namespace WebSocket { Database.Instance.getDocuments(ids, callback); } - const suffixMap: { [type: string]: string | [string, string | ((json: any) => any)] } = { - number: '_n', - string: '_t', - boolean: '_b', - image: ['_t', 'url'], - video: ['_t', 'url'], - pdf: ['_t', 'url'], - audio: ['_t', 'url'], - web: ['_t', 'url'], - map: ['_t', 'url'], - script: ['_t', value => value.script.originalScript], - RichTextField: ['_t', value => value.Text], - date: ['_d', value => new Date(value.date).toISOString()], - proxy: ['_i', 'fieldId'], - list: [ - '_l', - list => { - const results: any[] = []; - // eslint-disable-next-line no-use-before-define - list.fields.forEach((value: any) => ToSearchTerm(value) && results.push(ToSearchTerm(value)!.value)); - return results.length ? results : null; - }, - ], - }; - - function ToSearchTerm(valIn: any): { suffix: string; value: any } | undefined { - let val = valIn; - if (val === null || val === undefined) { - return undefined; - } - const type = val.__type || typeof val; - - let suffix = suffixMap[type]; - if (!suffix) { - return undefined; - } - if (Array.isArray(suffix)) { - const accessor = suffix[1]; - if (typeof accessor === 'function') { - val = accessor(val); - } else { - val = val[accessor]; - } - [suffix] = suffix; - } - return { suffix, value: val }; - } - - function getSuffix(value: string | [string, any]): string { - return typeof value === 'string' ? value : value[0]; - } const pendingOps = new Map(); - function dispatchNextOp(id: string) { - const next = pendingOps.get(id)!.shift(); + function dispatchNextOp(id: string): unknown { + const next = pendingOps.get(id)?.shift(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const nextOp = (res: boolean) => dispatchNextOp(id); if (next) { const { diff, socket } = next; - if (diff.diff.$addToSet) { - // eslint-disable-next-line no-use-before-define - return GetRefFieldLocal([diff.id, (result?: serializedDoctype) => addToListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own + // ideally, we'd call the Database update method for all actions, but for now we handle list insertion/removal on our own + switch (diff.diff.$addToSet ? 'add' : diff.diff.$remFromSet ? 'rem' : 'set') { + case 'add': return GetRefFieldLocal(id, (result) => addToListField(socket, diff, result, nextOp)); // prettier-ignore + case 'rem': return GetRefFieldLocal(id, (result) => remFromListField(socket, diff, result, nextOp)); // prettier-ignore + default: return Database.Instance.update(id, diff.diff, + () => nextOp(socket.broadcast.emit(MessageStore.UpdateField.Message, diff)), + false + ); // prettier-ignore } - if (diff.diff.$remFromSet) { - // eslint-disable-next-line no-use-before-define - return GetRefFieldLocal([diff.id, (result?: serializedDoctype) => remFromListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own - } - // eslint-disable-next-line no-use-before-define - return SetField(socket, diff); } - return !pendingOps.get(id)!.length && pendingOps.delete(id); + return !pendingOps.get(id)?.length && pendingOps.delete(id); } - function addToListField(socket: Socket, diffIn: Diff, listDoc?: serializedDoctype): void { - const diff = diffIn; - diff.diff.$set = diff.diff.$addToSet; - delete diff.diff.$addToSet; // convert add to set to a query of the current fields, and then a set of the composition of the new fields with the old ones - const updatefield = Array.from(Object.keys(diff.diff.$set ?? {}))[0]; - const newListItems = diff.diff.$set?.[updatefield]?.fields; - if (!newListItems) { - console.log('Error: addToListField - no new list items'); - return; - } - const listItems = listDoc?.fields?.[updatefield.replace('fields.', '')]?.fields.filter(item => item !== undefined) ?? []; - if (diff.diff.$set?.[updatefield]?.fields !== undefined) { + function addToListField(socket: Socket, diff: Diff, listDoc: serializedDoctype | undefined, cb: (res: boolean) => void): void { + const $addToSet = diff.diff.$addToSet as serializedFieldsType; + const updatefield = Array.from(Object.keys($addToSet ?? {}))[0]; + const newListItems = $addToSet?.[updatefield]?.fields; + + if (newListItems) { + const length = diff.diff.$addToSet?.length; + diff.diff.$set = $addToSet; // convert add to set to a query of the current fields, and then a set of the composition of the new fields with the old ones + delete diff.diff.$addToSet; // can't pass $set to Mongo, or it will do that insetead of $addToSet + const listItems = listDoc?.fields?.[updatefield.replace('fields.', '')]?.fields.filter(item => item) ?? []; diff.diff.$set[updatefield]!.fields = [...listItems, ...newListItems]; // , ...newListItems.filter((newItem: any) => newItem === null || !curList.some((curItem: any) => curItem.fieldId ? curItem.fieldId === newItem.fieldId : curItem.heading ? curItem.heading === newItem.heading : curItem === newItem))]; - const sendBack = diff.diff.length !== diff.diff.$set[updatefield]!.fields?.length; - delete diff.diff.length; - Database.Instance.update( - diff.id, - diff.diff, - () => { - if (sendBack) { - console.log('Warning: list modified during update. Composite list is being returned.'); - const { id } = socket; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (socket as any).id = ''; // bcz: HACK to reference private variable. this allows the update message to go back to the client that made the change. - socket.broadcast.emit(MessageStore.UpdateField.Message, diff); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (socket as any).id = id; - } else { - socket.broadcast.emit(MessageStore.UpdateField.Message, diff); - } - dispatchNextOp(diff.id); - }, - false - ); - } + + // if the client's list length is not the same as what we're writing to the server, + // then we need to send the server's version back to the client so that they are in synch. + // this could happen if another client made a change before the server receives the update from the first client + const target = length !== diff.diff.$set[updatefield].fields.length ? socket : socket.broadcast; + target === socket && console.log('Warning: SEND BACK: list modified during add update. Composite list is being returned.'); + Database.Instance.update(diff.id, diff.diff, () => cb(target.emit(MessageStore.UpdateField.Message, diff)), false); + } else cb(false); } /** @@ -206,15 +140,17 @@ export namespace WebSocket { * items to delete) * @param curListItems the server's current copy of the data */ - function remFromListField(socket: Socket, diffIn: Diff, curListItems?: serializedDoctype): void { - const diff = diffIn; - diff.diff.$set = diff.diff.$remFromSet as serializedFieldsType; - const hint = diff.diff.$remFromSet?.hint; - delete diff.diff.$remFromSet; - const updatefield = Array.from(Object.keys(diff.diff.$set ?? {}))[0]; - const remListItems = diff.diff.$set[updatefield]?.fields; - if (diff.diff.$set[updatefield] !== undefined && remListItems) { - const curList = curListItems?.fields?.[updatefield.replace('fields.', '')]?.fields.filter(f => f !== null) || []; + function remFromListField(socket: Socket, diff: Diff, curListItems: serializedDoctype | undefined, cb: (res: boolean) => void): void { + const $remFromSet = diff.diff.$remFromSet as serializedFieldsType; + const updatefield = Array.from(Object.keys($remFromSet ?? {}))[0]; + const remListItems = $remFromSet?.[updatefield]?.fields; + + if (remListItems) { + const hint = diff.diff.$remFromSet?.hint; + const length = diff.diff.$remFromSet?.length; + diff.diff.$set = $remFromSet; // convert rem from set to a query of the current fields, and then a set of the old fields minus the removed ones + delete diff.diff.$remFromSet; // can't pass $set to Mongo, or it will do that insetead of $remFromSet + const curList = curListItems?.fields?.[updatefield.replace('fields.', '')]?.fields.filter(f => f) ?? []; if (hint) { // indexesToRemove stores the indexes that we mark for deletion, which is later used to filter the list (delete the elements) @@ -227,83 +163,51 @@ export namespace WebSocket { if (closestIndex !== -1) { indexesToRemove.push(closestIndex); } else { - console.log('Item to delete was not found - index = -1'); + console.log('Item to delete was not found'); } } } diff.diff.$set[updatefield]!.fields = curList.filter((curItem, index) => !indexesToRemove.includes(index)); } else { - // go back to the original way to delete if we didn't receive - // a hint from the client + // if we didn't get a hint, remove all matching items from the list diff.diff.$set[updatefield]!.fields = curList?.filter(curItem => !remListItems.some(remItem => (remItem.fieldId ? remItem.fieldId === curItem.fieldId : remItem.heading ? remItem.heading === curItem.heading : remItem === curItem))); } - // if the client and server have different versions of the data after - // deletion, they will have different lengths and the server will - // send its version of the data to the client - const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length; - delete diff.diff.length; - Database.Instance.update( - diff.id, - diff.diff, - () => { - if (sendBack) { - // the two copies are different, so the server sends its copy. - console.log('SEND BACK'); - const { id } = socket; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (socket as any).id = ''; // bcz: HACK to access private variable this allows the update message to go back to the client that made the change. - socket.broadcast.emit(MessageStore.UpdateField.Message, diff); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (socket as any).id = id; - } else { - socket.broadcast.emit(MessageStore.UpdateField.Message, diff); - } - dispatchNextOp(diff.id); - }, - false - ); - } + + // if the client's list length is not the same as what we're writing to the server, + // then we need to send the server's version back to the client so that they are in synch. + // this could happen if another client made a change before the server receives the update from the first client + const target = length !== diff.diff.$set[updatefield].fields.length ? socket : socket.broadcast; + target === socket && console.log('Warning: SEND BACK: list modified during remove update. Composite list is being returned.'); + Database.Instance.update(diff.id, diff.diff, () => cb(target.emit(MessageStore.UpdateField.Message, diff)), false); + } else cb(false); } function UpdateField(socket: Socket, diff: Diff) { const curUser = socketMap.get(socket); - if (!curUser) return false; - const currentUsername = curUser.split(' ')[0]; - userOperations.set(currentUsername, userOperations.get(currentUsername) !== undefined ? userOperations.get(currentUsername)! + 1 : 0); + if (curUser) { + const currentUsername = curUser.split(' ')[0]; + userOperations.set(currentUsername, userOperations.get(currentUsername) !== undefined ? userOperations.get(currentUsername)! + 1 : 0); - if (CurUser !== socketMap.get(socket)) { - CurUser = socketMap.get(socket); - console.log('Switch User: ' + CurUser); - } - if (pendingOps.has(diff.id)) { - pendingOps.get(diff.id)!.push({ diff, socket }); - return true; - } - pendingOps.set(diff.id, [{ diff, socket }]); - if (diff.diff.$addToSet) { - return GetRefFieldLocal([diff.id, (result?: serializedDoctype) => addToListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own - } - if (diff.diff.$remFromSet) { - return GetRefFieldLocal([diff.id, (result?: serializedDoctype) => remFromListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own + if (CurUser !== socketMap.get(socket)) { + CurUser = socketMap.get(socket); + console.log('Switch User: ' + CurUser); + } + if (pendingOps.has(diff.id)) { + pendingOps.get(diff.id)!.push({ diff, socket }); + return true; + } + pendingOps.set(diff.id, [{ diff, socket }]); + return dispatchNextOp(diff.id); } - // eslint-disable-next-line no-use-before-define - return SetField(socket, diff); - } - function SetField(socket: Socket, diff: Diff /* , curListItems?: Transferable */) { - Database.Instance.update(diff.id, diff.diff, () => socket.broadcast.emit(MessageStore.UpdateField.Message, diff), false); - dispatchNextOp(diff.id); + return false; } function DeleteField(socket: Socket, id: string) { - Database.Instance.delete({ _id: id }).then(() => { - socket.broadcast.emit(MessageStore.DeleteField.Message, id); - }); + Database.Instance.delete({ _id: id }).then(() => socket.broadcast.emit(MessageStore.DeleteField.Message, id)); } function DeleteFields(socket: Socket, ids: string[]) { - Database.Instance.delete({ _id: { $in: ids } }).then(() => { - socket.broadcast.emit(MessageStore.DeleteFields.Message, ids); - }); + Database.Instance.delete({ _id: { $in: ids } }).then(() => socket.broadcast.emit(MessageStore.DeleteFields.Message, ids)); } function CreateDocField(newValue: serializedDoctype) { @@ -343,21 +247,19 @@ export namespace WebSocket { socket.in(room).emit('message', message); }); - socket.on('ipaddr', () => { + socket.on('ipaddr', () => networkInterfaces().keys?.forEach(dev => { if (dev.family === 'IPv4' && dev.address !== '127.0.0.1') { socket.emit('ipaddr', dev.address); } - }); - }); + }) + ); - socket.on('bye', () => { - console.log('received bye'); - }); + socket.on('bye', () => console.log('received bye')); socket.on('disconnect', () => { const currentUser = socketMap.get(socket); - if (!(currentUser === undefined)) { + if (currentUser !== undefined) { const currentUsername = currentUser.split(' ')[0]; DashStats.logUserLogout(currentUsername); delete timeMap[currentUsername]; -- cgit v1.2.3-70-g09d2 From fed4bf0b7363b9d58828a7a51f037a26f287d874 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 15 Aug 2024 15:45:44 -0400 Subject: fixed emptyPath type --- src/ServerUtils.ts | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 2 +- src/client/views/nodes/trails/PresBox.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/client/views/nodes/DocumentContentsView.tsx') diff --git a/src/ServerUtils.ts b/src/ServerUtils.ts index 904541fc7..8b2d0b9f6 100644 --- a/src/ServerUtils.ts +++ b/src/ServerUtils.ts @@ -16,7 +16,7 @@ export namespace ServerUtils { export function AddServerHandlerCallback(socket: Socket, message: Message, handler: (args: [T, (res: unknown) => void]) => void) { socket.on(message.Message, (arg: T, fn: (res: unknown) => void) => { Utils.log('S receiving', message.Name, arg, true); - handler([arg, Utils.loggingCallback('S sending', fn, message.Name)]); + handler([arg, Utils.loggingCallback('Sending', fn, message.Name)]); }); } export type RoomHandler = (socket: Socket, room: string) => void; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 15baebae1..afc160297 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -4,7 +4,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import * as XRegExp from 'xregexp'; import { OmitKeys } from '../../../ClientUtils'; -import { Without, emptyPath } from '../../../Utils'; +import { Without } from '../../../Utils'; import { Doc, Opt } from '../../../fields/Doc'; import { AclPrivate, DocData } from '../../../fields/DocSymbols'; import { ScriptField } from '../../../fields/ScriptField'; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index c403f9415..cf32a0196 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -20,7 +20,7 @@ import { ObjectField } from '../../../../fields/ObjectField'; import { listSpec } from '../../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../../fields/ScriptField'; import { BoolCast, Cast, DocCast, NumCast, StrCast, toList } from '../../../../fields/Types'; -import { emptyFunction, emptyPath, stringHash } from '../../../../Utils'; +import { emptyFunction, stringHash } from '../../../../Utils'; import { getSlideTransitionSuggestions, gptSlideProperties, gptTrailSlideCustomization } from '../../../apis/gpt/PresCustomization'; import { DocServer } from '../../../DocServer'; import { Docs } from '../../../documents/Documents'; -- cgit v1.2.3-70-g09d2