From 2a313f28fcb8675223708b0657de7517a3281095 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 17 Apr 2024 12:27:21 -0400 Subject: restoring eslint - updates not complete yet --- src/client/documents/DocumentTypes.ts | 6 +- src/client/documents/Documents.ts | 317 ++++++++++++++++++++++------------ 2 files changed, 210 insertions(+), 113 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 1123bcac9..4e3496608 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -25,14 +25,13 @@ export enum DocumentType { MAP = 'map', DATAVIZ = 'dataviz', LOADING = 'loading', - SIMULATION = 'simulation', //physics simulation + SIMULATION = 'simulation', // physics simulation // special purpose wrappers that either take no data or are compositions of lower level types LINK = 'link', IMPORT = 'import', PRES = 'presentation', PRESELEMENT = 'preselement', - YOUTUBE = 'youtube', COMPARISON = 'comparison', GROUP = 'group', PUSHPIN = 'pushpin', @@ -56,11 +55,10 @@ export enum CollectionViewType { Carousel = 'carousel', Carousel3D = '3D Carousel', Linear = 'linear', - //Staff = "staff", Map = 'map', Grid = 'grid', Pile = 'pileup', StackedTimeline = 'stacked timeline', NoteTaking = 'notetaking', - Calendar = 'calendar' + Calendar = 'calendar', } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b160379df..d41a96db2 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,28 +1,35 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { saveAs } from 'file-saver'; +import * as JSZip from 'jszip'; import { action, reaction, runInAction } from 'mobx'; import { basename } from 'path'; -import { OmitKeys, Utils } from '../../Utils'; +import { ClientUtils, OmitKeys } from '../../ClientUtils'; +import * as JSZipUtils from '../../JSZipUtils'; +import { decycle } from '../../decycler/decycler'; import { DateField } from '../../fields/DateField'; -import { Doc, DocListCast, Field, LinkedTo, Opt, StrListCast, updateCachedAcls } from '../../fields/Doc'; +import { Doc, DocListCast, Field, FieldType, LinkedTo, Opt, StrListCast, updateCachedAcls } from '../../fields/Doc'; import { DocData, Initializing } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { HtmlField } from '../../fields/HtmlField'; -import { InkField, PointData } from '../../fields/InkField'; -import { List } from '../../fields/List'; +import { InkDataFieldName, InkField } from '../../fields/InkField'; +import { List, ListFieldName } from '../../fields/List'; +import { ProxyField } from '../../fields/Proxy'; import { RichTextField } from '../../fields/RichTextField'; import { SchemaHeaderField } from '../../fields/SchemaHeaderField'; import { ComputedField, ScriptField } from '../../fields/ScriptField'; import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types'; -import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField, YoutubeField } from '../../fields/URLField'; +import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from '../../fields/URLField'; import { SharingPermissions, inheritParentAcls } from '../../fields/util'; +import { PointData } from '../../pen-gestures/GestureTypes'; import { Upload } from '../../server/SharedMediaTypes'; import { DocServer } from '../DocServer'; import { Networking } from '../Network'; -import { YoutubeBox } from '../apis/youtube/YoutubeBox'; -import { DragManager, dropActionType } from '../util/DragManager'; +import { DragManager } from '../util/DragManager'; +import { dropActionType } from '../util/DropActionTypes'; import { FollowLinkScript } from '../util/LinkFollower'; import { LinkManager } from '../util/LinkManager'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; +import { SerializationHelper } from '../util/SerializationHelper'; import { UndoManager, undoable } from '../util/UndoManager'; import { ContextMenu } from '../views/ContextMenu'; import { ContextMenuProps } from '../views/ContextMenuItem'; @@ -30,7 +37,7 @@ import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveIn import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { CollectionView } from '../views/collections/CollectionView'; import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView'; -import { AudioBox, media_state } from '../views/nodes/AudioBox'; +import { AudioBox, mediaState } from '../views/nodes/AudioBox'; import { ComparisonBox } from '../views/nodes/ComparisonBox'; import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox'; import { OpenWhere } from '../views/nodes/DocumentView'; @@ -60,6 +67,7 @@ import { PresBox } from '../views/nodes/trails/PresBox'; import { PresElementBox } from '../views/nodes/trails/PresElementBox'; import { SearchBox } from '../views/search/SearchBox'; import { CollectionViewType, DocumentType } from './DocumentTypes'; + const { DFLT_IMAGE_NATIVE_DIM } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', '')); @@ -83,7 +91,7 @@ export class FInfo { description: string = ''; readOnly: boolean = false; fieldType?: FInfoFieldType; - values?: Field[]; + values?: FieldType[]; filterable?: boolean = true; // can be used as a Filter in FilterPanel // format?: string; // format to display values (e.g, decimal places, $, etc) @@ -252,8 +260,8 @@ export class DocumentOptions { opacity?: NUMt = new NumInfo('document opacity', false); viewTransitionTime?: NUMt = new NumInfo('transition duration for view parameters', false); dontRegisterView?: BOOLt = new BoolInfo('are views of this document registered so that they can be found when following links, etc', false); - _undoIgnoreFields?: List; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)' - undoIgnoreFields?: List; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)' + _undoIgnoreFields?: List; // 'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)' + undoIgnoreFields?: List; // 'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)' _header_height?: NUMt = new NumInfo('height of document header used for displaying title', false); _header_fontSize?: NUMt = new NumInfo('font size of header of custom notes', false); _header_pointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume'); @@ -321,7 +329,7 @@ export class DocumentOptions { _label_maxFontSize?: NUMt = new NumInfo('maximum font size for labelBoxes', false); stroke_width?: NUMt = new NumInfo('width of an ink stroke', false); stroke_showLabel?: BOOLt = new BoolInfo('show label inside of stroke'); - mediaState?: STRt = new StrInfo(`status of audio/video media document: ${media_state.PendingRecording}, ${media_state.Recording}, ${media_state.Paused}, ${media_state.Playing}`, false); + mediaState?: STRt = new StrInfo(`status of audio/video media document: ${mediaState.PendingRecording}, ${mediaState.Recording}, ${mediaState.Paused}, ${mediaState.Playing}`, false); recording?: BOOLt = new BoolInfo('whether WebCam is recording or not'); slides?: DOCt = new DocInfo('presentation slide associated with video recording (bcz: should be renamed!!)'); autoPlayAnchors?: BOOLt = new BoolInfo('whether to play audio/video when an anchor is clicked in a stackedTimeline.'); @@ -401,7 +409,7 @@ export class DocumentOptions { _freeform_noZoom?: BOOLt = new BoolInfo('disables zooming (used by Pile docs)'); _freeform_fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content'); - //BUTTONS + // BUTTONS buttonText?: string; btnType?: string; btnList?: List; @@ -413,7 +421,7 @@ export class DocumentOptions { switchToggle?: boolean; badgeValue?: ScriptField; - //LINEAR VIEW + // LINEAR VIEW linearView_IsOpen?: BOOLt = new BoolInfo('is linear view open'); linearView_Expandable?: BOOLt = new BoolInfo('can linear view be expanded'); linearView_Dropdown?: BOOLt = new BoolInfo('can linear view be opened as a dropdown'); @@ -431,7 +439,7 @@ export class DocumentOptions { link_anchor_1_useSmallAnchor?: BOOLt = new BoolInfo('whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection)'); link_anchor_2_useSmallAnchor?: BOOLt = new BoolInfo('whether link_anchor_1 of a link should use a miniature anchor dot (as when the anchor is a text selection)'); link_relationshipList?: List; // for storing different link relationships (when set by user in the link editor) - link_relationshipSizes?: List; //stores number of links contained in each relationship + link_relationshipSizes?: List; // stores number of links contained in each relationship link_colorList?: List; // colors of links corresponding to specific link relationships followLinkZoom?: BOOLt = new BoolInfo('whether to zoom to the target of a link'); followLinkToggle?: BOOLt = new BoolInfo('whether target of link should be toggled on and off when following a link to it'); @@ -463,7 +471,7 @@ export class DocumentOptions { dragFactory_count?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', false, true); dragFactory?: DOCt = new DocInfo('document to create when dragging with a suitable onDragStart script', false); clickFactory?: DOCt = new DocInfo('document to create when clicking on a button with a suitable onClick script', false); - onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop + onDragStart?: ScriptField; // script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop target?: Doc; // available for use in scripts. used to provide a document parameter to the script (Note, this is a convenience entry since any field could be used for parameterizing a script) tags?: LISTt = new ListInfo('hashtags added to document, typically using a text view', true); treeView_HideTitle?: BOOLt = new BoolInfo('whether to hide the top document title of a tree view'); @@ -502,8 +510,6 @@ export class DocumentOptions { export const DocOptions = new DocumentOptions(); export namespace Docs { - export let newAccount: boolean = false; - export namespace Prototypes { type LayoutSource = { LayoutString: (key: string) => string }; type PrototypeTemplate = { @@ -646,12 +652,6 @@ export namespace Docs { options: { systemIcon: 'BsFileEarmarkCodeFill' }, }, ], - [ - DocumentType.YOUTUBE, - { - layout: { view: YoutubeBox, dataField: defaultDataKey }, - }, - ], [ DocumentType.LABEL, { @@ -670,7 +670,7 @@ export namespace Docs { layout_nativeDimEditable: true, layout_hideDecorationTitle: true, systemIcon: 'BsCalculatorFill', - }, ///systemIcon: 'BsSuperscript' + BsSubscript + }, // systemIcon: 'BsSuperscript' + BsSubscript }, ], [ @@ -850,7 +850,7 @@ export namespace Docs { // fetch the actual prototype documents from the server const actualProtos = await DocServer.GetRefFields(prototypeIds); // update this object to include any default values: DocumentOptions for all prototypes - prototypeIds.map(id => { + prototypeIds.forEach(id => { const existing = actualProtos[id] as Doc; const type = id.replace(suffix, '') as DocumentType; // get or create prototype of the specified type... @@ -908,7 +908,7 @@ export namespace Docs { if (!template) { return undefined; } - const layout = template.layout; + const { layout } = template; // create title const upper = suffix.toUpperCase(); const title = prototypeId.toUpperCase().replace(upper, `_${upper}`); @@ -928,7 +928,7 @@ export namespace Docs { }; Object.entries(options) .filter(pair => typeof pair[1] === 'string' && pair[1].startsWith('@')) - .map(pair => { + .forEach(pair => { if (!existing || ScriptCast(existing[pair[0]])?.script.originalScript !== pair[1].substring(1)) { (options as any)[pair[0]] = ComputedField.MakeFunction(pair[1].substring(1)); } @@ -960,7 +960,9 @@ export namespace Docs { * only when creating a DockDocument from the current user's already existing * main document. */ - function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDoc?: Doc, noView?: boolean) { + // eslint-disable-next-line default-param-last + function InstanceFromProto(proto: Doc, data: FieldType | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDocIn?: Doc, noView?: boolean) { + const placeholderDoc = placeholderDocIn; const viewKeys = ['x', 'y', 'isSystem']; // keys that should be addded to the view document even though they don't begin with an "_" const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_'); @@ -968,7 +970,7 @@ export namespace Docs { dataProps['acl-Guest'] = options['acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View); dataProps.isSystem = viewProps.isSystem; dataProps.isDataDoc = true; - dataProps.author = Doc.CurrentUserEmail; + dataProps.author = ClientUtils.CurrentUserEmail; dataProps.author_date = new DateField(); if (fieldKey) { dataProps[`${fieldKey}_modificationDate`] = new DateField(); @@ -988,7 +990,7 @@ export namespace Docs { } if (!noView) { - const viewFirstProps: { [id: string]: any } = { author: Doc.CurrentUserEmail }; + const viewFirstProps: { [id: string]: any } = { author: ClientUtils.CurrentUserEmail }; viewFirstProps['acl-Guest'] = options['_acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View); let viewDoc: Doc; // determines whether viewDoc should be created using placeholder Doc or default @@ -998,7 +1000,9 @@ export namespace Docs { viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true); Array.from(Object.keys(placeholderDoc)) .filter(key => key.startsWith('acl')) - .forEach(key => (dataDoc[key] = viewDoc[key] = placeholderDoc[key])); + .forEach(key => { + dataDoc[key] = viewDoc[key] = placeholderDoc[key]; + }); } else { viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true); } @@ -1017,6 +1021,7 @@ export namespace Docs { return dataDoc; } + // eslint-disable-next-line default-param-last export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}, overwriteDoc?: Doc) { const imgField = url instanceof ImageField ? url : url ? new ImageField(url) : undefined; return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField?.url.href ?? '-no image-'), ...options }, undefined, undefined, undefined, overwriteDoc); @@ -1035,18 +1040,16 @@ export namespace Docs { * @param fieldKey the field that the compiled script is written into. * @returns the Scripting Doc */ + // eslint-disable-next-line default-param-last export function ScriptingDocument(script: Opt | null, options: DocumentOptions = {}, fieldKey?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script : undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined }); + return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script || undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined }); } + // eslint-disable-next-line default-param-last export function VideoDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) { return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(url), options, undefined, undefined, undefined, overwriteDoc); } - export function YoutubeDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) { - return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(url), options, undefined, undefined, undefined, overwriteDoc); - } - export function WebCamDocument(url: string, options: DocumentOptions = {}) { return InstanceFromProto(Prototypes.get(DocumentType.WEBCAM), '', options); } @@ -1059,6 +1062,7 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), undefined, options); } + // eslint-disable-next-line default-param-last export function AudioDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) { return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url), options, undefined, undefined, undefined, overwriteDoc); } @@ -1072,7 +1076,7 @@ export namespace Docs { } export function LoadingDocument(file: File | string, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.LOADING), undefined, { _height: 150, _width: 200, title: typeof file == 'string' ? file : file.name, ...options }, undefined, ''); + return InstanceFromProto(Prototypes.get(DocumentType.LOADING), undefined, { _height: 150, _width: 200, title: typeof file === 'string' ? file : file.name, ...options }, undefined, ''); } export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') { @@ -1101,6 +1105,7 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey); } + // eslint-disable-next-line default-param-last export function LinkDocument(source: Doc, target: Doc, options: DocumentOptions = {}, id?: string) { const linkDoc = InstanceFromProto( Prototypes.get(DocumentType.LINK), @@ -1124,7 +1129,7 @@ export namespace Docs { options: DocumentOptions = {}, strokeWidth = ActiveInkWidth(), color = ActiveInkColor(), - stroke_bezier = ActiveInkBezierApprox(), + strokeBezier = ActiveInkBezierApprox(), fillColor = ActiveFillColor(), arrowStart = ActiveArrowStart(), arrowEnd = ActiveArrowEnd(), @@ -1138,7 +1143,7 @@ export namespace Docs { I.fillColor = fillColor; I.stroke = new InkField(points); I.stroke_width = strokeWidth; - I.stroke_bezier = stroke_bezier; + I.stroke_bezier = strokeBezier; I.stroke_startMarker = arrowStart; I.stroke_endMarker = arrowEnd; I.stroke_dash = dash; @@ -1148,12 +1153,13 @@ export namespace Docs { I.defaultDoubleClick = 'ignore'; I.author_date = new DateField(); I['acl-Guest'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View; - //I['acl-Override'] = SharingPermissions.Unset; + // I['acl-Override'] = SharingPermissions.Unset; I[Initializing] = false; return ink; } + // eslint-disable-next-line default-param-last export function PdfDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) { const width = options._width || undefined; const height = options._height || undefined; @@ -1169,7 +1175,7 @@ export namespace Docs { const nwid = options._nativeWidth || undefined; const nhght = options._nativeHeight || undefined; if (!nhght && width && height && nwid) options._nativeHeight = (Number(nwid) * Number(height)) / Number(width); - return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(url ? url : 'https://www.wikipedia.org/'), options); + return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(url || 'https://www.wikipedia.org/'), options); } export function HtmlDocument(html: string, options: DocumentOptions = {}) { @@ -1324,10 +1330,10 @@ export namespace Docs { const doc = DockDocument( configs.map(c => c.doc), JSON.stringify(layoutConfig), - Doc.CurrentUserEmail === 'guest' ? options : { 'acl-Guest': SharingPermissions.View, ...options }, + ClientUtils.CurrentUserEmail === 'guest' ? options : { 'acl-Guest': SharingPermissions.View, ...options }, id ); - configs.map(c => { + configs.forEach(c => { Doc.SetContainer(c.doc, doc); inheritParentAcls(doc, c.doc, false); }); @@ -1345,8 +1351,9 @@ export namespace Docs { } export namespace DocUtils { - function matchFieldValue(doc: Doc, key: string, value: any): boolean { - const hasFunctionFilter = Utils.HasFunctionFilter(value); + function matchFieldValue(doc: Doc, key: string, valueIn: any): boolean { + let value = valueIn; + const hasFunctionFilter = ClientUtils.HasFunctionFilter(value); if (hasFunctionFilter) { return hasFunctionFilter(StrCast(doc[key])); } @@ -1365,7 +1372,7 @@ export namespace DocUtils { matchLink(value,DocCast(link.link_anchor_2)) )); } if (typeof value === 'string') { - value = value.replace(`,${Utils.noRecursionHack}`, ''); + value = value.replace(`,${ClientUtils.noRecursionHack}`, ''); } const fieldVal = doc[key]; // prettier-ignore @@ -1377,7 +1384,7 @@ export namespace DocUtils { if (vals.length) { return vals.some(v => typeof v === 'string' && v.includes(value)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring } - return Field.toString(fieldVal as Field).includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring + return Field.toString(fieldVal as FieldType).includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring } /** * @param docs @@ -1410,7 +1417,9 @@ export namespace DocUtils { if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) { return false; } - for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== Utils.noDragDocsFilter.split(Doc.FilterSep)[0])) { + const facetKeys = Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== ClientUtils.noDragDocsFilter.split(Doc.FilterSep)[0]); + // eslint-disable-next-line no-restricted-syntax + for (const facetKey of facetKeys) { const facet = filterFacets[facetKey]; // facets that match some value in the field of the document (e.g. some text field) @@ -1431,8 +1440,8 @@ export namespace DocUtils { if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true; const failsNotEqualFacets = !xs.length ? false : xs.some(value => matchFieldValue(d, facetKey, value)); const satisfiesCheckFacets = !checks.length ? true : checks.some(value => matchFieldValue(d, facetKey, value)); - const satisfiesExistsFacets = !exists.length ? true : exists.some(value => (facetKey !== LinkedTo ? d[facetKey] !== undefined : LinkManager.Instance.getAllRelatedLinks(d).length)); - const satisfiesUnsetsFacets = !unsets.length ? true : unsets.some(value => d[facetKey] === undefined); + const satisfiesExistsFacets = !exists.length ? true : facetKey !== LinkedTo ? d[facetKey] !== undefined : LinkManager.Instance.getAllRelatedLinks(d).length; + const satisfiesUnsetsFacets = !unsets.length ? true : d[facetKey] === undefined; const satisfiesMatchFacets = !matches.length ? true : matches.some(value => { @@ -1441,20 +1450,18 @@ export namespace DocUtils { const allKeys = Array.from(Object.keys(d)); allKeys.push(...Object.keys(Doc.GetProto(d))); const keys = allKeys.filter(key => key.includes(facetKey.substring(1))); - return keys.some(key => Field.toString(d[key] as Field).includes(value)); + return keys.some(key => Field.toString(d[key] as FieldType).includes(value)); } - return Field.toString(d[facetKey] as Field).includes(value); + return Field.toString(d[facetKey] as FieldType).includes(value); }); // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria if (parentCollection?.childFilters_boolean === 'OR') { if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true; } // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria - else { - if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; - } + else if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; } - return parentCollection?.childFilters_boolean === 'OR' ? false : true; + return parentCollection?.childFilters_boolean !== 'OR'; }) : childDocs; const rangeFilteredDocs = filteredDocs.filter(d => { @@ -1464,7 +1471,7 @@ export namespace DocUtils { const max = Number(childFiltersByRanges[i + 2]); const val = typeof d[key] === 'string' ? (Number(StrCast(d[key])).toString() === StrCast(d[key]) ? Number(StrCast(d[key])) : undefined) : Cast(d[key], 'number', null); if (val === undefined) { - //console.log("Should 'undefined' pass range filter or not?") + // console.log("Should 'undefined' pass range filter or not?") } else if (val < min || val > max) return false; } return true; @@ -1472,10 +1479,10 @@ export namespace DocUtils { return rangeFilteredDocs; } - export let ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; + export const ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) { - broadcastEvent && runInAction(() => (Doc.RecordingEvent = Doc.RecordingEvent + 1)); + broadcastEvent && runInAction(() => { Doc.RecordingEvent += 1; }); // prettier-ignore return DocUtils.ActiveRecordings.map(audio => { const sourceDoc = getSourceDoc(); return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { link_displayLine: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' }); @@ -1510,7 +1517,9 @@ export namespace DocUtils { } setTimeout( - action(() => (TaskCompletionBox.taskCompleted = false)), + action(() => { + TaskCompletionBox.taskCompleted = false; + }), 2500 ); } @@ -1550,7 +1559,7 @@ export namespace DocUtils { export function AssignScripts(doc: Doc, scripts?: { [key: string]: string | undefined }, funcs?: { [key: string]: string }) { scripts && - Object.keys(scripts).map(key => { + Object.keys(scripts).forEach(key => { const script = scripts[key]; if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && script) { (key.startsWith('_') ? doc : Doc.GetProto(doc))[key] = ScriptField.MakeScript(script, { @@ -1573,7 +1582,7 @@ export namespace DocUtils { funcs && Object.keys(funcs) .filter(key => !key.endsWith('-setter')) - .map(key => { + .forEach(key => { const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); if (ScriptCast(cfield)?.script.originalScript !== funcs[key]) { const setFunc = Cast(funcs[key + '-setter'], 'string', null); @@ -1602,6 +1611,7 @@ export namespace DocUtils { return doc; } export function AssignDocField(doc: Doc, field: string, creator: (reqdOpts: DocumentOptions, items?: Doc[]) => Doc, reqdOpts: DocumentOptions, items?: Doc[], scripts?: { [key: string]: string }, funcs?: { [key: string]: string }) { + // eslint-disable-next-line no-return-assign return DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts, items) ?? (doc[field] = creator(reqdOpts, items)), scripts, funcs); } @@ -1649,7 +1659,7 @@ export namespace DocUtils { * @returns */ export async function DocumentFromType(type: string, path: string, options: DocumentOptions, overwriteDoc?: Doc): Promise> { - let ctor: ((path: string, options: DocumentOptions, overwriteDoc?: Doc) => Doc | Promise) | undefined = undefined; + let ctor: ((path: string, options: DocumentOptions, overwriteDoc?: Doc) => Doc | Promise) | undefined; if (type.indexOf('image') !== -1) { ctor = Docs.Create.ImageDocument; if (!options._width) options._width = 300; @@ -1672,7 +1682,7 @@ export namespace DocUtils { if (!options._width) options._width = 400; if (!options._height) options._height = ((options._width as number) * 1200) / 927; } - //TODO:al+glr + // TODO:al+glr // if (type.indexOf("map") !== -1) { // ctor = Docs.Create.MapDocument; // if (!options._width) options._width = 800; @@ -1695,6 +1705,7 @@ export namespace DocUtils { }); } ctor = Docs.Create.WebDocument; + // eslint-disable-next-line no-param-reassign options = { ...options, _width: 400, _height: 512, title: path }; } @@ -1706,12 +1717,12 @@ export namespace DocUtils { .filter(btnDoc => !btnDoc.hidden) .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc.title) - .map((dragDoc, i) => ({ + .map(dragDoc => ({ description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), - event: undoable((args: { x: number; y: number }) => { + event: undoable(() => { const newDoc = DocUtils.copyDragFactory(dragDoc); if (newDoc) { - newDoc.author = Doc.CurrentUserEmail; + newDoc.author = ClientUtils.CurrentUserEmail; newDoc.x = x; newDoc.y = y; EquationBox.SelectOnLoad = newDoc[Id]; @@ -1732,9 +1743,9 @@ export namespace DocUtils { !simpleMenu && ContextMenu.Instance.addItem({ description: 'Styled Notes', - subitems: DocListCast((Doc.UserDoc().template_notes as Doc).data).map((note, i) => ({ + subitems: DocListCast((Doc.UserDoc().template_notes as Doc).data).map(note => ({ description: ':' + StrCast(note.title), - event: undoable((args: { x: number; y: number }) => { + event: undoable(() => { const textDoc = Docs.Create.TextDocument('', { _width: 200, x, @@ -1757,12 +1768,12 @@ export namespace DocUtils { .filter(btnDoc => !btnDoc.hidden) .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyNote && doc.title) - .map((dragDoc, i) => ({ + .map(dragDoc => ({ description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), - event: undoable((args: { x: number; y: number }) => { + event: undoable(() => { const newDoc = DocUtils.delegateDragFactory(dragDoc); if (newDoc) { - newDoc.author = Doc.CurrentUserEmail; + newDoc.author = ClientUtils.CurrentUserEmail; newDoc.x = x; newDoc.y = y; EquationBox.SelectOnLoad = newDoc[Id]; @@ -1802,11 +1813,11 @@ export namespace DocUtils { } export function findTemplate(templateName: string, type: string, signature: string) { let docLayoutTemplate: Opt; - const iconViews = DocListCast(Cast(Doc.UserDoc()['template_icons'], Doc, null)?.data); - const templBtns = DocListCast(Cast(Doc.UserDoc()['template_buttons'], Doc, null)?.data); - const noteTypes = DocListCast(Cast(Doc.UserDoc()['template_notes'], Doc, null)?.data); - const userTypes = DocListCast(Cast(Doc.UserDoc()['template_user'], Doc, null)?.data); - const clickFuncs = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data); + const iconViews = DocListCast(Cast(Doc.UserDoc().template_icons, Doc, null)?.data); + const templBtns = DocListCast(Cast(Doc.UserDoc().template_buttons, Doc, null)?.data); + const noteTypes = DocListCast(Cast(Doc.UserDoc().template_notes, Doc, null)?.data); + const userTypes = DocListCast(Cast(Doc.UserDoc().template_user, Doc, null)?.data); + const clickFuncs = DocListCast(Cast(Doc.UserDoc().template_clickFuncs, Doc, null)?.data); const allTemplates = iconViews .concat(templBtns) .concat(noteTypes) @@ -1816,13 +1827,20 @@ export namespace DocUtils { .filter(doc => doc.isTemplateDoc); // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document. but type is not reliable and there could be other types of template searches so this should be generalized // first try to find a template that matches the specific document type (_). otherwise, fallback to a general match on - !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + '_' + type && (docLayoutTemplate = tempDoc)); - !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc)); + !docLayoutTemplate && + allTemplates.forEach(tempDoc => { + StrCast(tempDoc.title) === templateName + '_' + type && (docLayoutTemplate = tempDoc); + }); + !docLayoutTemplate && + allTemplates.forEach(tempDoc => { + StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc); + }); return docLayoutTemplate; } export function createCustomView(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) { const templateName = templateSignature.replace(/\(.*\)/, ''); - doc.layout_fieldKey = 'layout_' + (templateSignature || docLayoutTemplate?.title); + doc.layout_fieldKey = 'layout_' + (templateSignature || (docLayoutTemplate?.title ?? '')); + // eslint-disable-next-line no-param-reassign docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc.isGroup && doc.transcription ? 'transcription' : doc.type), templateSignature); const customName = 'layout_' + templateSignature; @@ -1859,14 +1877,15 @@ export namespace DocUtils { } } export function iconify(doc: Doc) { - const layout_fieldKey = Cast(doc.layout_fieldKey, 'string', null); + const layoutFieldKey = Cast(doc.layout_fieldKey, 'string', null); DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, 'icon', undefined); - if (layout_fieldKey && layout_fieldKey !== 'layout' && layout_fieldKey !== 'layout_icon') doc.deiconifyLayout = layout_fieldKey.replace('layout_', ''); + if (layoutFieldKey && layoutFieldKey !== 'layout' && layoutFieldKey !== 'layout_icon') doc.deiconifyLayout = layoutFieldKey.replace('layout_', ''); } export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) { runInAction(() => { - docList.forEach((d, i) => { + docList.forEach((doc, i) => { + const d = doc; DocUtils.iconify(d); d.x = Math.cos((Math.PI * 2 * i) / docList.length) * size - size; d.y = Math.sin((Math.PI * 2 * i) / docList.length) * size - size; @@ -1880,6 +1899,7 @@ export namespace DocUtils { newCollection._width = newCollection._height = size * 2; return newCollection; } + return undefined; } export function makeIntoPortal(doc: Doc, layoutDoc: Doc, allLinks: Doc[]) { const portalLink = allLinks.find(d => d.link_anchor_1 === doc && d.link_relationship === 'portal to:portal from'); @@ -1921,7 +1941,7 @@ export namespace DocUtils { _timecodeToShow: Cast(doc._timecodeToShow, 'number', null), }); Doc.AddDocToList(context, annotationField, pushpin); - const pushpinLink = DocUtils.MakeLink(pushpin, doc, { link_relationship: 'pushpin' }, ''); + DocUtils.MakeLink(pushpin, doc, { link_relationship: 'pushpin' }, ''); doc._timecodeToShow = undefined; return pushpin; } @@ -1948,23 +1968,24 @@ export namespace DocUtils { // } function ConvertDMSToDD(degrees: number, minutes: number, seconds: number, direction: string) { - var dd = degrees + minutes / 60 + seconds / (60 * 60); + let dd = degrees + minutes / 60 + seconds / (60 * 60); if (direction === 'S' || direction === 'W') { - dd = dd * -1; + dd *= -1; } // Don't do anything for N or E return dd; } - export function assignImageInfo(result: Upload.FileInformation, proto: Doc) { + export function assignImageInfo(result: Upload.FileInformation, protoIn: Doc) { + const proto = protoIn; if (Upload.isImageInformation(result)) { const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim); const exifRotation = StrCast((result.exifData?.data as any)?.Orientation).toLowerCase(); - proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined); - proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; - proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); - if (NumCast(proto['data-nativeOrientation']) >= 5) { - proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; - proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); + proto.data_nativeOrientation = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined); + proto.data_nativeWidth = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; + proto.data_nativeHeight = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); + if (NumCast(proto.data_nativeOrientation) >= 5) { + proto.data_nativeHeight = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; + proto.data_nativeWidth = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); } proto.data_exif = JSON.stringify(result.exifData?.data); proto.data_contentSize = result.contentSize; @@ -2033,7 +2054,7 @@ export namespace DocUtils { const generatedDocuments: Doc[] = []; Networking.UploadYoutubeToServer(videoId, overwriteDoc?.[Id]).then(upfiles => { const { - source: { newFilename, originalFilename, mimetype }, + source: { newFilename, mimetype }, result, } = upfiles.lastElement(); if ((result as any).message) { @@ -2061,12 +2082,9 @@ export namespace DocUtils { const fileNoGuidPairs: Networking.FileGuidPair[] = files.map(file => ({ file })); const upfiles = await Networking.UploadFilesToServer(fileNoGuidPairs); - for (const { - source: { newFilename, mimetype }, - result, - } of upfiles) { + upfiles.forEach(({ source: { newFilename, mimetype }, result }) => { newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options); - } + }); return generatedDocuments; } @@ -2091,34 +2109,115 @@ export namespace DocUtils { export function copyDragFactory(dragFactory: Doc) { if (!dragFactory) return undefined; const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); - if (ndoc && dragFactory['dragFactory_count'] !== undefined) { - dragFactory['dragFactory_count'] = NumCast(dragFactory['dragFactory_count']) + 1; - Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString(), true); + if (ndoc && dragFactory.dragFactory_count !== undefined) { + dragFactory.dragFactory_count = NumCast(dragFactory.dragFactory_count) + 1; + Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory.dragFactory_count).toString(), true); } return ndoc; } export function delegateDragFactory(dragFactory: Doc) { const ndoc = Doc.MakeDelegateWithProto(dragFactory); - if (ndoc && dragFactory['dragFactory_count'] !== undefined) { - dragFactory['dragFactory_count'] = NumCast(dragFactory['dragFactory_count']) + 1; - Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString(); + if (ndoc && dragFactory.dragFactory_count !== undefined) { + dragFactory.dragFactory_count = NumCast(dragFactory.dragFactory_count) + 1; + Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory.dragFactory_count).toString(); } return ndoc; } + + export async function Zip(doc: Doc, zipFilename = 'dashExport.zip') { + const { clone, map, linkMap } = await Doc.MakeClone(doc); + const proms = new Set(); + function replacer(key: any, value: any) { + if (key && ['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined; + if (value?.__type === 'image') { + const extension = value.url.replace(/.*\./, ''); + proms.add(value.url.replace('.' + extension, '_o.' + extension)); + return SerializationHelper.Serialize(new ImageField(value.url)); + } + if (value?.__type === 'pdf') { + proms.add(value.url); + return SerializationHelper.Serialize(new PdfField(value.url)); + } + if (value?.__type === 'audio') { + proms.add(value.url); + return SerializationHelper.Serialize(new AudioField(value.url)); + } + if (value?.__type === 'video') { + proms.add(value.url); + return SerializationHelper.Serialize(new VideoField(value.url)); + } + if ( + value instanceof Doc || + value instanceof ScriptField || + value instanceof RichTextField || + value instanceof InkField || + value instanceof CsvField || + value instanceof WebField || + value instanceof DateField || + value instanceof ProxyField || + value instanceof ComputedField + ) { + return SerializationHelper.Serialize(value); + } + if (value instanceof Array && key !== ListFieldName && key !== InkDataFieldName) return { fields: value, __type: 'list' }; + return value; + } + + const docs: { [id: string]: any } = {}; + const links: { [id: string]: any } = {}; + Array.from(map.entries()).forEach(f => (docs[f[0]] = f[1])); + Array.from(linkMap.entries()).forEach(l => (links[l[0]] = l[1])); + const jsonDocs = JSON.stringify({ id: clone[Id], docs, links }, decycle(replacer)); + + const zip = new JSZip(); + var count = 0; + const promArr = Array.from(proms) + .filter(url => url?.startsWith('/files')) + .map(url => url.replace('/', '')); // window.location.origin)); + console.log(promArr.length); + if (!promArr.length) { + zip.file('docs.json', jsonDocs); + zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename)); + } else + promArr.forEach((url, i) => { + // loading a file and add it in a zip file + JSZipUtils.getBinaryContent(window.location.origin + '/' + url, (err: any, data: any) => { + if (err) throw err; // or handle the error + // // Generate a directory within the Zip file structure + // const assets = zip.folder("assets"); + // assets.file(filename, data, {binary: true}); + const assetPathOnServer = promArr[i].replace(window.location.origin + '/', '').replace(/\//g, '%%%'); + zip.file(assetPathOnServer, data, { binary: true }); + console.log(' => ' + url); + if (++count === promArr.length) { + zip.file('docs.json', jsonDocs); + zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename)); + // const a = document.createElement("a"); + // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`); + // a.href = url; + // a.download = `DocExport-${this.props.Document[Id]}.zip`; + // a.click(); + } + }); + }); + } } ScriptingGlobals.add('Docs', Docs); +// eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc, asDelegate?: boolean) { return dragFactory instanceof Doc ? (asDelegate ? DocUtils.delegateDragFactory(dragFactory) : DocUtils.copyDragFactory(dragFactory)) : dragFactory; }); +// eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: 'child of ' + proto.title }); return d; }); +// eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function generateLinkTitle(link: Doc) { - const link_anchor_1title = link.link_anchor_1 && link.link_anchor_1 !== link ? Cast(link.link_anchor_1, Doc, null)?.title : ''; - const link_anchor_2title = link.link_anchor_2 && link.link_anchor_2 !== link ? Cast(link.link_anchor_2, Doc, null)?.title : ''; + const linkAnchor1title = link.link_anchor_1 && link.link_anchor_1 !== link ? Cast(link.link_anchor_1, Doc, null)?.title : ''; + const linkAnchor2title = link.link_anchor_2 && link.link_anchor_2 !== link ? Cast(link.link_anchor_2, Doc, null)?.title : ''; const relation = link.link_relationship || 'to'; - return `${link_anchor_1title} (${relation}) ${link_anchor_2title}`; + return `${linkAnchor1title} (${relation}) ${linkAnchor2title}`; }); -- cgit v1.2.3-70-g09d2 From 89e5b4e224d77c7a029ec7d9c9027095665508ac Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 19 Apr 2024 11:32:46 -0400 Subject: lint fixes. --- .gitignore | 1 - src/.DS_Store | Bin 10244 -> 10244 bytes src/ClientUtils.ts | 649 +++++++++++++++++++++ src/client/DocServer.ts | 10 +- src/client/documents/Documents.ts | 10 +- src/client/util/CurrentUserUtils.ts | 14 +- src/client/util/DocumentManager.ts | 2 +- src/client/util/GroupManager.tsx | 6 +- src/client/util/HypothesisUtils.ts | 2 +- src/client/util/SettingsManager.tsx | 4 +- src/client/util/SharingManager.tsx | 10 +- src/client/util/reportManager/ReportManager.tsx | 2 +- src/client/views/DashboardView.tsx | 6 +- src/client/views/MainView.tsx | 2 +- src/client/views/PropertiesView.tsx | 4 +- src/client/views/SidebarAnnos.tsx | 2 +- src/client/views/StyleProvider.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/collections/TreeView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 18 +- .../formattedText/FormattedTextBoxComment.tsx | 2 +- .../formattedText/ProsemirrorExampleTransfer.ts | 4 +- .../views/nodes/formattedText/RichTextRules.ts | 4 +- src/client/views/nodes/formattedText/marks_rts.ts | 2 +- src/fields/Doc.ts | 75 ++- src/fields/util.ts | 10 +- 27 files changed, 744 insertions(+), 103 deletions(-) create mode 100644 src/ClientUtils.ts (limited to 'src/client/documents') diff --git a/.gitignore b/.gitignore index f841c1a86..6a1963b4e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ node_modules dist/ .DS_Store .env -ClientUtils.ts solr-8.3.1/server/logs/ solr-8.3.1/server/solr/dash/data/tlog/* solr-8.3.1/server/solr/dash/data/index/* diff --git a/src/.DS_Store b/src/.DS_Store index f8d745dbf..75cff7b55 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts new file mode 100644 index 000000000..57e0373c4 --- /dev/null +++ b/src/ClientUtils.ts @@ -0,0 +1,649 @@ +import * as Color from 'color'; +import * as React from 'react'; +import { ColorResult } from 'react-color'; +import * as rp from 'request-promise'; +import { numberRange, decimalToHexString } from './Utils'; +import { DocumentType } from './client/documents/DocumentTypes'; +import { Colors } from './client/views/global/globalEnums'; + +export function DashColor(color: string) { + try { + return color ? Color(color.toLowerCase()) : Color('transparent'); + } catch (e) { + if (color.includes('gradient')) console.log("using color 'white' in place of :" + color); + else console.log('COLOR error:', e); + return Color('white'); + } +} + +export function lightOrDark(color: any) { + if (color === 'transparent' || !color) return Colors.BLACK; + if (color.startsWith?.('linear')) return Colors.BLACK; + if (DashColor(color).isLight()) return Colors.BLACK; + return Colors.WHITE; +} + +export function returnTransparent() { + return 'transparent'; +} + +export function returnTrue() { + return true; +} + +export function returnIgnore(): 'ignore' { + return 'ignore'; +} +export function returnAlways(): 'always' { + return 'always'; +} +export function returnNever(): 'never' { + return 'never'; +} + +export function returnDefault(): 'default' { + return 'default'; +} + +export function return18() { + return 18; +} + +export function returnFalse() { + return false; +} + +export function returnAll(): 'all' { + return 'all'; +} + +export function returnNone(): 'none' { + return 'none'; +} + +export function returnVal(val1?: number, val2?: number) { + return val1 || (val2 !== undefined ? val2 : 0); +} + +export function returnOne() { + return 1; +} + +export function returnZero() { + return 0; +} + +export function returnEmptyString() { + return ''; +} + +export function returnEmptyFilter() { + return [] as string[]; +} + +export function returnEmptyDoclist() { + return [] as any[]; +} + +export namespace ClientUtils { + export const CLICK_TIME = 300; + export const DRAG_THRESHOLD = 4; + export const SNAP_THRESHOLD = 10; + let _currentUserEmail: string = ''; + export function CurrentUserEmail() { + return _currentUserEmail; + } + export function SetCurrentUserEmail(email: string) { + _currentUserEmail = email; + } + export function isClick(x: number, y: number, downX: number, downY: number, downTime: number) { + return Date.now() - downTime < ClientUtils.CLICK_TIME && Math.abs(x - downX) < ClientUtils.DRAG_THRESHOLD && Math.abs(y - downY) < ClientUtils.DRAG_THRESHOLD; + } + + export function cleanDocumentType(type: DocumentType) { + switch (type) { + case DocumentType.IMG: return 'Image'; + case DocumentType.AUDIO: return 'Audio'; + case DocumentType.COL: return 'Collection'; + case DocumentType.RTF: return 'Text'; + default: return type.charAt(0).toUpperCase() + type.slice(1); + } // prettier-ignore + } + + export function readUploadedFileAsText(inputFile: File) { + // eslint-disable-next-line no-undef + const temporaryFileReader = new FileReader(); + + return new Promise((resolve, reject) => { + temporaryFileReader.onerror = () => { + temporaryFileReader.abort(); + reject(new DOMException('Problem parsing input file.')); + }; + + temporaryFileReader.onload = () => { + resolve(temporaryFileReader.result); + }; + temporaryFileReader.readAsText(inputFile); + }); + } + + /** + * Uploads an image buffer to the server and stores with specified filename. by default the image + * is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large) + * @param imageUri the bytes of the image + * @param returnedFilename the base filename to store the image on the server + * @param nosuffix optionally suppress creating multiple resolution images + */ + export async function convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false, replaceRootFilename: string | undefined = undefined) { + try { + const posting = ClientUtils.prepend('/uploadURI'); + const returnedUri = await rp.post(posting, { + body: { + uri: imageUri, + name: returnedFilename, + nosuffix, + replaceRootFilename, + }, + json: true, + }); + return returnedUri; + } catch (e) { + console.log('ConvertDataURI :' + e); + } + return undefined; + } + + export function GetScreenTransform(ele?: HTMLElement | null): { scale: number; translateX: number; translateY: number } { + if (!ele) { + return { scale: 1, translateX: 1, translateY: 1 }; + } + const rect = ele.getBoundingClientRect(); + const scale = ele.offsetWidth === 0 && rect.width === 0 ? 1 : rect.width / ele.offsetWidth; + const translateX = rect.left; + const translateY = rect.top; + + return { scale, translateX, translateY }; + } + + /** + * A convenience method. Prepends the full path (i.e. http://localhost:) to the + * requested extension + * @param extension the specified sub-path to append to the window origin + */ + export function prepend(extension: string): string { + return window.location.origin + extension; + } + export function fileUrl(filename: string): string { + return prepend(`/files/${filename}`); + } + + export function shareUrl(documentId: string): string { + return prepend(`/doc/${documentId}?sharing=true`); + } + + export function CorsProxy(url: string): string { + return prepend('/corsProxy/') + encodeURIComponent(url); + } + + export function CopyText(text: string) { + navigator.clipboard.writeText(text); + } + + export function colorString(color: ColorResult) { + return color.hex.startsWith('#') && color.hex.length < 8 ? color.hex + (color.rgb.a ? decimalToHexString(Math.round(color.rgb.a * 255)) : 'ff') : color.hex; + } + + export function fromRGBAstr(rgba: string) { + const rm = rgba.match(/rgb[a]?\(([ 0-9]+)/); + const r = rm ? Number(rm[1]) : 0; + const gm = rgba.match(/rgb[a]?\([ 0-9]+,([ 0-9]+)/); + const g = gm ? Number(gm[1]) : 0; + const bm = rgba.match(/rgb[a]?\([ 0-9]+,[ 0-9]+,([ 0-9]+)/); + const b = bm ? Number(bm[1]) : 0; + const am = rgba.match(/rgba?\([ 0-9]+,[ 0-9]+,[ 0-9]+,([ .0-9]+)/); + const a = am ? Number(am[1]) : 1; + return { r: r, g: g, b: b, a: a }; + } + + const isTransparentFunctionHack = 'isTransparent(__value__)'; + export const noRecursionHack = '__noRecursion'; + + // special case filters + export const noDragDocsFilter = 'noDragDocs::any::check'; + export const TransparentBackgroundFilter = `backgroundColor::${isTransparentFunctionHack},${noRecursionHack}::check`; // bcz: hack. noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field + export const OpaqueBackgroundFilter = `backgroundColor::${isTransparentFunctionHack},${noRecursionHack}::x`; // bcz: hack. noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field + + export function IsRecursiveFilter(val: string) { + return !val.includes(noRecursionHack); + } + export function HasFunctionFilter(val: string) { + if (val.includes(isTransparentFunctionHack)) return (color: string) => color !== '' && DashColor(color).alpha() !== 1; + // add other function filters here... + return undefined; + } + + export function toRGBAstr(col: { r: number; g: number; b: number; a?: number }) { + return 'rgba(' + col.r + ',' + col.g + ',' + col.b + (col.a !== undefined ? ',' + col.a : '') + ')'; + } + + export function HSLtoRGB(h: number, s: number, l: number) { + // Must be fractions of 1 + // s /= 100; + // l /= 100; + + const c = (1 - Math.abs(2 * l - 1)) * s; + const x = c * (1 - Math.abs(((h / 60) % 2) - 1)); + const m = l - c / 2; + let r = 0; + let g = 0; + let b = 0; + if (h >= 0 && h < 60) { + r = c; + g = x; + b = 0; + } else if (h >= 60 && h < 120) { + r = x; + g = c; + b = 0; + } else if (h >= 120 && h < 180) { + r = 0; + g = c; + b = x; + } else if (h >= 180 && h < 240) { + r = 0; + g = x; + b = c; + } else if (h >= 240 && h < 300) { + r = x; + g = 0; + b = c; + } else if (h >= 300 && h < 360) { + r = c; + g = 0; + b = x; + } + r = Math.round((r + m) * 255); + g = Math.round((g + m) * 255); + b = Math.round((b + m) * 255); + return { r: r, g: g, b: b }; + } + + export function RGBToHSL(red: number, green: number, blue: number) { + // Make r, g, and b fractions of 1 + const r = red / 255; + const g = green / 255; + const b = blue / 255; + + // Find greatest and smallest channel values + const cmin = Math.min(r, g, b); + const cmax = Math.max(r, g, b); + const delta = cmax - cmin; + let h = 0; + let s = 0; + let l = 0; + // Calculate hue + + // No difference + if (delta === 0) h = 0; + // Red is max + else if (cmax === r) h = ((g - b) / delta) % 6; + // Green is max + else if (cmax === g) h = (b - r) / delta + 2; + // Blue is max + else h = (r - g) / delta + 4; + + h = Math.round(h * 60); + + // Make negative hues positive behind 360° + if (h < 0) h += 360; // Calculate lightness + + l = (cmax + cmin) / 2; + + // Calculate saturation + s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); + + // Multiply l and s by 100 + // s = +(s * 100).toFixed(1); + // l = +(l * 100).toFixed(1); + + return { h: h, s: s, l: l }; + } + + export function scrollIntoView(targetY: number, targetHgt: number, scrollTop: number, contextHgt: number, minSpacing: number, scrollHeight: number) { + if (!targetHgt) return targetY; // if there's no height, then assume that + if (scrollTop + contextHgt < Math.min(scrollHeight, targetY + minSpacing + targetHgt)) { + return Math.ceil(targetY + minSpacing + targetHgt - contextHgt); + } + if (scrollTop >= Math.max(0, targetY - minSpacing)) { + return Math.max(0, Math.floor(targetY - minSpacing)); + } + return undefined; + } + + export function GetClipboardText(): string { + const textArea = document.createElement('textarea'); + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + + try { + document.execCommand('paste'); + } catch (err) { + /* empty */ + } + + const val = textArea.value; + document.body.removeChild(textArea); + return val; + } +} + +export function OmitKeys(obj: any, keys: string[], pattern?: string, addKeyFunc?: (dup: any) => void): { omit: any; extract: any } { + const omit: any = { ...obj }; + const extract: any = {}; + keys.forEach(key => { + extract[key] = omit[key]; + delete omit[key]; + }); + pattern && + Array.from(Object.keys(omit)) + .filter(key => key.match(pattern)) + .forEach(key => { + extract[key] = omit[key]; + delete omit[key]; + }); + addKeyFunc?.(omit); + return { omit, extract }; +} + +export function WithKeys(obj: any, keys: string[], addKeyFunc?: (dup: any) => void) { + const dup: any = {}; + keys.forEach(key => { + dup[key] = obj[key]; + }); + addKeyFunc && addKeyFunc(dup); + return dup; +} + +export function incrementTitleCopy(title: string) { + const numstr = title.match(/.*(\{([0-9]*)\})+/); + const copyNumStr = `{${1 + (numstr ? +numstr[2] : 0)}}`; + return (numstr ? title.replace(numstr[1], '') : title) + copyNumStr; +} + +const easeFunc = (transition: 'ease' | 'linear' | undefined, currentTime: number, start: number, change: number, duration: number) => { + if (transition === 'linear') { + const newCurrentTime = currentTime / duration; // currentTime / (duration / 2); + return start + newCurrentTime * change; + } + + let newCurrentTime = currentTime / (duration / 2); + if (newCurrentTime < 1) { + return (change / 2) * newCurrentTime * newCurrentTime + start; + } + + newCurrentTime -= 1; + return (-change / 2) * (newCurrentTime * (newCurrentTime - 2) - 1) + start; +}; + +export function smoothScroll(duration: number, element: HTMLElement | HTMLElement[], to: number, transition: 'ease' | 'linear' | undefined, stopper?: () => void) { + stopper?.(); + const elements = element instanceof HTMLElement ? [element] : element; + const starts = elements.map(element => element.scrollTop); + const startDate = new Date().getTime(); + let _stop = false; + const stop = () => { + _stop = true; + }; + const animateScroll = () => { + const currentDate = new Date().getTime(); + const currentTime = currentDate - startDate; + const setScrollTop = (element: HTMLElement, value: number) => { + element.scrollTop = value; + }; + if (!_stop) { + if (currentTime < duration) { + elements.forEach((element, i) => currentTime && setScrollTop(element, easeFunc(transition, Math.min(currentTime, duration), starts[i], to - starts[i], duration))); + requestAnimationFrame(animateScroll); + } else { + elements.forEach(element => setScrollTop(element, to)); + } + } + }; + animateScroll(); + return stop; +} + +export function smoothScrollHorizontal(duration: number, element: HTMLElement | HTMLElement[], to: number) { + const elements = element instanceof HTMLElement ? [element] : element; + const starts = elements.map(element => element.scrollLeft); + const startDate = new Date().getTime(); + + const animateScroll = () => { + const currentDate = new Date().getTime(); + const currentTime = currentDate - startDate; + elements.forEach((element, i) => { + element.scrollLeft = easeFunc('ease', currentTime, starts[i], to - starts[i], duration); + }); + + if (currentTime < duration) { + requestAnimationFrame(animateScroll); + } else { + elements.forEach(element => { + element.scrollLeft = to; + }); + } + }; + animateScroll(); +} + +export function addStyleSheet(styleType: string = 'text/css') { + const style = document.createElement('style'); + style.type = styleType; + const sheets = document.head.appendChild(style); + return (sheets as any).sheet; +} +export function addStyleSheetRule(sheet: any, selector: any, css: any, selectorPrefix = '.') { + const propText = + typeof css === 'string' + ? css + : Object.keys(css) + .map(p => p + ':' + (p === 'content' ? "'" + css[p] + "'" : css[p])) + .join(';'); + return sheet.insertRule(selectorPrefix + selector + '{' + propText + '}', sheet.cssRules.length); +} +export function removeStyleSheetRule(sheet: any, rule: number) { + if (sheet.rules.length) { + sheet.removeRule(rule); + return true; + } + return false; +} +export function clearStyleSheetRules(sheet: any) { + if (sheet.rules.length) { + numberRange(sheet.rules.length).map(() => sheet.removeRule(0)); + return true; + } + return false; +} + +export function simulateMouseClick(element: Element | null | undefined, x: number, y: number, sx: number, sy: number, rightClick = true) { + if (!element) return; + ['pointerdown', 'pointerup'].forEach(event => { + const me = new PointerEvent(event, { + view: window, + bubbles: true, + cancelable: true, + button: 2, + pointerType: 'mouse', + clientX: x, + clientY: y, + screenX: sx, + screenY: sy, + }); + (me as any).dash = true; + element.dispatchEvent(me); + }); + + if (rightClick) { + const me = new MouseEvent('contextmenu', { + view: window, + bubbles: true, + cancelable: true, + button: 2, + clientX: x, + clientY: y, + movementX: 0, + movementY: 0, + screenX: sx, + screenY: sy, + }); + (me as any).dash = true; + element.dispatchEvent(me); + } +} + +export function getWordAtPoint(elem: any, x: number, y: number): string | undefined { + if (elem.tagName === 'INPUT') return 'input'; + if (elem.tagName === 'TEXTAREA') return 'textarea'; + if (elem.nodeType === elem.TEXT_NODE) { + const range = elem.ownerDocument.createRange(); + range.selectNodeContents(elem); + let currentPos = 0; + const endPos = range.endOffset; + while (currentPos + 1 <= endPos) { + range.setStart(elem, currentPos); + range.setEnd(elem, currentPos + 1); + const rangeRect = range.getBoundingClientRect(); + if (rangeRect.left <= x && rangeRect.right >= x && rangeRect.top <= y && rangeRect.bottom >= y) { + range.expand?.('word'); // doesn't exist in firefox + const ret = range.toString(); + range.detach(); + return ret; + } + currentPos += 1; + } + } else { + Array.from(elem.childNodes).forEach((childNode: any) => { + const range = childNode.ownerDocument.createRange(); + range.selectNodeContents(childNode); + const rangeRect = range.getBoundingClientRect(); + if (rangeRect.left <= x && rangeRect.right >= x && rangeRect.top <= y && rangeRect.bottom >= y) { + range.detach(); + const word = getWordAtPoint(childNode, x, y); + if (word) return word; + } else { + range.detach(); + } + return undefined; + }); + } + return undefined; +} + +export function isTargetChildOf(ele: HTMLDivElement | null, target: Element | null) { + let entered = false; + for (let child = target; !entered && child; child = child.parentElement) { + entered = child === ele; + } + return entered; +} + +export function StopEvent(e: React.PointerEvent | React.MouseEvent | React.KeyboardEvent) { + e.stopPropagation(); + e.preventDefault(); +} + +export function setupMoveUpEvents( + target: object, + e: React.PointerEvent, + moveEvent: (e: PointerEvent, down: number[], delta: number[]) => boolean, + upEvent: (e: PointerEvent, movement: number[], isClick: boolean) => any, + clickEvent: (e: PointerEvent, doubleTap?: boolean) => any, + // eslint-disable-next-line default-param-last + stopPropagation: boolean = true, + // eslint-disable-next-line default-param-last + stopMovePropagation: boolean = true, + noDoubleTapTimeout?: () => void +) { + const targetAny = target as any; + const doubleTapTimeout = 300; + targetAny._doubleTap = Date.now() - (target as any)._lastTap < doubleTapTimeout; + targetAny._lastTap = Date.now(); + targetAny._downX = targetAny._lastX = e.clientX; + targetAny._downY = targetAny._lastY = e.clientY; + targetAny._noClick = false; + let moving = false; + + const _moveEvent = (e: PointerEvent): void => { + if (moving || Math.abs(e.clientX - (target as any)._downX) > ClientUtils.DRAG_THRESHOLD || Math.abs(e.clientY - (target as any)._downY) > ClientUtils.DRAG_THRESHOLD) { + moving = true; + if ((target as any)._doubleTime) { + clearTimeout((target as any)._doubleTime); + targetAny._doubleTime = undefined; + } + if (moveEvent(e, [(target as any)._downX, (target as any)._downY], [e.clientX - (target as any)._lastX, e.clientY - (target as any)._lastY])) { + document.removeEventListener('pointermove', _moveEvent); + // eslint-disable-next-line no-use-before-define + document.removeEventListener('pointerup', _upEvent); + } + } + targetAny._lastX = e.clientX; + targetAny._lastY = e.clientY; + stopMovePropagation && e.stopPropagation(); + }; + const _upEvent = (e: PointerEvent): void => { + const isClick = Math.abs(e.clientX - targetAny._downX) < 4 && Math.abs(e.clientY - targetAny._downY) < 4; + upEvent(e, [e.clientX - targetAny._downX, e.clientY - targetAny._downY], isClick); + if (isClick) { + if (!targetAny._doubleTime && noDoubleTapTimeout) { + targetAny._doubleTime = setTimeout(() => { + noDoubleTapTimeout?.(); + targetAny._doubleTime = undefined; + }, doubleTapTimeout); + } + if (targetAny._doubleTime && targetAny._doubleTap) { + clearTimeout(targetAny._doubleTime); + targetAny._doubleTime = undefined; + } + targetAny._noClick = clickEvent(e, targetAny._doubleTap); + } + document.removeEventListener('pointermove', _moveEvent); + document.removeEventListener('pointerup', _upEvent, true); + }; + const _clickEvent = (e: MouseEvent): void => { + if ((target as any)._noClick) e.stopPropagation(); + document.removeEventListener('click', _clickEvent, true); + }; + if (stopPropagation) { + e.stopPropagation(); + e.preventDefault(); + } + document.addEventListener('pointermove', _moveEvent); + document.addEventListener('pointerup', _upEvent, true); + document.addEventListener('click', _clickEvent, true); +} + +export function DivHeight(ele: HTMLElement): number { + return Number(getComputedStyle(ele).height.replace('px', '')); +} +export function DivWidth(ele: HTMLElement): number { + return Number(getComputedStyle(ele).width.replace('px', '')); +} + +export function dateRangeStrToDates(dateStr: string) { + // dateStr in yyyy-mm-dd format + const dateRangeParts = dateStr.split('|'); // splits into from and to date + const fromParts = dateRangeParts[0].split('-'); + const toParts = dateRangeParts[1].split('-'); + + const fromYear = parseInt(fromParts[0]); + const fromMonth = parseInt(fromParts[1]) - 1; + const fromDay = parseInt(fromParts[2]); + + const toYear = parseInt(toParts[0]); + const toMonth = parseInt(toParts[1]) - 1; + const toDay = parseInt(toParts[2]); + + return [new Date(fromYear, fromMonth, fromDay), new Date(toYear, toMonth, toDay)]; +} diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 804e6d1d7..b833d3287 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -102,7 +102,7 @@ export namespace DocServer { } export function getFieldWriteMode(field: string) { - return ClientUtils.CurrentUserEmail === 'guest' ? WriteMode.LivePlayground : fieldWriteModes[field] || WriteMode.Default; + return ClientUtils.CurrentUserEmail() === 'guest' ? WriteMode.LivePlayground : fieldWriteModes[field] || WriteMode.Default; } export function registerDocWithCachedUpdate(doc: Doc, field: string, oldValue: any) { @@ -463,7 +463,7 @@ export namespace DocServer { function _CreateFieldImpl(field: RefField) { _cache[field[Id]] = field; const initialState = SerializationHelper.Serialize(field); - ClientUtils.CurrentUserEmail !== 'guest' && DocServer.Emit(_socket, MessageStore.CreateField, initialState); + ClientUtils.CurrentUserEmail() !== 'guest' && DocServer.Emit(_socket, MessageStore.CreateField, initialState); } // NOTIFY THE SERVER OF AN UPDATE TO A DOC'S STATE @@ -481,7 +481,7 @@ export namespace DocServer { } function _UpdateFieldImpl(id: string, diff: any) { - !DocServer.Control.isReadOnly() && ClientUtils.CurrentUserEmail !== 'guest' && DocServer.Emit(_socket, MessageStore.UpdateField, { id, diff }); + !DocServer.Control.isReadOnly() && ClientUtils.CurrentUserEmail() !== 'guest' && DocServer.Emit(_socket, MessageStore.UpdateField, { id, diff }); } function _respondToUpdateImpl(diff: any) { @@ -516,11 +516,11 @@ export namespace DocServer { } export function DeleteDocument(id: string) { - ClientUtils.CurrentUserEmail !== 'guest' && DocServer.Emit(_socket, MessageStore.DeleteField, id); + ClientUtils.CurrentUserEmail() !== 'guest' && DocServer.Emit(_socket, MessageStore.DeleteField, id); } export function DeleteDocuments(ids: string[]) { - ClientUtils.CurrentUserEmail !== 'guest' && DocServer.Emit(_socket, MessageStore.DeleteFields, ids); + ClientUtils.CurrentUserEmail() !== 'guest' && DocServer.Emit(_socket, MessageStore.DeleteFields, ids); } function _respondToDeleteImpl(ids: string | string[]) { diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d41a96db2..f827ea81c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -970,7 +970,7 @@ export namespace Docs { dataProps['acl-Guest'] = options['acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View); dataProps.isSystem = viewProps.isSystem; dataProps.isDataDoc = true; - dataProps.author = ClientUtils.CurrentUserEmail; + dataProps.author = ClientUtils.CurrentUserEmail(); dataProps.author_date = new DateField(); if (fieldKey) { dataProps[`${fieldKey}_modificationDate`] = new DateField(); @@ -990,7 +990,7 @@ export namespace Docs { } if (!noView) { - const viewFirstProps: { [id: string]: any } = { author: ClientUtils.CurrentUserEmail }; + const viewFirstProps: { [id: string]: any } = { author: ClientUtils.CurrentUserEmail() }; viewFirstProps['acl-Guest'] = options['_acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View); let viewDoc: Doc; // determines whether viewDoc should be created using placeholder Doc or default @@ -1330,7 +1330,7 @@ export namespace Docs { const doc = DockDocument( configs.map(c => c.doc), JSON.stringify(layoutConfig), - ClientUtils.CurrentUserEmail === 'guest' ? options : { 'acl-Guest': SharingPermissions.View, ...options }, + ClientUtils.CurrentUserEmail() === 'guest' ? options : { 'acl-Guest': SharingPermissions.View, ...options }, id ); configs.forEach(c => { @@ -1722,7 +1722,7 @@ export namespace DocUtils { event: undoable(() => { const newDoc = DocUtils.copyDragFactory(dragDoc); if (newDoc) { - newDoc.author = ClientUtils.CurrentUserEmail; + newDoc.author = ClientUtils.CurrentUserEmail(); newDoc.x = x; newDoc.y = y; EquationBox.SelectOnLoad = newDoc[Id]; @@ -1773,7 +1773,7 @@ export namespace DocUtils { event: undoable(() => { const newDoc = DocUtils.delegateDragFactory(dragDoc); if (newDoc) { - newDoc.author = ClientUtils.CurrentUserEmail; + newDoc.author = ClientUtils.CurrentUserEmail(); newDoc.x = x; newDoc.y = y; EquationBox.SelectOnLoad = newDoc[Id]; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 535f7cf9d..27ae5c9a0 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -586,7 +586,7 @@ pie title Minerals in my tap water /// initializes the left sidebar panel view of the UserDoc static setupUserDocView(doc: Doc, field:string) { const reqdOpts:DocumentOptions = { - _lockedPosition: true, _gridGap: 5, _forceActive: true, title: ClientUtils.CurrentUserEmail +"-view", + _lockedPosition: true, _gridGap: 5, _forceActive: true, title: ClientUtils.CurrentUserEmail() +"-view", layout_boxShadow: "0 0", childDontRegisterViews: true, dropAction: dropActionType.same, ignoreClick: true, isSystem: true, treeView_HideTitle: true, treeView_TruncateTitleWidth: 350 }; @@ -832,8 +832,8 @@ pie title Minerals in my tap water static setupLinkDocs(doc: Doc, linkDatabaseId: string) { if (!(CurrentUserUtils.newAccount ? undefined : DocCast(doc.myLinkDatabase))) { const linkDocs = new Doc(linkDatabaseId, true); - linkDocs.title = "LINK DATABASE: " + ClientUtils.CurrentUserEmail; - linkDocs.author = ClientUtils.CurrentUserEmail; + linkDocs.title = "LINK DATABASE: " + ClientUtils.CurrentUserEmail(); + linkDocs.author = ClientUtils.CurrentUserEmail(); linkDocs.isSystem = true; linkDocs.data = new List([]); linkDocs["acl-Guest"] = SharingPermissions.Augment; @@ -893,11 +893,11 @@ pie title Minerals in my tap water reaction(() => DateCast(DocCast(doc.globalGroupDatabase).data_modificationDate), async () => { const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data); - const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(ClientUtils.CurrentUserEmail)) || []; + const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(ClientUtils.CurrentUserEmail())) || []; SetCachedGroups(["Guest", ...(mygroups?.map(g => StrCast(g.title))??[])]); }, { fireImmediately: true }); doc.isSystem ?? (doc.isSystem = true); - doc.title ?? (doc.title = ClientUtils.CurrentUserEmail); + doc.title ?? (doc.title = ClientUtils.CurrentUserEmail()); Doc.noviceMode ?? (Doc.noviceMode = true); doc._showLabel ?? (doc._showLabel = true); doc.textAlign ?? (doc.textAlign = "left"); @@ -975,7 +975,7 @@ pie title Minerals in my tap water if (response) { const result: { version: string, userDocumentId: string, sharingDocumentId: string, linkDatabaseId: string, email: string, cacheDocumentIds: string, resolvedPorts: string } = JSON.parse(response); runInAction(() => { CurrentUserUtils.ServerVersion = result.version; }); - ClientUtils.CurrentUserEmail = result.email; + ClientUtils.SetCurrentUserEmail(result.email); resolvedPorts = result.resolvedPorts as any; DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts?.socket, result.email); if (result.cacheDocumentIds) @@ -1004,7 +1004,7 @@ pie title Minerals in my tap water const userDoc = CurrentUserUtils.newAccount ? new Doc(info.userDocumentId, true) : field as Doc; this.updateUserDocument(Doc.SetUserDoc(userDoc), info.sharingDocumentId, info.linkDatabaseId); if (CurrentUserUtils.newAccount) { - if (ClientUtils.CurrentUserEmail === "guest") { + if (ClientUtils.CurrentUserEmail() === "guest") { DashboardView.createNewDashboard(undefined, "guest dashboard"); } else { userDoc.activePage = "home"; diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 67a61f17e..9a7786125 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -321,7 +321,7 @@ export class DocumentManager { if (options.toggleTarget && (!options.didMove || docView.Document.hidden)) docView.Document.hidden = !docView.Document.hidden; if (options.effect) docView.Document[Animation] = options.effect; - if (options.zoomTextSelections && Doc.UnhighlightTimer && contextView && targetDoc.text_html) { + if (options.zoomTextSelections && Doc.IsUnhighlightTimerSet() && contextView && targetDoc.text_html) { // if the docView is a text anchor, the contextView is the PDF/Web/Text doc contextView.setTextHtmlOverlay(StrCast(targetDoc.text_html), options.effect); DocumentManager._overlayViews.add(contextView); diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 6b20b885b..c261c0f1e 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -136,7 +136,7 @@ export class GroupManager extends ObservableReactComponent<{}> { hasEditAccess(groupDoc: Doc): boolean { if (!groupDoc) return false; const accessList: string[] = JSON.parse(StrCast(groupDoc.owners)); - return accessList.includes(ClientUtils.CurrentUserEmail) || this.adminGroupMembers?.includes(ClientUtils.CurrentUserEmail); + return accessList.includes(ClientUtils.CurrentUserEmail()) || this.adminGroupMembers?.includes(ClientUtils.CurrentUserEmail()); } /** @@ -148,7 +148,7 @@ export class GroupManager extends ObservableReactComponent<{}> { const name = groupName.toLowerCase() === 'admin' ? 'Admin' : groupName; const groupDoc = new Doc('GROUP:' + name, true); groupDoc.title = name; - groupDoc.owners = JSON.stringify([ClientUtils.CurrentUserEmail]); + groupDoc.owners = JSON.stringify([ClientUtils.CurrentUserEmail()]); groupDoc.members = JSON.stringify(memberEmails); this.addGroup(groupDoc); } @@ -177,7 +177,7 @@ export class GroupManager extends ObservableReactComponent<{}> { Doc.RemoveDocFromList(this.GroupManagerDoc, 'data', group); SharingManager.Instance.removeGroup(group); const members = JSON.parse(StrCast(group.members)); - if (members.includes(ClientUtils.CurrentUserEmail)) { + if (members.includes(ClientUtils.CurrentUserEmail())) { const index = DocListCast(this.GroupManagerDoc.data).findIndex(grp => grp === group); index !== -1 && Cast(this.GroupManagerDoc.data, listSpec(Doc), [])?.splice(index, 1); } diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts index d1600468a..48d3ac046 100644 --- a/src/client/util/HypothesisUtils.ts +++ b/src/client/util/HypothesisUtils.ts @@ -33,7 +33,7 @@ export namespace Hypothesis { // await SearchUtil.Search('web', true).then( // action(async (res: SearchUtil.DocSearchResult) => { // const docs = res.docs; - // const filteredDocs = docs.filter(doc => doc.author === ClientUtils.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data); + // const filteredDocs = docs.filter(doc => doc.author === ClientUtils.CurrentUserEmail() && doc.type === DocumentType.WEB && doc.data); // filteredDocs.forEach(doc => { // uri === Cast(doc.data, WebField)?.url.href && results.push(doc); // TODO check visited sites history? // }); diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index e2971895a..f983c29b7 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -128,7 +128,7 @@ export class SettingsManager extends React.Component<{}> { if (this.playgroundMode) { DocServer.Control.makeReadOnly(); addStyleSheetRule(SettingsManager._settingsStyle, 'topbar-inner-container', { background: 'red !important' }); - } else ClientUtils.CurrentUserEmail !== 'guest' && DocServer.Control.makeEditable(); + } else ClientUtils.CurrentUserEmail() !== 'guest' && DocServer.Control.makeEditable(); }); @undoBatch @@ -537,7 +537,7 @@ export class SettingsManager extends React.Component<{}> {
{DashVersion}
- {ClientUtils.CurrentUserEmail} + {ClientUtils.CurrentUserEmail()}
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 03f7e9b71..ade4cc218 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -137,7 +137,7 @@ export class SharingManager extends React.Component<{}> { if (!this.populating && Doc.UserDoc()[Id] !== Utils.GuestID()) { this.populating = true; const userList = await RequestPromise.get(ClientUtils.prepend('/getUsers')); - const raw = (JSON.parse(userList) as User[]).filter(user => user.email !== 'guest' && user.email !== ClientUtils.CurrentUserEmail); + const raw = (JSON.parse(userList) as User[]).filter(user => user.email !== 'guest' && user.email !== ClientUtils.CurrentUserEmail()); runInAction(() => { FieldLoader.ServerLoadStatus.message = 'users'; }); @@ -234,7 +234,7 @@ export class SharingManager extends React.Component<{}> { shareFromPropertiesSidebar = undoable((shareWith: string, permission: SharingPermissions, docs: Doc[], layout: boolean) => { if (layout) this.layoutDocAcls = true; if (shareWith !== 'Guest') { - const user = this.users.find(({ user: { email } }) => email === (shareWith === 'Me' ? ClientUtils.CurrentUserEmail : shareWith)); + const user = this.users.find(({ user: { email } }) => email === (shareWith === 'Me' ? ClientUtils.CurrentUserEmail() : shareWith)); docs.forEach(doc => { if (user) this.setInternalSharing(user, permission, doc); else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc, undefined, true); @@ -499,19 +499,19 @@ export class SharingManager extends React.Component<{}> { const sameAuthor = docs.every(doc => doc?.author === docs[0]?.author); // the owner of the doc and the current user are placed at the top of the user list. - const userKey = `acl-${normalizeEmail(ClientUtils.CurrentUserEmail)}`; + const userKey = `acl-${normalizeEmail(ClientUtils.CurrentUserEmail())}`; const curUserPermission = StrCast(targetDoc[userKey]); // const curUserPermission = HierarchyMapping.get(effectiveAcls[0])!.name userListContents.unshift( sameAuthor ? (
- {targetDoc?.author === ClientUtils.CurrentUserEmail ? 'Me' : StrCast(targetDoc?.author)} + {targetDoc?.author === ClientUtils.CurrentUserEmail() ? 'Me' : StrCast(targetDoc?.author)}
Owner
) : null, - sameAuthor && targetDoc?.author !== ClientUtils.CurrentUserEmail ? ( + sameAuthor && targetDoc?.author !== ClientUtils.CurrentUserEmail() ? (
Me
diff --git a/src/client/util/reportManager/ReportManager.tsx b/src/client/util/reportManager/ReportManager.tsx index d6fd339a9..02b3ee32c 100644 --- a/src/client/util/reportManager/ReportManager.tsx +++ b/src/client/util/reportManager/ReportManager.tsx @@ -134,7 +134,7 @@ export class ReportManager extends React.Component<{}> { const req = await this.octokit.request('POST /repos/{owner}/{repo}/issues', { owner: 'brown-dash', repo: 'Dash-Web', - title: formatTitle(this.formData.title, ClientUtils.CurrentUserEmail), + title: formatTitle(this.formData.title, ClientUtils.CurrentUserEmail()), body: `${this.formData.description} ${formattedLinks.length > 0 ? `\n\nFiles:\n${formattedLinks.join('\n')}` : ''}`, labels: ['from-dash-app', this.formData.type, this.formData.priority], }); diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 1d286c987..14abd5f89 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -64,7 +64,7 @@ export class DashboardView extends ObservableReactComponent<{}> { getDashboards = (whichGroup: DashboardGroup) => { if (whichGroup === DashboardGroup.MyDashboards) { - return DocListCast(Doc.MyDashboards.data).filter(dashboard => dashboard[DocData].author === ClientUtils.CurrentUserEmail); + return DocListCast(Doc.MyDashboards.data).filter(dashboard => dashboard[DocData].author === ClientUtils.CurrentUserEmail()); } return DocListCast(Doc.MySharedDocs.data_dashboards).filter(doc => doc.dockingConfig); }; @@ -155,7 +155,7 @@ export class DashboardView extends ObservableReactComponent<{}> { : this.getDashboards(this.selectedDashboardGroup).map(dashboard => { const href = ImageCast(dashboard.thumb)?.url?.href; const shared = Object.keys(dashboard[DocAcl]) - .filter(key => key !== `acl-${normalizeEmail(ClientUtils.CurrentUserEmail)}` && !['acl-Me', 'acl-Guest'].includes(key)) + .filter(key => key !== `acl-${normalizeEmail(ClientUtils.CurrentUserEmail())}` && !['acl-Me', 'acl-Guest'].includes(key)) .some(key => dashboard[DocAcl][key] !== AclPrivate); return (
{ } else if (doc.readOnly) { DocServer.Control.makeReadOnly(); } else { - ClientUtils.CurrentUserEmail !== 'guest' && DocServer.Control.makeEditable(); + ClientUtils.CurrentUserEmail() !== 'guest' && DocServer.Control.makeEditable(); } } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 8a060d0e0..13945cacf 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -122,7 +122,7 @@ export class MainView extends ObservableReactComponent<{}> { } @observable mainDoc: Opt = undefined; @computed private get mainContainer() { - if (window.location.pathname.startsWith('/doc/') && ClientUtils.CurrentUserEmail === 'guest') { + if (window.location.pathname.startsWith('/doc/') && ClientUtils.CurrentUserEmail() === 'guest') { DocServer.GetRefField(window.location.pathname.substring('/doc/'.length)).then(main => runInAction(() => { this.mainDoc = main as Doc; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 77e68866e..2a847e51a 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -405,7 +405,7 @@ export class PropertiesView extends ObservableReactComponent, props: Opt(moreProps?: X) { let ind; const doc = this.Document; const id = Doc.UserDoc()[Id]; - const email = ClientUtils.CurrentUserEmail; + const email = ClientUtils.CurrentUserEmail(); const pos = { x: position[0], y: position[1] }; if (id && email) { const proto = Doc.GetProto(doc); diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index e266ccd14..20323a521 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -229,7 +229,7 @@ export class TreeView extends ObservableReactComponent { this.treeViewOpen = !this.treeViewOpen; } else { // choose an appropriate embedding or make one. --- choose the first embedding that (1) user owns, (2) has no context field ... otherwise make a new embedding - const bestEmbedding = docView.Document.author === ClientUtils.CurrentUserEmail && !Doc.IsDataProto(docView.Document) ? docView.Document : Doc.BestEmbedding(docView.Document); + const bestEmbedding = docView.Document.author === ClientUtils.CurrentUserEmail() && !Doc.IsDataProto(docView.Document) ? docView.Document : Doc.BestEmbedding(docView.Document); this._props.addDocTab(bestEmbedding, OpenWhere.lightbox); } }; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index dbd2ebe0d..62a3e2467 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1512,7 +1512,7 @@ export class DocumentView extends DocComponent LightboxView.Instance.SetLightboxDoc( diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index c7543560d..a82f025f9 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -580,7 +580,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { const view = this._editorView!; - const nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: ClientUtils.CurrentUserEmail }); + const nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: ClientUtils.CurrentUserEmail() }); view.dispatch(view.state.tr.removeMark(start, end, nmark).addMark(start, end, nmark)); }; protected createDropTarget = (ele: HTMLDivElement) => { @@ -722,7 +722,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-min-' + (min - i), { opacity: ((10 - i - 1) / 10).toString() })); } if (highlights.includes('By Recent Hour')) { - addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-' + ClientUtils.CurrentUserEmail.replace('.', '').replace('@', ''), { opacity: '0.1' }); + addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-' + ClientUtils.CurrentUserEmail().replace('.', '').replace('@', ''), { opacity: '0.1' }); const hr = Math.round(Date.now() / 1000 / 60 / 60); numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-hr-' + (hr - i), { opacity: ((10 - i - 1) / 10).toString() })); } @@ -1489,7 +1489,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent m.type !== mark.type), mark]; const tr1 = this._editorView.state.tr.setStoredMarks(storedMarks); @@ -1503,7 +1503,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent m.type !== mark.type), mark]; const { tr } = this._editorView.state; @@ -1534,7 +1534,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== ClientUtils.CurrentUserEmail) && [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.Document))) { + if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== ClientUtils.CurrentUserEmail()) && [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.Document))) { e.preventDefault(); } } @@ -1799,7 +1799,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent m.type === schema.marks.user_mark && m.attrs.modified === modified); - _editorView.dispatch(state.tr.removeStoredMark(schema.marks.user_mark).addStoredMark(mark ?? schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail, modified }))); + _editorView.dispatch(state.tr.removeStoredMark(schema.marks.user_mark).addStoredMark(mark ?? schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified }))); } break; } diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index 533fefa09..52d93ec38 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -10,7 +10,7 @@ import './FormattedTextBoxComment.scss'; import { schema } from './schema_rts'; export function findOtherUserMark(marks: readonly Mark[]): Mark | undefined { - return marks.find(m => m.attrs.userid && m.attrs.userid !== ClientUtils.CurrentUserEmail); + return marks.find(m => m.attrs.userid && m.attrs.userid !== ClientUtils.CurrentUserEmail()); } export function findUserMark(marks: readonly Mark[]): Mark | undefined { return marks.find(m => m.attrs.userid); diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index e9ed2549e..c3a5a2c86 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -51,8 +51,8 @@ export function buildKeymap>(schema: S, props: any, mapKey case AclAugment: { const prevNode = state.selection.$cursor.nodeBefore; - const prevUser = !prevNode ? ClientUtils.CurrentUserEmail : prevNode.marks[prevNode.marks.length - 1].attrs.userid; - if (prevUser !== ClientUtils.CurrentUserEmail) { + const prevUser = !prevNode ? ClientUtils.CurrentUserEmail() : prevNode.marks[prevNode.marks.length - 1].attrs.userid; + if (prevUser !== ClientUtils.CurrentUserEmail()) { return false; } } diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 78ea99592..5b5617484 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -433,8 +433,8 @@ export class RichTextRules { return node ? state.tr .removeMark(start, end, schema.marks.user_mark) - .addMark(start, end, schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })) - .addMark(start, end, schema.marks.user_tag.create({ userid: ClientUtils.CurrentUserEmail, tag: tag, modified: Math.round(Date.now() / 1000 / 60) })) + .addMark(start, end, schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) })) + .addMark(start, end, schema.marks.user_tag.create({ userid: ClientUtils.CurrentUserEmail(), tag: tag, modified: Math.round(Date.now() / 1000 / 60) })) : state.tr; }), diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts index 0f3306bef..d1c7b72a5 100644 --- a/src/client/views/nodes/formattedText/marks_rts.ts +++ b/src/client/views/nodes/formattedText/marks_rts.ts @@ -335,7 +335,7 @@ export const marks: { [index: string]: MarkSpec } = { const min = Math.round(node.attrs.modified / 60); const hr = Math.round(min / 60); const day = Math.round(hr / 60 / 24); - const remote = node.attrs.userid !== ClientUtils.CurrentUserEmail ? ' UM-remote' : ''; + const remote = node.attrs.userid !== ClientUtils.CurrentUserEmail() ? ' UM-remote' : ''; return ['span', { class: 'UM-' + uid + remote + ' UM-min-' + min + ' UM-hr-' + hr + ' UM-day-' + day }, 0]; }, }, diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 3fb914423..f8351c238 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -152,7 +152,7 @@ export const ReverseHierarchyMap: Map { key.startsWith('acl') && (permissions[key] = ReverseHierarchyMap.get(StrCast(target[key]))!.acl); }); @@ -301,15 +301,16 @@ export class Doc extends RefField { private set __fieldTuples(value) { // called by deserializer to set all fields in one shot this[FieldTuples] = value; - // eslint-disable-next-line no-restricted-syntax - for (const key in value) { - const field = value[key]; - field !== undefined && (this[FieldKeys][key] = true); - if (field instanceof ObjectField) { - field[Parent] = this[Self]; - field[FieldChanged] = containedFieldChangedHandler(this[SelfProxy], key, field); + Object.keys(value).forEach(key => { + if (Object.prototype.hasOwnProperty.call(value, key)) { + const field = value[key]; + field !== undefined && (this[FieldKeys][key] = true); + if (field instanceof ObjectField) { + field[Parent] = this[Self]; + field[FieldChanged] = containedFieldChangedHandler(this[SelfProxy], key, field); + } } - } + }); } @observable private [FieldTuples]: any = {}; @@ -365,14 +366,11 @@ export class Doc extends RefField { public async [HandleUpdate](diff: any) { const set = diff.$set; - const sameAuthor = this.author === ClientUtils.CurrentUserEmail; - if (set) { - for (const key in set) { - const fprefix = 'fields.'; - if (!key.startsWith(fprefix)) { - // eslint-disable-next-line no-continue - continue; - } + const sameAuthor = this.author === ClientUtils.CurrentUserEmail(); + const fprefix = 'fields.'; + Object.keys(set ?? {}) + .filter(key => Object.prototype.hasOwnProperty.call(set, key) && key.startsWith(fprefix)) + .forEach(async key => { const fKey = key.substring(fprefix.length); const fn = async () => { const value = await SerializationHelper.Deserialize(set[key]); @@ -395,15 +393,11 @@ export class Doc extends RefField { } else { this[CachedUpdates][fKey] = fn; } - } - } + }); const unset = diff.$unset; - if (unset) { - for (const key in unset) { - if (!key.startsWith('fields.')) { - // eslint-disable-next-line no-continue - continue; - } + Object.keys(unset ?? {}) + .filter(key => Object.prototype.hasOwnProperty.call(unset, key) && key.startsWith(fprefix)) + .forEach(async key => { const fKey = key.substring(7); const fn = () => { this[UpdatingFromServer] = true; @@ -412,13 +406,11 @@ export class Doc extends RefField { }; if (sameAuthor || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) { delete this[CachedUpdates][fKey]; - // eslint-disable-next-line no-await-in-loop await fn(); } else { this[CachedUpdates][fKey] = fn; } - } - } + }); } } @@ -519,15 +511,15 @@ export namespace Doc { */ export function assign(doc: Doc, fields: Partial>>, skipUndefineds: boolean = false, isInitializing = false) { isInitializing && (doc[Initializing] = true); - for (const key in fields) { - if (fields.hasOwnProperty(key)) { - const value = fields[key]; + Object.keys(fields).forEach(key => { + if (Object.prototype.hasOwnProperty.call(fields.hasOwnProperty, key)) { + const value = (fields as any)[key]; if (!skipUndefineds || value !== undefined) { // Do we want to filter out undefineds? doc[key] = value; } } - } + }); isInitializing && (doc[Initializing] = false); return doc; } @@ -640,7 +632,7 @@ export namespace Doc { embedding.createdFrom = doc; embedding.proto_embeddingId = doc[DocData].proto_embeddingId = Doc.GetEmbeddings(doc).length - 1; !Doc.GetT(embedding, 'title', 'string', true) && (embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`)); - embedding.author = ClientUtils.CurrentUserEmail; + embedding.author = ClientUtils.CurrentUserEmail(); return embedding; } @@ -648,7 +640,7 @@ export namespace Doc { export function BestEmbedding(doc: Doc) { const dataDoc = doc[DocData]; const availableEmbeddings = Doc.GetEmbeddings(dataDoc); - const bestEmbedding = [...(dataDoc !== doc ? [doc] : []), ...availableEmbeddings].find(doc => !doc.embedContainer && doc.author === ClientUtils.CurrentUserEmail); + const bestEmbedding = [...(dataDoc !== doc ? [doc] : []), ...availableEmbeddings].find(doc => !doc.embedContainer && doc.author === ClientUtils.CurrentUserEmail()); bestEmbedding && Doc.AddEmbedding(doc, doc); return bestEmbedding ?? Doc.MakeEmbedding(doc); } @@ -708,7 +700,7 @@ export namespace Doc { }; const docAtKey = doc[key]; if (key === 'author') { - assignKey(ClientUtils.CurrentUserEmail); + assignKey(ClientUtils.CurrentUserEmail()); } else if (docAtKey instanceof Doc) { if (pruneDocs.includes(docAtKey)) { // prune doc and do nothing @@ -717,7 +709,7 @@ export namespace Doc { (key.includes('layout[') || key.startsWith('layout') || // ['embedContainer', 'annotationOn', 'proto'].includes(key) || - (['link_anchor_1', 'link_anchor_2'].includes(key) && doc.author === ClientUtils.CurrentUserEmail)) + (['link_anchor_1', 'link_anchor_2'].includes(key) && doc.author === ClientUtils.CurrentUserEmail())) ) { assignKey(await Doc.makeClone(docAtKey, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates)); } else { @@ -886,7 +878,7 @@ export namespace Doc { } } else { const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); - const field = key === 'author' ? ClientUtils.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]); + const field = key === 'author' ? ClientUtils.CurrentUserEmail() : ProxyField.WithoutProxy(() => doc[key]); if (field instanceof RefField) { if (field instanceof Doc) { if (key === 'myLinkDatabase') { @@ -934,7 +926,7 @@ export namespace Doc { } } else { const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); - const field = key === 'author' ? ClientUtils.CurrentUserEmail : ProxyField.WithoutProxy(() => doc[key]); + const field = key === 'author' ? ClientUtils.CurrentUserEmail() : ProxyField.WithoutProxy(() => doc[key]); if (field instanceof RefField) { copy[key] = field; } else if (cfield instanceof ComputedField) { @@ -973,7 +965,7 @@ export namespace Doc { delegate[Initializing] = true; updateCachedAcls(delegate); delegate.proto = doc; - delegate.author = ClientUtils.CurrentUserEmail; + delegate.author = ClientUtils.CurrentUserEmail(); Object.keys(doc) .filter(key => key.startsWith('acl')) .forEach(key => { @@ -1004,7 +996,7 @@ export namespace Doc { export function ApplyTemplate(templateDoc: Doc) { if (templateDoc) { const proto = new Doc(); - proto.author = ClientUtils.CurrentUserEmail; + proto.author = ClientUtils.CurrentUserEmail(); const target = Doc.MakeDelegate(proto); const targetKey = StrCast(templateDoc.layout_fieldKey, 'layout'); const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + '(...' + _applyCount++ + ')'); @@ -1199,7 +1191,8 @@ export namespace Doc { } const UnhighlightWatchers: (() => void)[] = []; - export let UnhighlightTimer: any; + let UnhighlightTimer: any; + export function IsUnhighlightTimerSet() { return UnhighlightTimer; } // prettier-ignore export function AddUnHighlightWatcher(watcher: () => void) { if (UnhighlightTimer) { UnhighlightWatchers.push(watcher); diff --git a/src/fields/util.ts b/src/fields/util.ts index 5ed10cf05..d7268f31a 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -84,7 +84,7 @@ const _setterImpl = action((target: any, prop: string | symbol | number, valueIn const writeMode = DocServer.getFieldWriteMode(prop as string); const fromServer = target[UpdatingFromServer]; - const sameAuthor = fromServer || receiver.author === ClientUtils.CurrentUserEmail; + const sameAuthor = fromServer || receiver.author === ClientUtils.CurrentUserEmail(); const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode === DocServer.WriteMode.Playground || writeMode === DocServer.WriteMode.LivePlayground || (effectiveAcl === AclAugment && value instanceof RichTextField); const writeToServer = @@ -163,7 +163,7 @@ const getEffectiveAclCache = computedFn((target: any, user?: string) => getEffec */ export function GetEffectiveAcl(target: any, user?: string): symbol { if (!target) return AclPrivate; - if (target[UpdatingFromServer] || ClientUtils.CurrentUserEmail === 'guest') return AclAdmin; + if (target[UpdatingFromServer] || ClientUtils.CurrentUserEmail() === 'guest') return AclAdmin; return getEffectiveAclCache(target, user); // all changes received from the server must be processed as Admin. return this directly so that the acls aren't cached (UpdatingFromServer is not observable) } @@ -186,7 +186,7 @@ function getEffectiveAcl(target: any, user?: string): symbol { const targetAcls = target[DocAcl]; if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin; - const userChecked = user || ClientUtils.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group + const userChecked = user || ClientUtils.CurrentUserEmail(); // if the current user is the author of the document / the current user is a member of the admin group if (targetAcls && Object.keys(targetAcls).length) { let effectiveAcl = AclPrivate; Object.entries(targetAcls).forEach(([key, value]) => { @@ -219,7 +219,7 @@ function getEffectiveAcl(target: any, user?: string): symbol { */ // eslint-disable-next-line default-param-last export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, visited: Doc[] = [], allowUpgrade?: boolean, layoutOnly = false) { - const selfKey = `acl-${normalizeEmail(ClientUtils.CurrentUserEmail)}`; + const selfKey = `acl-${normalizeEmail(ClientUtils.CurrentUserEmail())}`; if (!target || visited.includes(target) || key === selfKey) return; visited.push(target); @@ -268,7 +268,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc * Copies parent's acl fields to the child */ export function inheritParentAcls(parent: Doc, child: Doc, layoutOnly: boolean) { - [...Object.keys(parent), ...(ClientUtils.CurrentUserEmail !== parent.author ? ['acl-Owner'] : [])] + [...Object.keys(parent), ...(ClientUtils.CurrentUserEmail() !== parent.author ? ['acl-Owner'] : [])] .filter(key => key.startsWith('acl')) .forEach(key => { // if the default acl mode is private, then don't inherit the acl-guest permission, but set it to private. -- cgit v1.2.3-70-g09d2 From b1376d401e709515cee078cc08b05fd3fb89caeb Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 24 Apr 2024 18:12:30 -0400 Subject: completing eslint pass --- src/client/apis/GoogleAuthenticationManager.tsx | 119 ++++++------ .../apis/google_docs/GooglePhotosClientUtils.ts | 2 +- src/client/documents/Documents.ts | 37 ++-- src/client/util/CalendarManager.tsx | 5 +- src/client/util/CaptureManager.tsx | 17 +- src/client/util/CurrentUserUtils.ts | 24 +-- src/client/util/DictationManager.ts | 63 ++++--- src/client/util/GroupMemberView.tsx | 32 ++-- src/client/util/History.ts | 34 ++-- .../util/Import & Export/ImportMetadataEntry.tsx | 40 ++-- src/client/util/KeyCodes.ts | 2 +- src/client/util/LinkFollower.ts | 2 +- src/client/util/PingManager.ts | 6 +- src/client/util/RTFMarkup.tsx | 25 +-- src/client/util/ReplayMovements.ts | 22 +-- src/client/util/ScriptManager.ts | 10 +- src/client/util/Scripting.ts | 4 +- src/client/util/SelectionManager.ts | 16 +- src/client/util/ServerStats.tsx | 45 +++-- src/client/util/SharingManager.tsx | 5 +- src/client/util/TrackMovements.ts | 24 +-- src/client/util/Transform.ts | 14 +- src/client/util/TypedEvent.ts | 18 +- src/client/util/bezierFit.ts | 6 +- .../util/reportManager/ReportManagerComponents.tsx | 131 +++++++------ .../util/reportManager/reportManagerSchema.ts | 1 + .../util/reportManager/reportManagerUtils.ts | 17 +- src/client/views/animationtimeline/Timeline.tsx | 60 +++--- .../views/animationtimeline/TimelineOverview.tsx | 16 +- .../views/collections/CollectionCalendarView.tsx | 30 ++- .../views/collections/CollectionCarouselView.tsx | 7 +- .../views/collections/CollectionDockingView.tsx | 6 +- .../collections/CollectionNoteTakingViewColumn.tsx | 39 ++-- .../CollectionNoteTakingViewDivider.tsx | 2 +- .../CollectionStackingViewFieldColumn.tsx | 6 +- .../views/collections/CollectionTreeView.tsx | 21 +-- src/client/views/collections/KeyRestrictionRow.tsx | 32 +++- .../CollectionFreeFormBackgroundGrid.tsx | 1 + .../CollectionFreeFormInfoState.tsx | 31 ++-- .../CollectionFreeFormLayoutEngines.tsx | 8 +- .../CollectionFreeFormRemoteCursors.tsx | 72 ++++---- .../collectionFreeForm/MarqueeOptionsMenu.tsx | 12 +- .../collectionGrid/CollectionGridView.tsx | 52 ++++-- .../collectionLinear/CollectionLinearView.tsx | 3 +- .../views/collections/collectionLinear/index.ts | 2 +- .../CollectionMulticolumnView.tsx | 57 ++++-- .../CollectionMultirowView.tsx | 24 ++- .../collectionMulticolumn/MulticolumnResizer.tsx | 24 +-- .../MulticolumnWidthLabel.tsx | 2 +- .../collectionMulticolumn/MultirowHeightLabel.tsx | 3 +- .../collectionMulticolumn/MultirowResizer.tsx | 26 +-- .../collectionSchema/SchemaColumnHeader.tsx | 18 +- .../collections/collectionSchema/SchemaRowBox.tsx | 23 +-- src/client/views/global/globalEnums.tsx | 52 +++--- src/client/views/linking/LinkMenuGroup.tsx | 27 ++- src/client/views/linking/LinkMenuItem.tsx | 10 +- .../views/linking/LinkRelationshipSearch.tsx | 63 ------- src/client/views/nodes/AudioBox.tsx | 11 +- .../views/nodes/DataVizBox/SchemaCSVPopUp.tsx | 62 +++---- .../views/nodes/DataVizBox/components/PieChart.tsx | 197 ++++++++++---------- src/client/views/nodes/DataVizBox/utils/D3Utils.ts | 29 +-- src/client/views/nodes/DocumentLinksButton.tsx | 6 +- src/client/views/nodes/EquationBox.tsx | 11 +- src/client/views/nodes/FaceRectangle.tsx | 10 +- .../views/nodes/FontIconBox/ButtonInterface.ts | 2 +- src/client/views/nodes/FontIconBox/TrailsIcon.tsx | 86 ++++----- src/client/views/nodes/LinkAnchorBox.tsx | 2 +- src/client/views/nodes/LinkBox.tsx | 83 +++++---- src/client/views/nodes/LinkDescriptionPopup.tsx | 33 ++-- src/client/views/nodes/LinkDocPreview.tsx | 64 ++++--- src/client/views/nodes/LoadingBox.tsx | 4 +- .../views/nodes/MapBox/AnimationSpeedIcons.tsx | 39 ++-- src/client/views/nodes/MapBox/AnimationUtility.ts | 57 +++--- .../views/nodes/MapBox/DirectionsAnchorMenu.tsx | 11 +- src/client/views/nodes/MapBox/GeocoderControl.tsx | 7 +- src/client/views/nodes/MapBox/MapAnchorMenu.tsx | 99 +++++----- src/client/views/nodes/MapBox/MapboxApiUtility.ts | 76 ++++---- src/client/views/nodes/MapBox/MarkerIcons.tsx | 3 +- .../views/nodes/MapboxMapBox/MapboxContainer.tsx | 28 +-- src/client/views/nodes/RadialMenu.tsx | 140 +++++--------- src/client/views/nodes/RadialMenuItem.tsx | 4 +- .../views/nodes/RecordingBox/ProgressBar.tsx | 205 +++++++++++---------- .../views/nodes/RecordingBox/RecordingBox.tsx | 17 +- .../views/nodes/RecordingBox/RecordingView.tsx | 41 ++--- src/client/views/nodes/RecordingBox/index.ts | 4 +- src/client/views/nodes/ScriptingBox.tsx | 162 ++++++++-------- src/client/views/nodes/SliderBox-components.tsx | 114 ++++++------ src/client/views/nodes/VideoBox.tsx | 42 +++-- src/client/views/nodes/audio/AudioWaveform.tsx | 8 +- src/client/views/nodes/audio/WaveCanvas.tsx | 12 +- src/client/views/nodes/calendarBox/CalendarBox.tsx | 7 +- .../nodes/formattedText/DashDocCommentView.tsx | 159 ++++++++-------- .../views/nodes/formattedText/DashDocView.tsx | 123 +++++++------ .../views/nodes/formattedText/EquationEditor.tsx | 3 +- .../views/nodes/formattedText/EquationView.tsx | 93 +++++----- .../views/nodes/formattedText/FootnoteView.tsx | 8 +- .../formattedText/FormattedTextBoxComment.tsx | 24 ++- .../views/nodes/formattedText/OrderedListView.tsx | 9 +- .../views/nodes/formattedText/ParagraphNodeSpec.ts | 101 +++++----- .../views/nodes/formattedText/RichTextRules.ts | 15 +- .../views/nodes/formattedText/SummaryView.tsx | 24 +-- .../views/nodes/generativeFill/GenerativeFill.tsx | 21 ++- .../nodes/generativeFill/GenerativeFillButtons.tsx | 10 +- .../GenerativeFillMathHelpers.ts | 8 +- .../generativeFillUtils/ImageHandler.ts | 36 ++-- .../generativeFillUtils/PointerHandler.ts | 18 +- .../views/nodes/importBox/ImportElementBox.tsx | 3 +- src/client/views/nodes/trails/index.ts | 6 +- src/client/views/pdf/AnchorMenu.tsx | 14 +- src/mobile/ImageUpload.tsx | 3 +- 110 files changed, 1913 insertions(+), 1891 deletions(-) delete mode 100644 src/client/views/linking/LinkRelationshipSearch.tsx (limited to 'src/client/documents') diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx index 855f48f7e..5269f763b 100644 --- a/src/client/apis/GoogleAuthenticationManager.tsx +++ b/src/client/apis/GoogleAuthenticationManager.tsx @@ -1,14 +1,14 @@ -import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx"; -import { observer } from "mobx-react"; -import * as React from "react"; -import { Opt } from "../../fields/Doc"; -import { Networking } from "../Network"; -import { ScriptingGlobals } from "../util/ScriptingGlobals"; -import { MainViewModal } from "../views/MainViewModal"; -import "./GoogleAuthenticationManager.scss"; +import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { Opt } from '../../fields/Doc'; +import { Networking } from '../Network'; +import { ScriptingGlobals } from '../util/ScriptingGlobals'; +import { MainViewModal } from '../views/MainViewModal'; +import './GoogleAuthenticationManager.scss'; -const AuthenticationUrl = "https://accounts.google.com/o/oauth2/v2/auth"; -const prompt = "Paste authorization code here..."; +const AuthenticationUrl = 'https://accounts.google.com/o/oauth2/v2/auth'; +const prompt = 'Paste authorization code here...'; @observer export class GoogleAuthenticationManager extends React.Component<{}> { @@ -23,11 +23,11 @@ export class GoogleAuthenticationManager extends React.Component<{}> { private disposer: Opt; private set isOpen(value: boolean) { - runInAction(() => this.openState = value); + runInAction(() => (this.openState = value)); } private set shouldShowPasteTarget(value: boolean) { - runInAction(() => this.showPasteTargetState = value); + runInAction(() => (this.showPasteTargetState = value)); } public cancel() { @@ -35,7 +35,7 @@ export class GoogleAuthenticationManager extends React.Component<{}> { } public fetchOrGenerateAccessToken = async (displayIfFound = false) => { - let response: any = await Networking.FetchFromServer("/readGoogleAccessToken"); + let response: any = await Networking.FetchFromServer('/readGoogleAccessToken'); // if this is an authentication url, activate the UI to register the new access token if (new RegExp(AuthenticationUrl).test(response)) { this.isOpen = true; @@ -47,7 +47,7 @@ export class GoogleAuthenticationManager extends React.Component<{}> { async authenticationCode => { if (authenticationCode && /\d{1}\/[\w-]{55}/.test(authenticationCode)) { this.disposer?.(); - const response = await Networking.PostToServer("/writeGoogleAccessToken", { authenticationCode }); + const response = await Networking.PostToServer('/writeGoogleAccessToken', { authenticationCode }); runInAction(() => { this.success = true; this.credentials = response; @@ -71,7 +71,7 @@ export class GoogleAuthenticationManager extends React.Component<{}> { this.isOpen = true; } return response.access_token; - } + }; resetState = action((visibleForMS: number = 3000, fadesOutInMS: number = 500) => { if (!visibleForMS && !fadesOutInMS) { @@ -89,14 +89,20 @@ export class GoogleAuthenticationManager extends React.Component<{}> { this.displayLauncher = false; this.shouldShowPasteTarget = false; if (visibleForMS > 0 && fadesOutInMS > 0) { - setTimeout(action(() => { - this.isOpen = false; - setTimeout(action(() => { - this.success = undefined; - this.displayLauncher = true; - this.credentials = undefined; - }), fadesOutInMS); - }), visibleForMS); + setTimeout( + action(() => { + this.isOpen = false; + setTimeout( + action(() => { + this.success = undefined; + this.displayLauncher = true; + this.credentials = undefined; + }), + fadesOutInMS + ); + }), + visibleForMS + ); } }); @@ -108,61 +114,44 @@ export class GoogleAuthenticationManager extends React.Component<{}> { private get renderPrompt() { return (
- - {this.displayLauncher ? : (null)} - {this.showPasteTargetState ? this.authenticationCode = e.currentTarget.value)} - placeholder={prompt} - /> : (null)} - {this.credentials ? + {this.displayLauncher ? ( + + ) : null} + {this.showPasteTargetState ? (this.authenticationCode = e.currentTarget.value))} placeholder={prompt} /> : null} + {this.credentials ? ( <> - - Welcome to Dash, {this.credentials.userInfo.name} - + + Welcome to Dash, {this.credentials.userInfo.name}
{ - await Networking.FetchFromServer("/revokeGoogleAccessToken"); + await Networking.FetchFromServer('/revokeGoogleAccessToken'); this.resetState(0, 0); - }} - >Disconnect Account
- : (null)} + }}> + Disconnect Account +
+ + ) : null}
); } private get dialogueBoxStyle() { - const borderColor = this.success === undefined ? "black" : this.success ? "green" : "red"; - return { borderColor, transition: "0.2s borderColor ease", zIndex: 1002 }; + const borderColor = this.success === undefined ? 'black' : this.success ? 'green' : 'red'; + return { borderColor, transition: '0.2s borderColor ease', zIndex: 1002 }; } render() { - return ( - this.isOpen = false)} - /> - ); + return (this.isOpen = false))} />; } - } -ScriptingGlobals.add("GoogleAuthenticationManager", GoogleAuthenticationManager); \ No newline at end of file +ScriptingGlobals.add('GoogleAuthenticationManager', GoogleAuthenticationManager); diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts index 07a2708ec..1eb977813 100644 --- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts +++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts @@ -291,7 +291,7 @@ export namespace GooglePhotos { response.results.map( item => new Promise(resolve => { - Query.GetImage(item.mediaItem.id).then(item => resolve(item.baseUrl)); + Query.GetImage(item.mediaItem.id).then(itm => resolve(itm.baseUrl)); }) ) ); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f827ea81c..cf397e85a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,9 +1,13 @@ +/* eslint-disable prefer-destructuring */ +/* eslint-disable default-param-last */ +/* eslint-disable no-use-before-define */ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { saveAs } from 'file-saver'; import * as JSZip from 'jszip'; import { action, reaction, runInAction } from 'mobx'; import { basename } from 'path'; import { ClientUtils, OmitKeys } from '../../ClientUtils'; +// eslint-disable-next-line import/extensions import * as JSZipUtils from '../../JSZipUtils'; import { decycle } from '../../decycler/decycler'; import { DateField } from '../../fields/DateField'; @@ -81,6 +85,7 @@ export enum FInfoFieldType { string = 'string', boolean = 'boolean', number = 'number', + // eslint-disable-next-line @typescript-eslint/no-shadow Doc = 'Doc', enumeration = 'enum', date = 'date', @@ -1360,9 +1365,9 @@ export namespace DocUtils { if (key === LinkedTo) { // links are not a field value, so handled here. value is an expression of form ([field=]idToDoc("...")) const allLinks = LinkManager.Instance.getAllRelatedLinks(doc); - const matchLink = (value: string, anchor: Doc) => { - const linkedToExp = (value ?? '').split('='); - if (linkedToExp.length === 1) return Field.toScriptString(anchor) === value; + const matchLink = (val: string, anchor: Doc) => { + const linkedToExp = (val ?? '').split('='); + if (linkedToExp.length === 1) return Field.toScriptString(anchor) === val; return Field.toScriptString(DocCast(anchor[linkedToExp[0]])) === linkedToExp[1]; }; // prettier-ignore @@ -1493,17 +1498,17 @@ export namespace DocUtils { if (!linkSettings.link_relationship) linkSettings.link_relationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; if (target.doc === Doc.UserDoc()) return undefined; - const makeLink = action((linkDoc: Doc, showPopup?: number[]) => { - if (showPopup) { + const makeLink = action((linkDoc: Doc, showAt?: number[]) => { + if (showAt) { LinkManager.Instance.currentLink = linkDoc; TaskCompletionBox.textDisplayed = 'Link Created'; - TaskCompletionBox.popupX = showPopup[0]; - TaskCompletionBox.popupY = showPopup[1] - 33; + TaskCompletionBox.popupX = showAt[0]; + TaskCompletionBox.popupY = showAt[1] - 33; TaskCompletionBox.taskCompleted = true; - LinkDescriptionPopup.Instance.popupX = showPopup[0]; - LinkDescriptionPopup.Instance.popupY = showPopup[1]; + LinkDescriptionPopup.Instance.popupX = showAt[0]; + LinkDescriptionPopup.Instance.popupY = showAt[1]; LinkDescriptionPopup.Instance.display = true; const rect = document.body.getBoundingClientRect(); @@ -1811,7 +1816,7 @@ export namespace DocUtils { batch.end(); return doc; } - export function findTemplate(templateName: string, type: string, signature: string) { + export function findTemplate(templateName: string, type: string) { let docLayoutTemplate: Opt; const iconViews = DocListCast(Cast(Doc.UserDoc().template_icons, Doc, null)?.data); const templBtns = DocListCast(Cast(Doc.UserDoc().template_buttons, Doc, null)?.data); @@ -1841,7 +1846,7 @@ export namespace DocUtils { const templateName = templateSignature.replace(/\(.*\)/, ''); doc.layout_fieldKey = 'layout_' + (templateSignature || (docLayoutTemplate?.title ?? '')); // eslint-disable-next-line no-param-reassign - docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc.isGroup && doc.transcription ? 'transcription' : doc.type), templateSignature); + docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc.isGroup && doc.transcription ? 'transcription' : doc.type)); const customName = 'layout_' + templateSignature; const _width = NumCast(doc._width); @@ -2166,12 +2171,16 @@ export namespace DocUtils { const docs: { [id: string]: any } = {}; const links: { [id: string]: any } = {}; - Array.from(map.entries()).forEach(f => (docs[f[0]] = f[1])); - Array.from(linkMap.entries()).forEach(l => (links[l[0]] = l[1])); + Array.from(map.entries()).forEach(f => { + docs[f[0]] = f[1]; + }); + Array.from(linkMap.entries()).forEach(l => { + links[l[0]] = l[1]; + }); const jsonDocs = JSON.stringify({ id: clone[Id], docs, links }, decycle(replacer)); const zip = new JSZip(); - var count = 0; + let count = 0; const promArr = Array.from(proms) .filter(url => url?.startsWith('/files')) .map(url => url.replace('/', '')); // window.location.origin)); diff --git a/src/client/util/CalendarManager.tsx b/src/client/util/CalendarManager.tsx index 46aa4d238..8e3936d34 100644 --- a/src/client/util/CalendarManager.tsx +++ b/src/client/util/CalendarManager.tsx @@ -54,7 +54,6 @@ export class CalendarManager extends ObservableReactComponent<{}> { @observable private targetDoc: Doc | undefined = undefined; // the target document @observable private targetDocView: DocumentView | undefined = undefined; // the DocumentView of the target doc @observable private dialogueBoxOpacity = 1; // for the modal - @observable private overlayOpacity = 0.4; // for the modal @observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used @@ -205,14 +204,12 @@ export class CalendarManager extends ObservableReactComponent<{}> { if (docs.length) { docs.forEach(doc => doc && Doc.BrushDoc(doc)); this.dialogueBoxOpacity = 0.1; - this.overlayOpacity = 0.1; } })} onPointerLeave={action(() => { if (docs.length) { docs.forEach(doc => doc && Doc.UnBrushDoc(doc)); this.dialogueBoxOpacity = 1; - this.overlayOpacity = 0.4; } })}> {contents} @@ -352,6 +349,6 @@ export class CalendarManager extends ObservableReactComponent<{}> { } render() { - return ; + return ; } } diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx index 2939ba581..a03c80f2a 100644 --- a/src/client/util/CaptureManager.tsx +++ b/src/client/util/CaptureManager.tsx @@ -1,3 +1,5 @@ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; @@ -13,18 +15,22 @@ import { SelectionManager } from './SelectionManager'; @observer export class CaptureManager extends React.Component<{}> { + // eslint-disable-next-line no-use-before-define public static Instance: CaptureManager; static _settingsStyle = addStyleSheet(); @observable _document: any = undefined; @observable isOpen: boolean = false; // whether the CaptureManager is to be displayed or not. + // eslint-disable-next-line react/sort-comp constructor(props: {}) { super(props); makeObservable(this); CaptureManager.Instance = this; } - public close = action(() => (this.isOpen = false)); + public close = action(() => { + this.isOpen = false; + }); public open = action((doc: Doc) => { this.isOpen = true; this._document = doc; @@ -99,15 +105,15 @@ export class CaptureManager extends React.Component<{}> {
-
+
Conversation Capture
-
+
{this.visibilityContent} {this.linksContent}
- +
{this.closeButtons}
@@ -119,11 +125,10 @@ export class CaptureManager extends React.Component<{}> { ); } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index acbd0c0b9..65dce34a5 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -67,7 +67,7 @@ export class CurrentUserUtils { // initializes experimental advanced template views - slideView, headerView static setupUserDocumentCreatorButtons(doc: Doc, userDocTemplates: Opt) { - const userTemplates = DocListCast(userDocTemplates?.data).filter(doc => !Doc.IsSystem(doc)); + const userTemplates = DocListCast(userDocTemplates?.data).filter(fdoc => !Doc.IsSystem(fdoc)); const reqdOpts:DocumentOptions = { title: "User Tools", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true, hidden: false, _dragOnlyWithinContainer: true, layout_hideContextMenu: true, isSystem: true, _forceActive: true, @@ -87,7 +87,7 @@ export class CurrentUserUtils { { opts: { title: "Open Detail On Right", targetScriptKey: "onChildDoubleClick"}, script: `openDoc(this.doubleClickView.${OpenWhere.addRight})`}]; const reqdClickList = reqdTempOpts.map(opts => { const allOpts = {...reqdClickOpts, ...opts.opts}; - const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(doc => doc.title === opts.opts.title): undefined; + const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(fdoc => fdoc.title === opts.opts.title): undefined; return DocUtils.AssignOpts(clickDoc, allOpts) ?? Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, allOpts),allOpts); }); @@ -110,7 +110,7 @@ export class CurrentUserUtils { const reqdClickList = reqdTempOpts.map(opts => { const title = opts.opts.title?.toString(); const allOpts = {...reqdClickOpts, ...opts.opts}; - const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(doc => doc.title === title): undefined; + const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(fdoc => fdoc.title === title): undefined; const script = ScriptField.MakeScript(opts.script, {heading:Doc.name, checked:"boolean", containingTreeView:Doc.name}); const scriptDoc = Docs.Create.ScriptingDocument(script, allOpts, title) return DocUtils.AssignOpts(clickDoc, allOpts) ?? MakeTemplate(scriptDoc); @@ -130,7 +130,7 @@ export class CurrentUserUtils { { title: "Topic", backgroundColor: "lightblue", icon: "book-open" , _layout_showTitle: "title"}]; const reqdNoteList = [...reqdTempOpts.map(opts => { const reqdOpts = {...opts, isSystem:true, width:200, layout_autoHeight: true, layout_fitWidth: true}; - const noteTemp = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.title === opts.title): undefined; + const noteTemp = tempNotes ? DocListCast(tempNotes.data).find(fdoc => fdoc.title === opts.title): undefined; return DocUtils.AssignOpts(noteTemp, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts)); }), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))]; @@ -249,7 +249,7 @@ export class CurrentUserUtils { Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true, _height: 100, isSystem: true, _text_fontFamily: StrCast(Doc.UserDoc().fontFamily), _text_fontSize: StrCast(Doc.UserDoc().fontSize) }) ], {...opts, title: "Slide View Template"})); const plotlyApi = () => { - let plotly = Doc.MyPublishedDocs.find(doc => doc.title === "@plotly"); + let plotly = Doc.MyPublishedDocs.find(fdoc => fdoc.title === "@plotly"); if (!plotly) { plotly = Docs.Create.TextDocument( `await import("https://cdn.plot.ly/plotly-2.27.0.min.js"); @@ -289,7 +289,7 @@ export class CurrentUserUtils { return slide; } const mermaidsApi = () => { - let mermaids = Doc.MyPublishedDocs.find(doc => doc.title === "@mermaids"); + let mermaids = Doc.MyPublishedDocs.find(fdoc => fdoc.title === "@mermaids"); if (!mermaids) { mermaids = Docs.Create.TextDocument( `const mdef = (await import("https://cdn.jsdelivr.net/npm/mermaid@10.8.0/dist/mermaid.esm.min.mjs")).default; @@ -416,7 +416,7 @@ pie title Minerals in my tap water /// Initalizes the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools static setupCreatorButtons(doc: Doc, dragCreatorDoc?:Doc):Doc { const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => { - const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined; + const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(fdoc => fdoc.title === reqdOpts.title): undefined; const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit, _width: 60, _height: 60, _dragOnlyWithinContainer: true, btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, isSystem: true, @@ -460,7 +460,7 @@ pie title Minerals in my tap water this.setupLeftSidebarPanel(doc); const myLeftSidebarMenu = DocCast(doc[field]); const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, toolTip, hidden, scripts, funcs }) => { - const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined; + const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(fdoc => fdoc.title === title) : undefined; const reqdBtnOpts:DocumentOptions = { title, icon, target, toolTip, hidden, btnType: ButtonType.MenuButton, isSystem: true, undoIgnoreFields: new List(['height', 'data_columnHeaders']), dontRegisterView: true, _width: 60, _height: 60, _dragOnlyWithinContainer: true, @@ -614,7 +614,7 @@ pie title Minerals in my tap water static setupDockedButtons(doc: Doc, field="myDockedBtns") { const dockedBtns = DocCast(doc[field]); const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string|undefined}, funcs?: {[key:string]:string}) => - DocUtils.AssignScripts(DocUtils.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === opts.title), opts) ?? + DocUtils.AssignScripts(DocUtils.AssignOpts(DocListCast(dockedBtns?.data)?.find(fdoc => fdoc.title === opts.title), opts) ?? CurrentUserUtils.createToolButton(opts), scripts, funcs); const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet @@ -780,7 +780,7 @@ pie title Minerals in my tap water childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: !params.scripts?.onClick, linearView_SubMenu: true, linearView_Expandable: true, embedContainer: menuDoc}; - const items = (menuBtnDoc?:Doc) => !menuBtnDoc ? [] : subMenu.map(sub => this.setupContextMenuBtn(sub, menuBtnDoc) ); + const items = (menutBtn?:Doc) => !menutBtn ? [] : subMenu.map(sub => this.setupContextMenuBtn(sub, menutBtn) ); const creator = params.btnType === ButtonType.MultiToggleButton ? this.multiToggleList : this.linearButtonList; const btnDoc = DocUtils.AssignScripts( DocUtils.AssignDocField(menuDoc, StrCast(params.title), (opts) => creator(opts, items(menuBtnDoc)), reqdSubMenuOpts, items(menuBtnDoc)), params.scripts, params.funcs); @@ -802,7 +802,7 @@ pie title Minerals in my tap water Doc.UserDoc().workspaceReplayingState = undefined; const dockedBtns = DocCast(doc[field]); const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string|undefined}, funcs?: {[key:string]:any}) => - DocUtils.AssignScripts(DocUtils.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === opts.title), opts) ?? + DocUtils.AssignScripts(DocUtils.AssignOpts(DocListCast(dockedBtns?.data)?.find(fdoc => fdoc.title === opts.title), opts) ?? CurrentUserUtils.createToolButton(opts), scripts, funcs); const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet @@ -964,7 +964,7 @@ pie title Minerals in my tap water case FInfoFieldType.Doc: opts.fieldValues = new List(options.values as any); break; default: opts.fieldValues = new List(options.values as any); break;// string, pointerEvents, dimUnit, dropActionType } - DocUtils.AssignDocField(infos, pair[0], opts => Doc.assign(new Doc(), OmitKeys(opts,["values"]).omit), opts); + DocUtils.AssignDocField(infos, pair[0], docOpts => Doc.assign(new Doc(), OmitKeys(docOpts,["values"]).omit), opts); } }); } diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index 207d3ea0b..08fd80882 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-use-before-define */ import * as interpreter from 'words-to-numbers'; // @ts-ignore bcz: how are you supposed to include these definitions since dom-speech-recognition isn't a module? import type {} from '@types/dom-speech-recognition'; @@ -6,7 +7,7 @@ import { Doc, Opt } from '../../fields/Doc'; import { List } from '../../fields/List'; import { RichTextField } from '../../fields/RichTextField'; import { listSpec } from '../../fields/Schema'; -import { Cast, CastCtor, DocCast } from '../../fields/Types'; +import { Cast, CastCtor } from '../../fields/Types'; import { AudioField, ImageField } from '../../fields/URLField'; import { DocumentType } from '../documents/DocumentTypes'; import { Docs } from '../documents/Documents'; @@ -14,6 +15,7 @@ import { DictationOverlay } from '../views/DictationOverlay'; import { DocumentView, OpenWhere } from '../views/nodes/DocumentView'; import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; +import { DocData } from '../../fields/DocSymbols'; /** * This namespace provides a singleton instance of a manager that @@ -64,9 +66,10 @@ export namespace DictationManager { export let isListening = false; let isManuallyStopped = false; - let current: string | undefined = undefined; + let current: string | undefined; let sessionResults: string[] = []; + // eslint-disable-next-line new-cap const recognizer: Opt = webkitSpeechRecognition ? new webkitSpeechRecognition() : undefined; export type InterimResultHandler = (results: string) => any; @@ -87,7 +90,7 @@ export namespace DictationManager { let pendingListen: Promise | string | undefined; export const listen = async (options?: Partial) => { - if (pendingListen instanceof Promise) return pendingListen.then(pl => innerListen(options)); + if (pendingListen instanceof Promise) return pendingListen.then(() => innerListen(options)); return innerListen(options); }; const innerListen = async (options?: Partial) => { @@ -150,29 +153,29 @@ export namespace DictationManager { recognizer.start(); - return new Promise((resolve, reject) => { + return new Promise(resolve => { recognizer.onerror = (e: any) => { // e is SpeechRecognitionError but where is that defined? if (!(indefinite && e.error === 'no-speech')) { recognizer.stop(); resolve(e); - //reject(e); } }; recognizer.onresult = (e: SpeechRecognitionEvent) => { current = synthesize(e, intra); - let matchedTerminator: string | undefined; - if (options?.terminators && (matchedTerminator = options.terminators.find(end => (current ? current.trim().toLowerCase().endsWith(end.toLowerCase()) : false)))) { + const matchedTerminator = options?.terminators?.find(end => (current ? current.trim().toLowerCase().endsWith(end.toLowerCase()) : false)); + if (options?.terminators && matchedTerminator) { current = matchedTerminator; recognizer.abort(); return complete(); } !isManuallyStopped && handler?.(current); - //isManuallyStopped && complete(); + // isManuallyStopped && complete() + return undefined; }; - recognizer.onend = (e: Event) => { + recognizer.onend = () => { if (!indefinite || isManuallyStopped) { return complete(); } @@ -182,6 +185,7 @@ export namespace DictationManager { current = undefined; } recognizer.start(); + return undefined; }; const complete = () => { @@ -202,7 +206,7 @@ export namespace DictationManager { }); }; - export const stop = (salvageSession = true) => { + export const stop = (/* salvageSession = true */) => { if (!isListening || !recognizer) { return; } @@ -212,7 +216,7 @@ export namespace DictationManager { }; const synthesize = (e: SpeechRecognitionEvent, delimiter?: string) => { - const results = e.results; + const { results } = e; const transcripts: string[] = []; for (let i = 0; i < results.length; i++) { transcripts.push(results.item(i).item(0).transcript.trim()); @@ -233,22 +237,25 @@ export namespace DictationManager { export const RegisterIndependent = (key: string, value: IndependentEntry) => Independent.set(key, value); export const RegisterDependent = (entry: DependentEntry) => Dependent.push(entry); - export const execute = async (phrase: string) => { - return UndoManager.RunInBatch(async () => { + export const execute = async (phrase: string) => + UndoManager.RunInBatch(async () => { console.log('PHRASE: ' + phrase); const targets = SelectionManager.Views; if (!targets || !targets.length) { - return; + return undefined; } + // eslint-disable-next-line no-param-reassign phrase = phrase.toLowerCase(); const entry = Independent.get(phrase); if (entry) { let success = false; - const restrictTo = entry.restrictTo; + const { restrictTo } = entry; + // eslint-disable-next-line no-restricted-syntax for (const target of targets) { if (!restrictTo || validate(target, restrictTo)) { + // eslint-disable-next-line no-await-in-loop await entry.action(target); success = true; } @@ -256,16 +263,19 @@ export namespace DictationManager { return success; } - for (const entry of Dependent) { - const regex = entry.expression; + // eslint-disable-next-line no-restricted-syntax + for (const depEntry of Dependent) { + const regex = depEntry.expression; const matches = regex.exec(phrase); regex.lastIndex = 0; if (matches !== null) { let success = false; - const restrictTo = entry.restrictTo; + const { restrictTo } = depEntry; + // eslint-disable-next-line no-restricted-syntax for (const target of targets) { if (!restrictTo || validate(target, restrictTo)) { - await entry.action(target, matches); + // eslint-disable-next-line no-await-in-loop + await depEntry.action(target, matches); success = true; } } @@ -275,7 +285,6 @@ export namespace DictationManager { return false; }, 'Execute Command'); - }; const ConstructorMap = new Map([ [DocumentType.COL, listSpec(Doc)], @@ -294,6 +303,7 @@ export namespace DictationManager { }; const validate = (target: DocumentView, types: DocumentType[]) => { + // eslint-disable-next-line no-restricted-syntax for (const type of types) { if (tryCast(target, type)) { return true; @@ -318,7 +328,9 @@ export namespace DictationManager { [ 'clear', { - action: (target: DocumentView) => (Doc.GetProto(target.Document).data = new List()), + action: (target: DocumentView) => { + Doc.GetProto(target.Document).data = new List(); + }, restrictTo: [DocumentType.COL], }, ], @@ -328,7 +340,7 @@ export namespace DictationManager { { action: (target: DocumentView) => { const newBox = Docs.Create.TextDocument('', { _width: 400, _height: 200, title: 'My Outline', _layout_autoHeight: true }); - const proto = DocCast(newBox.proto); + const proto = newBox[DocData]; const prompt = 'Press alt + r to start dictating here...'; const head = 3; const anchor = head + prompt.length; @@ -341,7 +353,7 @@ export namespace DictationManager { ], ]); - const Dependent = new Array( + const Dependent = [ { expression: /create (\w+) documents of type (image|nested collection)/g, action: (target: DocumentView, matches: RegExpExecArray) => { @@ -361,6 +373,7 @@ export namespace DictationManager { case 'nested collection': created = Docs.Create.FreeformDocument([], {}); break; + default: } created && Doc.AddDocToList(dataDoc, fieldKey, created); } @@ -375,7 +388,7 @@ export namespace DictationManager { mode && (target.Document._type_collection = mode); }, restrictTo: [DocumentType.COL], - } - ); + }, + ]; } } diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index 894583711..c703c3f98 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -1,4 +1,7 @@ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Button, IconButton, Size, Type } from 'browndash-components'; import { action, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -8,7 +11,6 @@ import { StrCast } from '../../fields/Types'; import { MainViewModal } from '../views/MainViewModal'; import { GroupManager, UserOptions } from './GroupManager'; import './GroupMemberView.scss'; -import { Button, IconButton, Size, Type } from 'browndash-components'; import { SettingsManager } from './SettingsManager'; interface GroupMemberViewProps { @@ -38,20 +40,23 @@ export class GroupMemberView extends React.Component { className="group-title" style={{ marginLeft: !hasEditAccess ? '-14%' : 0 }} value={StrCast(this.group.title || this.group.groupName)} - onChange={e => (this.group.title = e.currentTarget.value)} - disabled={!hasEditAccess}> -
-
{GroupManager.Instance.hasEditAccess(this.group) ? (
(this.onDataDoc = e.target.checked)} ref={this.checkRef} style={{ margin: '0 10px 0 15px' }} type="checkbox" title={'Add to Data Document?'} checked={this.onDataDoc} /> -
- ''} oneLine={true} /> + { + this.onDataDoc = e.target.checked; + }} + ref={this.checkRef} + style={{ margin: '0 10px 0 15px' }} + type="checkbox" + title="Add to Data Document?" + checked={this.onDataDoc} + /> +
+ ''} oneLine />
-
- ''} oneLine={true} /> +
+ ''} oneLine />
-
this.props.remove(this)} title={'Delete Entry'}> +
this.props.remove(this)} title="Delete Entry"> , sourceDoc: Doc, finished?: () => void, traverseBacklink?: boolean) { const getView = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(DocCast(doc.layout_unrendered ? doc.annotationOn : doc)); - const isAnchor = (sourceDoc: Doc, anchor: Doc) => Doc.AreProtosEqual(anchor, sourceDoc) || Doc.AreProtosEqual(anchor.annotationOn as Doc, sourceDoc); + const isAnchor = (source: Doc, anchor: Doc) => Doc.AreProtosEqual(anchor, source) || Doc.AreProtosEqual(anchor.annotationOn as Doc, source); const linkDocs = link ? [link] : LinkManager.Links(sourceDoc); const fwdLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_1 as Doc)); // link docs where 'sourceDoc' is link_anchor_1 const backLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_2 as Doc)); // link docs where 'sourceDoc' is link_anchor_2 diff --git a/src/client/util/PingManager.ts b/src/client/util/PingManager.ts index 7638e2ce0..e5e69c5ac 100644 --- a/src/client/util/PingManager.ts +++ b/src/client/util/PingManager.ts @@ -1,8 +1,10 @@ import { action, makeObservable, observable, runInAction } from 'mobx'; import { Networking } from '../Network'; import { CurrentUserUtils } from './CurrentUserUtils'; + export class PingManager { // create static instance and getter for global use + // eslint-disable-next-line no-use-before-define @observable static _instance: PingManager; @observable IsBeating = true; static get Instance(): PingManager { @@ -29,7 +31,9 @@ export class PingManager { sendPing = async (): Promise => { try { const res = await Networking.PostToServer('/ping', { date: new Date() }); - runInAction(() => (CurrentUserUtils.ServerVersion = res.message)); + runInAction(() => { + CurrentUserUtils.ServerVersion = res.message; + }); !this.IsBeating && this.setIsBeating(true); } catch { if (this.IsBeating) { diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx index 35b1579df..05fb849fd 100644 --- a/src/client/util/RTFMarkup.tsx +++ b/src/client/util/RTFMarkup.tsx @@ -6,23 +6,16 @@ import { SettingsManager } from './SettingsManager'; @observer export class RTFMarkup extends React.Component<{}> { + // eslint-disable-next-line no-use-before-define static Instance: RTFMarkup; @observable private isOpen = false; // whether the SharingManager modal is open or not - @action - public open = () => (this.isOpen = true); - - @action - public close = () => (this.isOpen = false); - constructor(props: {}) { super(props); makeObservable(this); RTFMarkup.Instance = this; } - @observable _stats: { [key: string]: any } | undefined = undefined; - /** * @returns the main interface of the SharingManager. */ @@ -30,11 +23,11 @@ export class RTFMarkup extends React.Component<{}> { return (

- {`(@wiki:phrase)`} + (@wiki:phrase) {` display wikipedia page for entered text (terminate with carriage return)`}

- {`(( any text ))`} + (( any text )) {` submit text to Chat GPT to have results appended afterward`}

@@ -129,13 +122,23 @@ export class RTFMarkup extends React.Component<{}> { ); } + @action + public open = () => { + this.isOpen = true; + }; + + @action + public close = () => { + this.isOpen = false; + }; + render() { return ( ); diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts index b881f18b4..2c8fdf483 100644 --- a/src/client/util/ReplayMovements.ts +++ b/src/client/util/ReplayMovements.ts @@ -14,6 +14,7 @@ export class ReplayMovements { private isPlaying: boolean; // create static instance and getter for global use + // eslint-disable-next-line no-use-before-define @observable static _instance: ReplayMovements; static get Instance(): ReplayMovements { return ReplayMovements._instance; @@ -90,7 +91,7 @@ export class ReplayMovements { loadPresentation = (presentation: Presentation) => { const { movements } = presentation; if (movements === null) { - throw '[recordingApi.ts] followMovements() failed: no presentation data'; + throw new Error('[recordingApi.ts] followMovements() failed: no presentation data'); } movements.forEach((movement, i) => { @@ -106,9 +107,7 @@ export class ReplayMovements { // returns undefined if the docView isn't open on the screen getCollectionFFView = (doc: Doc) => { const isInView = DocumentManager.Instance.getDocumentView(doc); - if (isInView) { - return isInView.ComponentView as CollectionFreeFormView; - } + return isInView?.ComponentView as CollectionFreeFormView; }; // will open the doc in a tab then return the CollectionFFView that holds it @@ -136,9 +135,9 @@ export class ReplayMovements { if (movements === null) return new Map(); // generate a set of all unique docIds const docIdtoFirstMove = new Map(); - for (const move of movements) { + movements.forEach(move => { if (!docIdtoFirstMove.has(move.doc)) docIdtoFirstMove.set(move.doc, move); - } + }); return docIdtoFirstMove; }; @@ -151,10 +150,10 @@ export class ReplayMovements { // console.info('playMovements', presentation, timeViewed, docIdtoDoc); if (presentation.movements === null || presentation.movements.length === 0) { - //|| this.playFFView === null) { - return new Error('[recordingApi.ts] followMovements() failed: no presentation data'); + // || this.playFFView === null) { + return '[recordingApi.ts] followMovements() failed: no presentation data'; } - if (this.isPlaying) return; + if (this.isPlaying) return undefined; this.isPlaying = true; Doc.UserDoc().presentationMode = 'watching'; @@ -170,10 +169,10 @@ export class ReplayMovements { // for the open tabs, set it to the first move const docIdtoFirstMove = this.getFirstMovements(filteredMovements); - for (const [doc, firstMove] of docIdtoFirstMove) { + Array.from(docIdtoFirstMove).forEach(([doc, firstMove]) => { const colFFView = this.getCollectionFFView(doc); if (colFFView) this.zoomAndPan(firstMove, colFFView); - } + }); }; handleFirstMovements(); @@ -197,5 +196,6 @@ export class ReplayMovements { } }, timeDiff); }); + return undefined; }; } diff --git a/src/client/util/ScriptManager.ts b/src/client/util/ScriptManager.ts index 87509f2ea..9158f6c0b 100644 --- a/src/client/util/ScriptManager.ts +++ b/src/client/util/ScriptManager.ts @@ -7,8 +7,10 @@ import { ScriptingGlobals } from './ScriptingGlobals'; export class ScriptManager { static _initialized = false; + // eslint-disable-next-line no-use-before-define private static _instance: ScriptManager; public static get Instance(): ScriptManager { + // eslint-disable-next-line no-return-assign return this._instance || (this._instance = new this()); } private constructor() { @@ -58,14 +60,16 @@ export class ScriptManager { const params = Cast(scriptDoc['data-params'], listSpec('string'), []); const paramNames = params.reduce((o: string, p: string) => { + let out = o; if (params.indexOf(p) === params.length - 1) { - o = o + p.split(':')[0].trim(); + out += p.split(':')[0].trim(); } else { - o = o + p.split(':')[0].trim() + ','; + out += p.split(':')[0].trim() + ','; } - return o; + return out; }, '' as string); + // eslint-disable-next-line no-new-func const f = new Function(paramNames, StrCast(scriptDoc.script)); Object.defineProperty(f, 'name', { value: StrCast(scriptDoc.name), writable: false }); diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index de5e8b92e..f7d7ba6a4 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -124,7 +124,7 @@ class ScriptingCompilerHost { return 'node_modules/typescript/lib/lib.d.ts'; // No idea what this means... } writeFile(fileName: string, content: string) { - const file = this.files.find(file => file.fileName === fileName); + const file = this.files.find(f => f.fileName === fileName); if (file) { file.content = content; } else { @@ -147,7 +147,7 @@ class ScriptingCompilerHost { return this.files.some(file => file.fileName === fileName); } readFile(fileName: string): string | undefined { - const file = this.files.find(file => file.fileName === fileName); + const file = this.files.find(f => f.fileName === fileName); if (file) { return file.content; } diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 36b926053..1328e90e9 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -11,6 +11,7 @@ import { ScriptingGlobals } from './ScriptingGlobals'; import { UndoManager } from './UndoManager'; export class SelectionManager { + // eslint-disable-next-line no-use-before-define private static _manager: SelectionManager; private static get Instance() { return SelectionManager._manager ?? new SelectionManager(); @@ -61,7 +62,9 @@ export class SelectionManager { dv.IsSelected = false; dv._props.whenChildContentsActiveChanged(false); }); - runInAction(() => (this.Instance.SelectedViews.length = 0)); + runInAction(() => { + this.Instance.SelectedViews.length = 0; + }); if (found) this.SelectView(found, false); }; @@ -71,17 +74,20 @@ export class SelectionManager { public static get Docs() { return this.Instance.SelectedViews.map(dv => dv.Document).filter(doc => doc?._type_collection !== CollectionViewType.Docking); } // prettier-ignore } +// eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, expertMode: boolean, checkContext?: boolean) { if (Doc.noviceMode && expertMode) return false; if (type === 'tab') { return SelectionManager.Views.lastElement()?._props.renderDepth === 0; } - let selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(SelectionManager.SelectedSchemaDoc ?? SelectionManager.Docs.lastElement()); + const selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(SelectionManager.SelectedSchemaDoc ?? SelectionManager.Docs.lastElement()); return selected?.type === type || selected?.type_collection === type || !type; }); +// eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function deselectAll() { SelectionManager.DeselectAll(); }); +// eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); @@ -89,17 +95,19 @@ ScriptingGlobals.add(function undo() { export function ShowUndoStack() { SelectionManager.DeselectAll(); - var buffer = ''; + let buffer = ''; UndoManager.undoStack.forEach((batch, i) => { buffer += 'Batch => ' + UndoManager.undoStackNames[i] + '\n'; - ///batch.forEach(undo => (buffer += ' ' + undo.prop + '\n')); + // /batch.forEach(undo => (buffer += ' ' + undo.prop + '\n')); }); alert(buffer); } +// eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function redo() { SelectionManager.DeselectAll(); return UndoManager.Redo(); }); +// eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) { const docs = SelectionManager.Views.map(dv => dv.Document).filter( d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)) diff --git a/src/client/util/ServerStats.tsx b/src/client/util/ServerStats.tsx index c8df9182d..891561245 100644 --- a/src/client/util/ServerStats.tsx +++ b/src/client/util/ServerStats.tsx @@ -1,44 +1,28 @@ -import { action, computed, makeObservable, observable } from 'mobx'; +import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { MainViewModal } from '../views/MainViewModal'; import './SharingManager.scss'; import { PingManager } from './PingManager'; -import { StrCast } from '../../fields/Types'; -import { Doc } from '../../fields/Doc'; import { SettingsManager } from './SettingsManager'; @observer export class ServerStats extends React.Component<{}> { + // eslint-disable-next-line no-use-before-define public static Instance: ServerStats; @observable private isOpen = false; // whether the SharingManager modal is open or not + @observable _stats: { [key: string]: any } | undefined = undefined; // private get linkVisible() { // return this.targetDoc ? this.targetDoc["acl-" + PublicKey] !== SharingPermissions.None : false; // } - @action - public open = async () => { - /** - * Populates the list of users. - */ - fetch('/stats').then((res: Response) => res.text().then(action(stats => (this._stats = JSON.parse(stats))))); - - this.isOpen = true; - }; - - public close = action(() => { - this.isOpen = false; - }); - constructor(props: {}) { super(props); makeObservable(this); ServerStats.Instance = this; } - @observable _stats: { [key: string]: any } | undefined = undefined; - /** * @returns the main interface of the SharingManager. */ @@ -63,7 +47,28 @@ export class ServerStats extends React.Component<{}> { ); } + // eslint-disable-next-line react/sort-comp + public close = action(() => { + this.isOpen = false; + }); + public open = async () => { + /** + * Populates the list of users. + */ + fetch('/stats').then((res: Response) => + res.text().then( + action(stats => { + this._stats = JSON.parse(stats); + }) + ) + ); + + runInAction(() => { + this.isOpen = true; + }); + }; + render() { - return ; + return ; } } diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 6676e4e03..5b4ac5aff 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -78,7 +78,6 @@ export class SharingManager extends React.Component<{}> { @observable private targetDocView: DocumentView | undefined = undefined; // the DocumentView of the document being shared // @observable private copied = false; @observable private dialogueBoxOpacity = 1; // for the modal - @observable private overlayOpacity = 0.4; // for the modal @observable private selectedUsers: UserOptions[] | null = null; // users (individuals/groups) selected to share with @observable private permissions: SharingPermissions = SharingPermissions.Edit; // the permission with which to share with other users @observable private individualSort: 'ascending' | 'descending' | 'none' = 'none'; // sorting options for the list of individuals @@ -661,14 +660,12 @@ export class SharingManager extends React.Component<{}> { if (docs.length) { docs.forEach(doc => doc && Doc.BrushDoc(doc)); this.dialogueBoxOpacity = 0.1; - this.overlayOpacity = 0.1; } })} onPointerLeave={action(() => { if (docs.length) { docs.forEach(doc => doc && Doc.UnBrushDoc(doc)); this.dialogueBoxOpacity = 1; - this.overlayOpacity = 0.4; } })}> {contents} @@ -742,6 +739,6 @@ export class SharingManager extends React.Component<{}> { } render() { - return ; + return ; } } diff --git a/src/client/util/TrackMovements.ts b/src/client/util/TrackMovements.ts index f9c2d522f..25a3c9ad8 100644 --- a/src/client/util/TrackMovements.ts +++ b/src/client/util/TrackMovements.ts @@ -1,8 +1,7 @@ -import { IReactionDisposer, makeObservable, observable, observe, reaction } from 'mobx'; +import { IReactionDisposer, makeObservable, observable, reaction } from 'mobx'; import { NumCast } from '../../fields/Types'; import { Doc, DocListCast } from '../../fields/Doc'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; -import { Id } from '../../fields/FieldSymbols'; import { CollectionViewType } from '../documents/DocumentTypes'; export type Movement = { @@ -33,6 +32,7 @@ export class TrackMovements { private tabChangeDisposeFunc: IReactionDisposer | null; // create static instance and getter for global use + // eslint-disable-next-line no-use-before-define @observable static _instance: TrackMovements; static get Instance(): TrackMovements { return TrackMovements._instance; @@ -92,12 +92,13 @@ export class TrackMovements { // so that the size comparisons are correct, we must filter to only the FFViews const isFFView = (doc: Doc) => doc && doc._type_collection === CollectionViewType.Freeform; const tabbedFFViews = new Set(); - for (const DashDoc of tabbedDocs) { + tabbedDocs.forEach(DashDoc => { if (isFFView(DashDoc)) tabbedFFViews.add(DashDoc); - } + }); // new tab was added - need to add it if (tabbedFFViews.size > this.recordingFFViews.size) { + // eslint-disable-next-line no-restricted-syntax for (const DashDoc of tabbedDocs) { if (!this.recordingFFViews.has(DashDoc)) { if (isFFView(DashDoc)) { @@ -111,6 +112,7 @@ export class TrackMovements { } // tab was removed - need to remove it from recordingFFViews else if (tabbedFFViews.size < this.recordingFFViews.size) { + // eslint-disable-next-line no-restricted-syntax for (const [doc] of this.recordingFFViews) { if (!tabbedFFViews.has(doc)) { this.removeRecordingFFView(doc); @@ -208,11 +210,11 @@ export class TrackMovements { return; } - for (const [id, disposeFunc] of this.recordingFFViews) { + Array.from(this.recordingFFViews).forEach(([id, disposeFunc]) => { // console.info('calling dispose func : docId', id); disposeFunc(); - this.recordingFFViews.delete(id); - } + this.recordingFFViews?.delete(id); + }); }; private trackMovement = (panX: number, panY: number, doc: Doc, scale: number = 0) => { @@ -241,9 +243,9 @@ export class TrackMovements { // method that concatenates an array of presentatations into one public concatPresentations = (presentations: Presentation[]): Presentation => { // these three will lead to the combined presentation - let combinedMovements: Movement[] = []; + const combinedMovements: Movement[] = []; let sumTime = 0; - let combinedMetas: any[] = []; + const combinedMetas: any[] = []; presentations.forEach(presentation => { const { movements, totalTime, meta } = presentation; @@ -251,9 +253,7 @@ export class TrackMovements { // update movements if they had one if (movements) { // add the summed time to the movements - const addedTimeMovements = movements.map(move => { - return { ...move, time: move.time + sumTime }; - }); + const addedTimeMovements = movements.map(move => ({ ...move, time: move.time + sumTime })); // concat the movements already in the combined presentation with these new ones combinedMovements.push(...addedTimeMovements); } diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts index dca37c960..1a07dd6ae 100644 --- a/src/client/util/Transform.ts +++ b/src/client/util/Transform.ts @@ -116,20 +116,14 @@ export class Transform { preTransformed = (transform: Transform): Transform => this.copy().preTransform(transform); - transformPoint = (x: number, y: number): [number, number] => { - x *= this._scale; - x += this._translateX; - y *= this._scale; - y += this._translateY; - return [x, y]; - }; + transformPoint = (x: number, y: number): [number, number] => [x * this._scale + this._translateX, y * this._scale + this._translateY]; transformDirection = (x: number, y: number): [number, number] => [x * this._scale, y * this._scale]; transformBounds(x: number, y: number, width: number, height: number): { x: number; y: number; width: number; height: number } { - [x, y] = this.transformPoint(x, y); - [width, height] = this.transformDirection(width, height); - return { x, y, width, height }; + const [tx, ty] = this.transformPoint(x, y); + const [twidth, theight] = this.transformDirection(width, height); + return { x: tx, y: ty, width: twidth, height: theight }; } inverse = () => new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale, -this._rotate); diff --git a/src/client/util/TypedEvent.ts b/src/client/util/TypedEvent.ts index 90fd299c1..9ef2aa8d7 100644 --- a/src/client/util/TypedEvent.ts +++ b/src/client/util/TypedEvent.ts @@ -14,27 +14,27 @@ export class TypedEvent { on = (listener: Listener): Disposable => { this.listeners.push(listener); return { - dispose: () => this.off(listener) + dispose: () => this.off(listener), }; - } + }; once = (listener: Listener): void => { this.listenersOncer.push(listener); - } + }; off = (listener: Listener) => { const callbackIndex = this.listeners.indexOf(listener); if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1); - } + }; emit = (event: T) => { /** Update any general listeners */ - this.listeners.forEach((listener) => listener(event)); + this.listeners.forEach(listener => listener(event)); /** Clear the `once` queue */ - this.listenersOncer.forEach((listener) => listener(event)); + this.listenersOncer.forEach(listener => listener(event)); this.listenersOncer = []; - } + }; - pipe = (te: TypedEvent): Disposable => this.on((e) => te.emit(e)); -} \ No newline at end of file + pipe = (te: TypedEvent): Disposable => this.on(e => te.emit(e)); +} diff --git a/src/client/util/bezierFit.ts b/src/client/util/bezierFit.ts index 6bbf55e5a..d6f3f2340 100644 --- a/src/client/util/bezierFit.ts +++ b/src/client/util/bezierFit.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-use-before-define */ /* eslint-disable prefer-destructuring */ /* eslint-disable no-param-reassign */ /* eslint-disable camelcase */ @@ -242,6 +243,7 @@ function splitCubic(p: Point[], t: number, left: Point[], right: Point[]) { * is robust: a near-tangential intersection will yield zero or two * intersections. */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars function recursively_intersect(a: Point[], t0: number, t1: number, deptha: number, b: Point[], u0: number, u1: number, depthb: number, parameters: number[][]) { if (deptha > 0) { const a1 = new Array(4); @@ -538,8 +540,8 @@ function FitCubic(d: Point[], first: number, last: number, tHat1: Point, tHat2: for (let i = 0; i < maxIterations; i++) { const uPrime = ReparameterizeBezier(d, first, last, u, bezCurve); // Improved parameter values GenerateBezier(d, first, last, uPrime, tHat1, tHat2, bezCurve); - const { maxError } = ComputeMaxError(d, first, last, bezCurve, uPrime); - if (maxError < error) { + const { maxError: maximumError } = ComputeMaxError(d, first, last, bezCurve, uPrime); + if (maximumError < error) { result.push(bezCurve[1]); result.push(bezCurve[2]); result.push(bezCurve[3]); diff --git a/src/client/util/reportManager/ReportManagerComponents.tsx b/src/client/util/reportManager/ReportManagerComponents.tsx index 1e226bf6d..cecebc648 100644 --- a/src/client/util/reportManager/ReportManagerComponents.tsx +++ b/src/client/util/reportManager/ReportManagerComponents.tsx @@ -1,9 +1,15 @@ +/* eslint-disable react/require-default-props */ +/* eslint-disable prefer-destructuring */ +/* eslint-disable jsx-a11y/label-has-associated-control */ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable no-use-before-define */ import * as React from 'react'; -import { Issue } from './reportManagerSchema'; -import { darkColors, dashBlue, getLabelColors, isDarkMode, lightColors } from './reportManagerUtils'; import ReactMarkdown from 'react-markdown'; import rehypeRaw from 'rehype-raw'; import remarkGfm from 'remark-gfm'; +import { darkColors, dashBlue, getLabelColors, isDarkMode, lightColors } from './reportManagerUtils'; +import { Issue } from './reportManagerSchema'; import { StrCast } from '../../../fields/Types'; import { Doc } from '../../../fields/Doc'; @@ -18,7 +24,7 @@ interface FilterProps { } // filter ui for issues (horizontal list of tags) -export const Filter = ({ items, activeValue, setActiveValue }: FilterProps) => { +export function Filter({ items, activeValue, setActiveValue }: FilterProps) { // establishing theme const darkMode = isDarkMode(StrCast(Doc.UserDoc().userBackgroundColor)); const colors = darkMode ? darkColors : lightColors; @@ -28,7 +34,7 @@ export const Filter = ({ items, activeValue, setActiveValue }: return (

{ setActiveValue(null); }} @@ -38,25 +44,23 @@ export const Filter = ({ items, activeValue, setActiveValue }: borderColor={activeValue === null ? StrCast(Doc.UserDoc().userColor) : colors.border} border /> - {items.map(item => { - return ( - { - setActiveValue(item); - }} - fontSize="12px" - backgroundColor={activeValue === item ? StrCast(Doc.UserDoc().userColor) : 'transparent'} - color={activeValue === item ? activeTagTextColor : colors.textGrey} - border - borderColor={activeValue === item ? StrCast(Doc.UserDoc().userColor) : colors.border} - /> - ); - })} + {items.map(item => ( + { + setActiveValue(item); + }} + fontSize="12px" + backgroundColor={activeValue === item ? StrCast(Doc.UserDoc().userColor) : 'transparent'} + color={activeValue === item ? activeTagTextColor : colors.textGrey} + border + borderColor={activeValue === item ? StrCast(Doc.UserDoc().userColor) : colors.border} + /> + ))}
); -}; +} interface IssueCardProps { issue: Issue; @@ -64,7 +68,7 @@ interface IssueCardProps { } // Component for the issue cards list on the left -export const IssueCard = ({ issue, onSelect }: IssueCardProps) => { +export function IssueCard({ issue, onSelect }: IssueCardProps) { const [textColor, setTextColor] = React.useState(''); const [bgColor, setBgColor] = React.useState('transparent'); const [borderColor, setBorderColor] = React.useState('transparent'); @@ -103,14 +107,14 @@ export const IssueCard = ({ issue, onSelect }: IssueCardProps) => {

{issue.title}

); -}; +} interface IssueViewProps { issue: Issue; } // Detailed issue view that displays on the right -export const IssueView = ({ issue }: IssueViewProps) => { +export function IssueView({ issue }: IssueViewProps) { const [issueBody, setIssueBody] = React.useState(''); // Parses the issue body into a formatted markdown (main functionality is replacing urls with tags) @@ -127,15 +131,16 @@ export const IssueView = ({ issue }: IssueViewProps) => { parts.map(async part => { if (imgTagRegex.test(part) || videoTagRegex.test(part) || audioTagRegex.test(part)) { return `\n${await parseFileTag(part)}\n`; - } else if (fileRegex.test(part)) { + } + if (fileRegex.test(part)) { const tag = await parseDashFiles(part); return tag; - } else if (localRegex.test(part)) { + } + if (localRegex.test(part)) { const tag = await parseLocalFiles(part); return tag; - } else { - return part; } + return part; }) ); @@ -143,7 +148,7 @@ export const IssueView = ({ issue }: IssueViewProps) => { }; // Extracts the src from an image tag and either returns the raw url if not accessible or a new image tag - const parseFileTag = async (tag: string): Promise => { + const parseFileTag = async (tag: string): Promise => { const regex = /src="([^"]+)"/; let url = ''; const match = tag.match(regex); @@ -160,18 +165,19 @@ export const IssueView = ({ issue }: IssueViewProps) => { case '.png': case '.jpeg': case '.gif': - return await getDisplayedFile(url, 'image'); + return getDisplayedFile(url, 'image'); // video case '.mp4': case '.mpeg': case '.webm': case '.mov': - return await getDisplayedFile(url, 'video'); - //audio + return getDisplayedFile(url, 'video'); + // audio case '.mp3': case '.wav': case '.ogg': - return await getDisplayedFile(url, 'audio'); + return getDisplayedFile(url, 'audio'); + default: } return tag; }; @@ -183,14 +189,15 @@ export const IssueView = ({ issue }: IssueViewProps) => { const dashAudioRegex = /https:\/\/browndash\.com\/files[/\\]audio/; if (dashImgRegex.test(url)) { - return await getDisplayedFile(url, 'image'); - } else if (dashVideoRegex.test(url)) { - return await getDisplayedFile(url, 'video'); - } else if (dashAudioRegex.test(url)) { - return await getDisplayedFile(url, 'audio'); - } else { - return url; + return getDisplayedFile(url, 'image'); } + if (dashVideoRegex.test(url)) { + return getDisplayedFile(url, 'video'); + } + if (dashAudioRegex.test(url)) { + return getDisplayedFile(url, 'audio'); + } + return url; }; // Returns the corresponding HTML tag for a src url @@ -200,31 +207,37 @@ export const IssueView = ({ issue }: IssueViewProps) => { const dashAudioRegex = /http:\/\/localhost:1050\.com\/files[/\\]audio/; if (imgRegex.test(url)) { - return await getDisplayedFile(url, 'image'); - } else if (dashVideoRegex.test(url)) { - return await getDisplayedFile(url, 'video'); - } else if (dashAudioRegex.test(url)) { - return await getDisplayedFile(url, 'audio'); - } else { - return url; + return getDisplayedFile(url, 'image'); + } + if (dashVideoRegex.test(url)) { + return getDisplayedFile(url, 'video'); + } + if (dashAudioRegex.test(url)) { + return getDisplayedFile(url, 'audio'); } + return url; }; - const getDisplayedFile = async (url: string, fileType: 'image' | 'video' | 'audio'): Promise => { + const getDisplayedFile = async (url: string, fileType: 'image' | 'video' | 'audio'): Promise => { switch (fileType) { - case 'image': + case 'image': { const imgValid = await isImgValid(url); if (!imgValid) return `\n${url} (This image could not be loaded)\n`; return `\n${url}\nIssue asset\n`; - case 'video': + } + case 'video': { const videoValid = await isVideoValid(url); if (!videoValid) return `\n${url} (This video could not be loaded)\n`; return `\n${url}\n