aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/BranchingTrailManager.tsx14
-rw-r--r--src/client/util/CalendarManager.tsx44
-rw-r--r--src/client/util/CaptureManager.tsx10
-rw-r--r--src/client/util/CurrentUserUtils.ts122
-rw-r--r--src/client/util/DictationManager.ts39
-rw-r--r--src/client/util/DocumentManager.ts52
-rw-r--r--src/client/util/DragManager.ts102
-rw-r--r--src/client/util/DropConverter.ts13
-rw-r--r--src/client/util/GroupManager.tsx22
-rw-r--r--src/client/util/GroupMemberView.tsx2
-rw-r--r--src/client/util/History.ts6
-rw-r--r--src/client/util/Import & Export/ImageUtils.ts1
-rw-r--r--src/client/util/Import & Export/ImportMetadataEntry.tsx2
-rw-r--r--src/client/util/InteractionUtils.tsx21
-rw-r--r--src/client/util/LinkFollower.ts4
-rw-r--r--src/client/util/LinkManager.ts43
-rw-r--r--src/client/util/ProsemirrorCopy/prompt.js179
-rw-r--r--src/client/util/RTFMarkup.tsx4
-rw-r--r--src/client/util/ReplayMovements.ts15
-rw-r--r--src/client/util/Scripting.ts38
-rw-r--r--src/client/util/ScriptingGlobals.ts42
-rw-r--r--src/client/util/SearchUtil.ts1
-rw-r--r--src/client/util/SelectionManager.ts2
-rw-r--r--src/client/util/SerializationHelper.ts35
-rw-r--r--src/client/util/ServerStats.tsx19
-rw-r--r--src/client/util/SettingsManager.tsx13
-rw-r--r--src/client/util/SharingManager.tsx23
-rw-r--r--src/client/util/SnappingManager.ts2
-rw-r--r--src/client/util/TrackMovements.ts8
-rw-r--r--src/client/util/TypedEvent.ts2
-rw-r--r--src/client/util/UndoManager.ts33
-rw-r--r--src/client/util/reportManager/ReportManager.scss12
-rw-r--r--src/client/util/reportManager/ReportManager.tsx6
-rw-r--r--src/client/util/reportManager/ReportManagerComponents.tsx11
-rw-r--r--src/client/util/reportManager/reportManagerSchema.ts40
-rw-r--r--src/client/util/reportManager/reportManagerUtils.ts16
-rw-r--r--src/client/util/request-image-size.ts45
-rw-r--r--src/client/util/type_decls.d224
38 files changed, 438 insertions, 829 deletions
diff --git a/src/client/util/BranchingTrailManager.tsx b/src/client/util/BranchingTrailManager.tsx
index 119d103c5..65336812d 100644
--- a/src/client/util/BranchingTrailManager.tsx
+++ b/src/client/util/BranchingTrailManager.tsx
@@ -15,18 +15,18 @@ export class BranchingTrailManager extends React.Component {
public static Instance: BranchingTrailManager;
// stack of the history
- @observable private slideHistoryStack: String[] = [];
- @observable private containsSet: Set<String> = new Set<String>();
+ @observable private slideHistoryStack: string[] = [];
+ @observable private containsSet: Set<string> = new Set<string>();
// docId to Doc map
- @observable private docIdToDocMap: Map<String, Doc> = new Map<String, Doc>();
+ @observable private docIdToDocMap: Map<string, Doc> = new Map<string, Doc>();
// prev pres to copmare with
- @observable private prevPresId: String | null = null;
- @action setPrevPres = action((newId: String | null) => {
+ @observable private prevPresId: string | null = null;
+ @action setPrevPres = action((newId: string | null) => {
this.prevPresId = newId;
});
- constructor(props: any) {
+ constructor(props: object) {
super(props);
makeObservable(this);
if (!BranchingTrailManager.Instance) {
@@ -48,7 +48,7 @@ export class BranchingTrailManager extends React.Component {
// Doc.AddToMyOverlay(hi);
};
- @action setSlideHistoryStack = action((newArr: String[]) => {
+ @action setSlideHistoryStack = action((newArr: string[]) => {
this.slideHistoryStack = newArr;
});
diff --git a/src/client/util/CalendarManager.tsx b/src/client/util/CalendarManager.tsx
index 77cf80151..d0cd69273 100644
--- a/src/client/util/CalendarManager.tsx
+++ b/src/client/util/CalendarManager.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
import { DateRangePicker, Provider, defaultTheme } from '@adobe/react-spectrum';
import { IconLookup, faPlus } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -19,6 +17,7 @@ import { DocumentView } from '../views/nodes/DocumentView';
import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
import './CalendarManager.scss';
import { SnappingManager } from './SnappingManager';
+import { CalendarDate, DateValue } from '@internationalized/date';
// import 'react-date-range/dist/styles.css';
// import 'react-date-range/dist/theme/default.css';
@@ -29,7 +28,7 @@ interface CalendarSelectOptions {
value: string;
}
-const formatCalendarDateToString = (calendarDate: any) => {
+const formatCalendarDateToString = (calendarDate: DateValue) => {
console.log('Formatting the following date: ', calendarDate);
const date = new Date(calendarDate.year, calendarDate.month - 1, calendarDate.day);
console.log(typeof date);
@@ -44,7 +43,7 @@ const formatCalendarDateToString = (calendarDate: any) => {
// TODO: For a doc already in a calendar: give option to edit date range, delete from calendar
@observer
-export class CalendarManager extends ObservableReactComponent<{}> {
+export class CalendarManager extends ObservableReactComponent<object> {
// eslint-disable-next-line no-use-before-define
public static Instance: CalendarManager;
@observable private isOpen = false;
@@ -101,7 +100,7 @@ export class CalendarManager extends ObservableReactComponent<{}> {
this.layoutDocAcls = false;
});
- constructor(props: {}) {
+ constructor(props: object) {
super(props);
CalendarManager.Instance = this;
makeObservable(this);
@@ -110,15 +109,6 @@ export class CalendarManager extends ObservableReactComponent<{}> {
componentDidMount(): void {}
@action
- handleSelectChange = (option: any) => {
- if (option) {
- const selectOpt = option as CalendarSelectOptions;
- this.selectedExistingCalendarOption = selectOpt;
- this.calendarName = selectOpt.value; // or label
- }
- };
-
- @action
handleCalendarTitleChange = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
console.log('Existing calendars: ', this.existingCalendars);
this.calendarName = event.target.value;
@@ -212,15 +202,13 @@ export class CalendarManager extends ObservableReactComponent<{}> {
};
@observable
- selectedDateRange: any = [
- {
- start: new Date(),
- end: new Date(),
- },
- ];
+ selectedDateRange: { start: DateValue; end: DateValue } = {
+ start: new CalendarDate(2024, 1, 1),
+ end: new CalendarDate(2024, 1, 1),
+ };
@action
- setSelectedDateRange = (range: any) => {
+ setSelectedDateRange = (range: { start: DateValue; end: DateValue }) => {
console.log('Range: ', range);
this.selectedDateRange = range;
};
@@ -228,14 +216,14 @@ export class CalendarManager extends ObservableReactComponent<{}> {
@computed
get createButtonActive() {
if (this.calendarName.length === 0 || this.errorMessage.length > 0) return false; // disabled if no calendar name
- let startDate: Date | undefined;
- let endDate: Date | undefined;
+ let startDate: DateValue | undefined;
+ let endDate: DateValue | undefined;
try {
startDate = this.selectedDateRange.start;
endDate = this.selectedDateRange.end;
console.log(startDate);
console.log(endDate);
- } catch (e: any) {
+ } catch (e) {
console.log(e);
return false; // disabled
}
@@ -288,7 +276,13 @@ export class CalendarManager extends ObservableReactComponent<{}> {
isSearchable
options={this.selectOptions}
value={this.selectedExistingCalendarOption}
- onChange={this.handleSelectChange}
+ onChange={change => {
+ if (change) {
+ const selectOpt = change;
+ this.selectedExistingCalendarOption = selectOpt;
+ this.calendarName = selectOpt.value; // or label
+ }
+ }}
styles={{
control: () => ({
display: 'inline-flex',
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index 253cdd8b5..47f31612f 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -1,26 +1,24 @@
-/* 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';
import * as React from 'react';
import { addStyleSheet } from '../../ClientUtils';
-import { Doc } from '../../fields/Doc';
+import { Doc, Opt } from '../../fields/Doc';
import { DocCast, StrCast } from '../../fields/Types';
import { MainViewModal } from '../views/MainViewModal';
import { DocumentView } from '../views/nodes/DocumentView';
import './CaptureManager.scss';
@observer
-export class CaptureManager extends React.Component<{}> {
+export class CaptureManager extends React.Component<object> {
// eslint-disable-next-line no-use-before-define
public static Instance: CaptureManager;
static _settingsStyle = addStyleSheet();
- @observable _document: any = undefined;
+ @observable _document: Opt<Doc> = undefined;
@observable isOpen: boolean = false; // whether the CaptureManager is to be displayed or not.
// eslint-disable-next-line react/sort-comp
- constructor(props: {}) {
+ constructor(props: object) {
super(props);
makeObservable(this);
CaptureManager.Instance = this;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index c712dba21..8a9f20565 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,7 +1,8 @@
+
import { reaction, runInAction } from "mobx";
import * as rp from 'request-promise';
import { ClientUtils, OmitKeys } from "../../ClientUtils";
-import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
+import { Doc, DocListCast, DocListCastAsync, FieldType, Opt } from "../../fields/Doc";
import { DocData } from "../../fields/DocSymbols";
import { InkTool } from "../../fields/InkField";
import { List } from "../../fields/List";
@@ -60,11 +61,10 @@ interface Button {
// fields that do not correspond to DocumentOption fields
scripts?: { script?: string; onClick?: string; onDoubleClick?: string }
- funcs?: { [key:string]: any};
+ funcs?: { [key:string]: string};
subMenu?: Button[];
}
-// eslint-disable-next-line import/no-mutable-exports
export let resolvedPorts: { server: number, socket: number };
export class CurrentUserUtils {
@@ -95,7 +95,6 @@ export class CurrentUserUtils {
});
const reqdOpts:DocumentOptions = { title: "child click editors", _height:75, isSystem: true};
- // eslint-disable-next-line no-return-assign
return DocUtils.AssignOpts(tempClicks, reqdOpts, reqdClickList) ?? (doc[field] = Docs.Create.TreeDocument(reqdClickList, reqdOpts));
}
@@ -120,7 +119,6 @@ export class CurrentUserUtils {
});
const reqdOpts:DocumentOptions = {title: "click editor templates", _height:75, isSystem: true};
- // eslint-disable-next-line no-return-assign
return DocUtils.AssignOpts(tempClicks, reqdOpts, reqdClickList) ?? (doc[field] = Docs.Create.TreeDocument(reqdClickList, reqdOpts));
}
@@ -138,7 +136,6 @@ export class CurrentUserUtils {
}), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))];
const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true };
- // eslint-disable-next-line no-return-assign
return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
}
static setupUserTemplates(doc: Doc, field="template_user") {
@@ -146,7 +143,6 @@ export class CurrentUserUtils {
const reqdUserList = DocListCast(tempUsers?.data);
const reqdOpts:DocumentOptions = { title: "User Layouts", _height: 75, isSystem: true };
- // eslint-disable-next-line no-return-assign
return DocUtils.AssignOpts(tempUsers, reqdOpts, reqdUserList) ?? (doc[field] = Docs.Create.TreeDocument(reqdUserList, reqdOpts));
}
@@ -174,8 +170,8 @@ export class CurrentUserUtils {
const imageBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.ImageDocument( "http://www.cs.brown.edu/~bcz/noImage.png", { layout:ImageBox.LayoutString(fieldKey), "icon_nativeWidth": 360 / 4, "icon_nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _layout_showTitle: "title", ...opts });
const fontBox = (opts:DocumentOptions, fieldKey:string) => Docs.Create.FontIconDocument({ layout:FontIconBox.LayoutString(fieldKey), _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts });
- const makeIconTemplate = (type: DocumentType | undefined, templateField: string, opts:DocumentOptions) => {
- const title = "icon" + (type ? "_" + type : "");
+ const makeIconTemplate = (name: DocumentType | string | undefined, templateField: string, opts:DocumentOptions) => {
+ const title = "icon" + (name ? "_" + name : "");
const curIcon = DocCast(templateIconsDoc[title]);
const creator = (() => { switch (opts.iconTemplate) {
case DocumentType.IMG : return imageBox;
@@ -183,12 +179,15 @@ export class CurrentUserUtils {
default: return labelBox;
}})();
const allopts = {isSystem: true, onClickScriptDisable: "never", ...opts, title};
- // eslint-disable-next-line no-return-assign
return DocUtils.AssignScripts( (curIcon?.iconTemplate === opts.iconTemplate ?
DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[title] = MakeTemplate(creator(allopts, templateField)))),
{onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)", });
};
const iconTemplates = [
+ // see createCustomView for where icon templates are created at run time
+ // templates defined by a Docs icon_fieldKey (e.g., ink with a transciprtion shows a template of the transcribed text, not miniature ink)
+ makeIconTemplate("transcription", "text", { iconTemplate:DocumentType.LABEL, backgroundColor: "orange" }),
+ // templates defined by a Doc's type
makeIconTemplate(undefined, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "dimgray"}),
makeIconTemplate(DocumentType.AUDIO, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "lightgreen"}),
makeIconTemplate(DocumentType.PDF, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "pink"}),
@@ -199,10 +198,9 @@ export class CurrentUserUtils {
makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}),
makeIconTemplate(DocumentType.VID, "icon", { iconTemplate:DocumentType.IMG}),
makeIconTemplate(DocumentType.BUTTON,"title", { iconTemplate:DocumentType.FONTICON}),
- // nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now
- makeIconTemplate("transcription" as any, "transcription", { iconTemplate:DocumentType.LABEL, backgroundColor: "orange" }),
- // makeIconTemplate(DocumentType.PDF, "icon", {iconTemplate:DocumentType.IMG}, (opts) => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", opts))
- ].filter(d => d).map(d => d!);
+ // makeIconTemplate(DocumentType.PDF, "icon", { iconTemplate:DocumentType.IMG}),
+ ].filter(d => d).map(d => d!);
+
DocUtils.AssignOpts(DocCast(doc[field]), {}, iconTemplates);
}
@@ -320,20 +318,16 @@ export class CurrentUserUtils {
const rtfield = new RichTextField(JSON.stringify(
{doc: {type:"doc",content:[
{type:"code_block",content:[
- {type:"text",text:"^@mermaids"},
- {type:"text",text:"\n\n"},
- {type:"text",text:"pie "},
- {type:"text",text:"title"},
- {type:"text",text:" "},
- {type:"text",text:"Minerals in my tap water"},
- {type:"text",text:"\n \"Calcium\" : "},
+ {type:"text",text:`^@mermaids\n`},
+ {type:"text",text:`\n pie title Minerals in my tap water`},
+ {type:"text",text:`\n "Calcium" : `},
{type:"dashField",attrs:{fieldKey:"calcium",docId:"",hideKey:true,hideValue:false,editable:true}},
- {type:"text",text:"\n \"Potassium\" : "},
+ {type:"text",text:`\n "Potassium" : `},
{type:"dashField",attrs:{fieldKey:"pot",docId:"",hideKey:true,hideValue:false,editable:true}},
- {type:"text",text:"\n \"Magnesium\" : 10.01"}
+ {type:"text",text:`\n "Magnesium" : 10.01`}
]}
]},
- selection:{type:"text",anchor:109,head:109}
+ selection:{type:"text",anchor:1,head:1}
}),
`^@mermaids
pie title Minerals in my tap water
@@ -350,9 +344,9 @@ pie title Minerals in my tap water
plotlyApi(); mermaidsApi();
const emptyThings:{key:string, // the field name where the empty thing will be stored
opts:DocumentOptions, // the document options that are required for the empty thing
- funcs?:{[key:string]: any}, // computed fields that are rquired for the empth thing
- scripts?:{[key:string]: any},
- creator:(opts:DocumentOptions)=> any // how to create the empty thing if it doesn't exist
+ funcs?:{[key:string]: string}, // computed fields that are rquired for the empth thing
+ scripts?:{[key:string]: string},
+ creator:(opts:DocumentOptions)=> Doc // how to create the empty thing if it doesn't exist
}[] = [
{key: "Note", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _layout_autoHeight: true }},
{key: "Flashcard", creator: opts => Docs.Create.ComparisonDocument("", opts), opts: { _layout_isFlashcard: true, _width: 300, _height: 300}},
@@ -403,7 +397,7 @@ pie title Minerals in my tap water
{ toolTip: "Tap or drag to create a collection", title: "Col", icon: "folder", dragFactory: doc.emptyCollection as Doc, clickFactory: DocCast(doc.emptyTab)},
{ toolTip: "Tap or drag to create a webpage", title: "Web", icon: "globe-asia", dragFactory: doc.emptyWebpage as Doc, clickFactory: DocCast(doc.emptyWebpage)},
{ toolTip: "Tap or drag to create a comparison box", title: "Compare", icon: "columns", dragFactory: doc.emptyComparison as Doc, clickFactory: DocCast(doc.emptyComparison)},
- { toolTip: "Tap or drag to create a diagram", title: "Diagram", icon: "tree", dragFactory: doc.emptyDiagram as Doc, clickFactory: DocCast(doc.emptyDiagram)},
+ { toolTip: "Tap or drag to create a diagram", title: "Diagram", icon: "tree", dragFactory: doc.emptyDiagram as Doc, clickFactory: DocCast(doc.emptyDiagram)},
{ toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone", dragFactory: doc.emptyAudio as Doc, clickFactory: DocCast(doc.emptyAudio), openFactoryLocation: OpenWhere.overlay},
{ toolTip: "Tap or drag to create a map", title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, clickFactory: DocCast(doc.emptyMap)},
{ toolTip: "Tap or drag to create a chat assistant", title: "Assistant Chat", icon: "book",dragFactory: doc.emptyChat as Doc, clickFactory: DocCast(doc.emptyChat)},
@@ -412,11 +406,11 @@ pie title Minerals in my tap water
{ toolTip: "Tap or drag to create a button", title: "Button", icon: "circle", dragFactory: doc.emptyButton as Doc, clickFactory: DocCast(doc.emptyButton)},
{ toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, clickFactory: DocCast(doc.emptyScript), funcs: { hidden: "IsNoviceMode()"}},
{ toolTip: "Tap or drag to create a data viz node", title: "DataViz", icon: "chart-bar", dragFactory: doc.emptyDataViz as Doc, clickFactory: DocCast(doc.emptyDataViz)},
- { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "person-chalkboard", dragFactory: doc.emptySlide as Doc, clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
- { toolTip: "Tap or drag to create a view slide", title: "View Slide", icon: "address-card", dragFactory: doc.emptyViewSlide as Doc,clickFactory: DocCast(doc.emptyViewSlide),openFactoryLocation: OpenWhere.overlay,funcs: { hidden: "IsNoviceMode()"}},
- { toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize",dragFactory: doc.emptyHeader as Doc,clickFactory: DocCast(doc.emptyHeader), openFactoryAsDelegate: true, funcs: { hidden: "IsNoviceMode()"} },
- { toolTip: "Toggle a Calculator REPL", title: "replviewer", icon: "calculator", clickFactory: '<ScriptingRepl />' as any, openFactoryLocation: OpenWhere.overlay}, // hack: clickFactory is not a Doc but will get interpreted as a custom UI by the openDoc() onClick script
- // { toolTip: "Toggle an UndoStack", title: "undostacker", icon: "calculator", clickFactory: "<UndoStack />" as any, openFactoryLocation: OpenWhere.overlay},
+ { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "person-chalkboard",dragFactory: doc.emptySlide as Doc,clickFactory: DocCast(doc.emptySlide), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
+ { toolTip: "Tap or drag to create a view slide", title: "View Slide", icon: "address-card", dragFactory: doc.emptyViewSlide as Doc,clickFactory: DocCast(doc.emptyViewSlide), openFactoryLocation: OpenWhere.overlay,funcs: { hidden: "IsNoviceMode()"}},
+ { toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize",dragFactory: doc.emptyHeader as Doc,clickFactory: DocCast(doc.emptyHeader), openFactoryAsDelegate: true, funcs: { hidden: "IsNoviceMode()"} },
+ { toolTip: "Toggle a Calculator REPL", title: "replviewer", icon: "calculator", clickFactory: '<ScriptingRepl />' as unknown as Doc, openFactoryLocation: OpenWhere.overlay}, // hack: clickFactory is not a Doc but will get interpreted as a custom UI by the openDoc() onClick script
+ // { toolTip: "Toggle an UndoStack", title: "undostacker", icon: "calculator", clickFactory: "<UndoStack />" as any, openFactoryLocation: OpenWhere.overlay},
].map(tuple => (
{ openFactoryLocation: OpenWhere.addRight,
scripts: { onClick: 'openDoc(copyDragFactory(this.clickFactory,this.openFactoryAsDelegate), this.openFactoryLocation)',
@@ -446,7 +440,7 @@ pie title Minerals in my tap water
}
/// returns descriptions needed to buttons for the left sidebar to open up panes displaying different collections of documents
- static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, toolTip: string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}, hidden?: boolean}[] {
+ static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, toolTip: string, scripts:{[key:string]:undefined|string}, funcs?:{[key:string]:undefined|string}, hidden?: boolean}[] {
const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(this.target?.data).filter(doc => !docList(this.target.viewed).includes(doc)).length.toString())";
const getActiveDashTrails = "Doc.ActiveDashboard?.myTrails";
return [
@@ -458,13 +452,15 @@ pie title Minerals in my tap water
{ title: "Closed", toolTip: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), ignoreClick: true, icon: "archive", hidden: true }, // this doc is hidden from the Sidebar, but it's still being used in MyFilesystem which ignores the hidden field
{ title: "Shared", toolTip: "Shared Docs", target: Doc.MySharedDocs, ignoreClick: true, icon: "users", funcs: {badgeValue: badgeValue}},
{ title: "Trails", toolTip: "Trails ⌘R", target: Doc.UserDoc(), ignoreClick: true, icon: "pres-trail", funcs: {target: getActiveDashTrails}},
+ { title: "Image Grouper", toolTip: "Image Grouper", target: this.setupImageGrouper(doc, "myImageGrouper"), ignoreClick: true, icon: "folder-open", hidden: false },
+ { title: "Faces", toolTip: "Unique Faces", target: this.setupFaceCollection(doc, "myFaceCollection"), ignoreClick: true, icon: "face-smile", hidden: false },
{ title: "User Doc", toolTip: "User Doc", target: this.setupUserDocView(doc, "myUserDocView"), ignoreClick: true, icon: "address-card",funcs: {hidden: "IsNoviceMode()"} },
- ].map(tuple => ({...tuple, scripts:{onClick: 'selectMainMenu(this)'}}));
+ ].map(tuple => ({...tuple, scripts:{onClick: 'selectMainMenu(this)'}}));
}
/// the empty panel that is filled with whichever left menu button's panel has been selected
static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") {
- DocUtils.AssignDocField(doc, field, (opts) => Doc.assign(new Doc(), opts as any), {title:"leftSidebarPanel", isSystem:true, undoIgnoreFields: new List<string>(['proto'])});
+ DocUtils.AssignDocField(doc, field, (opts) => Doc.assign(new Doc(), opts as {[key:string]: FieldType}), {title:"leftSidebarPanel", isSystem:true, undoIgnoreFields: new List<string>(['proto'])});
}
/// Initializes the left sidebar menu buttons and the panels they open up
@@ -494,6 +490,18 @@ pie title Minerals in my tap water
_lockedPosition: true, _type_collection: CollectionViewType.Schema });
}
+ static setupImageGrouper(doc: Doc, field: string) {
+ return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.ImageGrouperDocument(opts), {
+ dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Image Grouper", isSystem: true, childDragAction: dropActionType.embed,
+ _lockedPosition: true, _type_collection: CollectionViewType.Schema });
+ }
+
+ static setupFaceCollection(doc: Doc, field: string) {
+ return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.FaceCollectionDocument(opts), {
+ dontRegisterView: true, ignoreClick: true, title: "Face Collection", isSystem: true, childDragAction: dropActionType.embed,
+ _lockedPosition: true, _type_collection: CollectionViewType.Schema });
+ }
+
/// Initializes the panel of draggable tools that is opened from the left sidebar.
static setupToolsBtnPanel(doc: Doc, field:string) {
const allTools = DocListCast(DocCast(doc[field])?.data);
@@ -524,7 +532,7 @@ pie title Minerals in my tap water
const contextMenuLabels = [/* "Create New Dashboard" */] as string[];
const contextMenuIcons = [/* "plus" */] as string[];
const childContextMenuScripts = [`toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(this)`, 'removeDashboard(this)', 'resetDashboard(this)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters
- const childContextMenuFilters = ['!IsNoviceMode()', '!IsNoviceMode()', undefined as any, undefined as any, '!IsNoviceMode()'];// entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuScripts
+ const childContextMenuFilters = ['!IsNoviceMode()', '!IsNoviceMode()', undefined, undefined, '!IsNoviceMode()'];// entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuScripts
const childContextMenuLabels = ["Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard", "Reset Dashboard"];// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters
const childContextMenuIcons = ["tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
const reqdOpts:DocumentOptions = {
@@ -546,7 +554,7 @@ pie title Minerals in my tap water
myDashboards.childContextMenuScripts = new List<ScriptField>(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
}
if (Cast(myDashboards.childContextMenuFilters, listSpec(ScriptField), null)?.length !== childContextMenuFilters.length) {
- myDashboards.childContextMenuFilters = new List<ScriptField>(childContextMenuFilters.map(script => !script ? script: ScriptField.MakeFunction(script)!));
+ myDashboards.childContextMenuFilters = new List<ScriptField>(childContextMenuFilters.map(script => !script ? script as unknown as ScriptField: ScriptField.MakeFunction(script)!));
}
return myDashboards;
}
@@ -612,7 +620,7 @@ pie title Minerals in my tap water
_lockedPosition: true, isSystem: true, flexDirection: "row"
})
static multiToggleList = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.FontIconDocument({
- ...opts, data:docs, _gridGap: 0, _xMargin: 5, _yMargin: 5, layout_boxShadow: "0 0", _forceActive: true,
+ ...opts, data: new List<Doc>(docs), _gridGap: 0, _xMargin: 5, _yMargin: 5, layout_boxShadow: "0 0", _forceActive: true,
dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
_lockedPosition: true, isSystem: true, flexDirection: "row"
})
@@ -696,8 +704,8 @@ pie title Minerals in my tap water
{ title: "Fit All", icon: "object-group", toolTip: "Fit Docs to View (double click to make sticky)",btnType: ButtonType.ToggleButton, ignoreClick:true, expertMode: false, toolType:"viewAll", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}', onDoubleClick: '{ return showFreeform(this.toolType, _readOnly_, true);}'}}, // Only when floating document is selected in freeform
{ title: "Clusters", icon: "braille", toolTip: "Show Doc Clusters", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"clusters", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
{ title: "Cards", icon: "brain", toolTip: "Flashcards", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"flashcards", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
- { title: "Arrange", icon:"arrow-down-short-wide",toolTip:"Toggle Auto Arrange", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"arrange", funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
-
+ { title: "Arrange", icon:"arrow-down-short-wide",toolTip:"Auto Arrange", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"arrange", funcs: {hidden: 'IsNoviceMode()'}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
+
]
}
static textTools():Button[] {
@@ -807,7 +815,7 @@ pie title Minerals in my tap water
/// initializes a context menu button for the top bar context menu
static setupContextMenuButton(params:Button, btnDoc?:Doc, btnContainer?:Doc) {
const reqdOpts:DocumentOptions = {
- ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit,
+ ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit as {[key:string]: string|undefined},
color: Colors.WHITE, isSystem: true,
_nativeWidth: params.width ?? 30, _width: params.width ?? 30,
_height: 30, _nativeHeight: 30, linearBtnWidth: params.linearBtnWidth,
@@ -815,7 +823,7 @@ pie title Minerals in my tap water
_dragOnlyWithinContainer: true, _lockedPosition: true,
_embedContainer: btnContainer
};
- const reqdFuncs:{[key:string]:any} = {
+ const reqdFuncs:{[key:string]:string} = {
...params.funcs,
}
return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdOpts) ?? Docs.Create.FontIconDocument(reqdOpts), params.scripts, reqdFuncs);
@@ -854,14 +862,14 @@ pie title Minerals in my tap water
Doc.UserDoc().workspaceRecordingState = undefined;
Doc.UserDoc().workspaceReplayingState = undefined;
const dockedBtns = DocCast(doc[field]);
- const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string|undefined}, funcs?: {[key:string]:any}) =>
+ const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string|undefined}, funcs?: {[key:string]:string|undefined}) =>
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
- { opts: { title: "Replicate",icon:"camera",toolTip: "Copy dashboard layout",btnType: ButtonType.ClickButton, expertMode: true}, scripts: { onClick: `snapshotDashboard()`}},
- { opts: { title: "Recordings", toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList,expertMode: false, ignoreClick: true, width: 100}, funcs: {hidden: `false`, btnList:`getWorkspaceRecordings()`}, scripts: { script: `{ return replayWorkspace(value, _readOnly_); }`, onDragScript: `{ return startRecordingDrag(value); }`}},
- { opts: { title: "Stop Rec",icon: "stop", toolTip: "Stop recording", btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `!isWorkspaceRecording()`}, scripts: { onClick: `stopWorkspaceRecording()`}},
+ { opts: { title: "Replicate",icon:"camera",toolTip: "Copy dashboard layout",btnType: ButtonType.ClickButton, expertMode: true}, scripts: { onClick: `snapshotDashboard()`}},
+ { opts: { title: "Recordings", toolTip: "Workspace Recordings", btnType: ButtonType.DropdownList,expertMode: false, ignoreClick: true, width: 100}, funcs: {hidden: `false`, btnList:`getWorkspaceRecordings()`},scripts: { script: `{ return replayWorkspace(value, _readOnly_); }`, onDragScript: `{ return startRecordingDrag(value); }`}},
+ { opts: { title: "Stop Rec",icon: "stop", toolTip: "Stop recording", btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `!isWorkspaceRecording()`}, scripts: { onClick: `stopWorkspaceRecording()`}},
{ opts: { title: "Play", icon: "play", toolTip: "Play recording", btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${mediaState.Paused}"`}, scripts: { onClick: `resumeWorkspaceReplaying(getCurrentRecording())`}},
{ opts: { title: "Pause", icon: "pause",toolTip: "Pause playback", btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${mediaState.Playing}"`}, scripts: { onClick: `pauseWorkspaceReplaying(getCurrentRecording())`}},
{ opts: { title: "Stop", icon: "stop", toolTip: "Stop playback", btnType: ButtonType.ClickButton, expertMode: false}, funcs: {hidden: `isWorkspaceReplaying() !== "${mediaState.Paused}"`}, scripts: { onClick: `stopWorkspaceReplaying(getCurrentRecording())`}},
@@ -1003,20 +1011,20 @@ pie title Minerals in my tap water
return doc;
}
static setupFieldInfos(doc:Doc, field="fieldInfos") {
- const fieldInfoOpts = { title: "Field Infos", isSystem: true}; // bcz: all possible document options have associated field infos which are stored onn the FieldInfos document **except for title and system which are used as part of the definition of the fieldInfos object
- const infos = DocUtils.AssignDocField(doc, field, opts => Doc.assign(new Doc(), opts as any), fieldInfoOpts);
+ const fieldInfoOpts = { title: "Field Infos", isSystem: true}; // bcz: all possible document options have associated field infos which are stored on the FieldInfos document **except for title and system which are used as part of the definition of the fieldInfos object
+ const infos = DocUtils.AssignDocField(doc, field, opts => Doc.assign(new Doc(), opts as {[key:string]: FieldType}), fieldInfoOpts);
const entries = Object.entries(new DocumentOptions());
entries.forEach(pair => {
if (!Array.from(Object.keys(fieldInfoOpts)).includes(pair[0])) {
const options = pair[1] as FInfo;
- const opts:DocumentOptions = { isSystem: true, title: pair[0], ...OmitKeys(options, ["values"]).omit, fieldIsLayout: pair[0].startsWith("_")};
+ const opts:DocumentOptions = { isSystem: true, title: pair[0], ...OmitKeys(options, ["values"]).omit};
switch (options.fieldType) {
- case FInfoFieldType.boolean: opts.fieldValues = new List<boolean>(options.values as any); break;
- case FInfoFieldType.number: opts.fieldValues = new List<number>(options.values as any); break;
- case FInfoFieldType.Doc: opts.fieldValues = new List<Doc>(options.values as any); break;
- default: opts.fieldValues = new List<string>(options.values as any); break;// string, pointerEvents, dimUnit, dropActionType
+ case FInfoFieldType.boolean: opts.fieldValues = new List<boolean>(options.values as boolean[]); break;
+ case FInfoFieldType.number: opts.fieldValues = new List<number>(options.values as number[]); break;
+ case FInfoFieldType.Doc: opts.fieldValues = new List<Doc>(options.values as Doc[]); break;
+ default: opts.fieldValues = new List<FieldType>(options.values); break;// string, pointerEvents, dimUnit, dropActionType
}
- DocUtils.AssignDocField(infos, pair[0], docOpts => Doc.assign(new Doc(), OmitKeys(docOpts,["values"]).omit), opts);
+ DocUtils.AssignDocField(infos, pair[0], docOpts => Doc.assign(new Doc(), OmitKeys(docOpts,["values"]).omit as {[key:string]: FieldType}), opts);
}
});
}
@@ -1024,10 +1032,10 @@ pie title Minerals in my tap water
public static async loadCurrentUser() {
return rp.get(ClientUtils.prepend("/getCurrentUser")).then(async response => {
if (response) {
- const result: { version: string, userDocumentId: string, sharingDocumentId: string, linkDatabaseId: string, email: string, cacheDocumentIds: string, resolvedPorts: string } = JSON.parse(response);
+ const result: { version: string, userDocumentId: string, sharingDocumentId: string, linkDatabaseId: string, email: string, cacheDocumentIds: string, resolvedPorts: {server: number, socket: number} } = JSON.parse(response);
runInAction(() => { SnappingManager.SetServerVersion(result.version); });
ClientUtils.SetCurrentUserEmail(result.email);
- resolvedPorts = result.resolvedPorts as any;
+ resolvedPorts = result.resolvedPorts;
DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts?.socket, result.email);
if (result.cacheDocumentIds)
{
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index b9a465515..831afe538 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -1,7 +1,5 @@
/* 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';
import { ClientUtils } from '../../ClientUtils';
import { Doc, Opt } from '../../fields/Doc';
import { DocData } from '../../fields/DocSymbols';
@@ -33,17 +31,19 @@ import { UndoManager } from './UndoManager';
* In addition to compile-time default commands, you can invoke DictationManager.Commands.Register(Independent|Dependent)
* to add new commands as classes or components are constructed.
*/
+
export namespace DictationManager {
/**
* Some type maneuvering to access Webkit's built-in
* speech recognizer.
*/
+
namespace CORE {
export interface IWindow extends Window {
- webkitSpeechRecognition: any;
+ webkitSpeechRecognition: { new (): SpeechRecognition };
}
}
- const { webkitSpeechRecognition }: CORE.IWindow = window as any as CORE.IWindow;
+ const { webkitSpeechRecognition }: CORE.IWindow = window as unknown as CORE.IWindow;
export const placeholder = 'Listening...';
export namespace Controls {
@@ -74,7 +74,7 @@ export namespace DictationManager {
// eslint-disable-next-line new-cap
const recognizer: Opt<SpeechRecognition> = webkitSpeechRecognition ? new webkitSpeechRecognition() : undefined;
- export type InterimResultHandler = (results: string) => any;
+ export type InterimResultHandler = (results: string) => void;
export type ContinuityArgs = { indefinite: boolean } | false;
export type DelimiterArgs = { inter: string; intra: string };
export type ListeningUIStatus = { interim: boolean } | false;
@@ -117,11 +117,11 @@ export namespace DictationManager {
}
options?.tryExecute && (await DictationManager.Commands.execute(results));
}
- } catch (e: any) {
+ } catch (e) {
console.log(e);
if (overlay) {
DictationOverlay.Instance.isListening = false;
- DictationOverlay.Instance.dictatedPhrase = results = `dictation error: ${'error' in e ? e.error : 'unknown error'}`;
+ DictationOverlay.Instance.dictatedPhrase = results = `dictation error: ${(e as { error: string }).error || 'unknown error'}`;
DictationOverlay.Instance.dictationSuccess = false;
}
} finally {
@@ -156,11 +156,11 @@ export namespace DictationManager {
recognizer.start();
return new Promise<string>(resolve => {
- recognizer.onerror = (e: any) => {
+ recognizer.onerror = e => {
// e is SpeechRecognitionError but where is that defined?
if (!(indefinite && e.error === 'no-speech')) {
recognizer.stop();
- resolve(e);
+ resolve(e.message);
}
};
@@ -230,10 +230,10 @@ export namespace DictationManager {
export namespace Commands {
export const dictationFadeDuration = 2000;
- export type IndependentAction = (target: DocumentView) => any | Promise<any>;
+ export type IndependentAction = (target: DocumentView) => void | Promise<void>;
export type IndependentEntry = { action: IndependentAction; restrictTo?: DocumentType[] };
- export type DependentAction = (target: DocumentView, matches: RegExpExecArray) => any | Promise<any>;
+ export type DependentAction = (target: DocumentView, matches: RegExpExecArray) => void | Promise<void>;
export type DependentEntry = { expression: RegExp; action: DependentAction; restrictTo?: DocumentType[] };
export const RegisterIndependent = (key: string, value: IndependentEntry) => Independent.set(key, value);
@@ -295,7 +295,6 @@ export namespace DictationManager {
[DocumentType.COL, listSpec(Doc)],
[DocumentType.AUDIO, AudioField],
[DocumentType.IMG, ImageField],
- [DocumentType.IMPORT, listSpec(Doc)],
[DocumentType.RTF, 'string'],
]);
@@ -397,8 +396,8 @@ export namespace DictationManager {
];
}
export function recordAudioAnnotation(dataDoc: Doc, field: string, onRecording?: (stop: () => void) => void, onEnd?: () => void) {
- let gumStream: any;
- let recorder: any;
+ let gumStream: MediaStream | undefined;
+ let recorder: MediaRecorder | undefined;
navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
let audioTextAnnos = Cast(dataDoc[field + '_audioAnnotations_text'], listSpec('string'), null);
if (audioTextAnnos) audioTextAnnos.push('');
@@ -415,8 +414,12 @@ export namespace DictationManager {
gumStream = stream;
recorder = new MediaRecorder(stream);
- recorder.ondataavailable = async (e: any) => {
- const [{ result }] = await Networking.UploadFilesToServer({ file: e.data });
+ recorder.ondataavailable = async (e: BlobEvent) => {
+ const file: Blob & { name?: string; lastModified?: number; webkitRelativePath?: string } = e.data;
+ file.name = '';
+ file.lastModified = 0;
+ file.webkitRelativePath = '';
+ const [{ result }] = await Networking.UploadFilesToServer({ file: file as Blob & { name: string; lastModified: number; webkitRelativePath: string } });
if (!(result instanceof Error)) {
const audioField = new AudioField(result.accessPaths.agnostic.client);
const audioAnnos = Cast(dataDoc[field + '_audioAnnotations'], listSpec(AudioField), null);
@@ -426,10 +429,10 @@ export namespace DictationManager {
};
recorder.start();
const stopFunc = () => {
- recorder.stop();
+ recorder?.stop();
DictationManager.Controls.stop(/* false */);
dataDoc.audioAnnoState = AudioAnnoState.stopped;
- gumStream.getAudioTracks()[0].stop();
+ gumStream?.getAudioTracks()[0].stop();
};
if (onRecording) onRecording(stopFunc);
else setTimeout(stopFunc, 5000);
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 96b8b5657..83b83240e 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -17,7 +17,6 @@ export class DocumentManager {
// eslint-disable-next-line no-use-before-define
private static _instance: DocumentManager;
public static get Instance(): DocumentManager {
- // eslint-disable-next-line no-return-assign
return this._instance || (this._instance = new this());
}
@@ -50,22 +49,23 @@ export class DocumentManager {
DocumentView.getLightboxDocumentView = this.getLightboxDocumentView;
observe(Doc.CurrentlyLoading, change => {
// watch CurrentlyLoading-- when something is loaded, it's removed from the list and we have to update its icon if it were iconified since LoadingBox icons are different than the media they become
- switch (change.type as any) {
+ switch (change.type) {
case 'update':
break;
- case 'remove':
- // DocumentManager.Instance.getAllDocumentViews(change as any).forEach(dv => StrCast(dv.Document.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify()));
- break;
case 'splice':
- (change as any).removed.forEach((doc: Doc) => DocumentManager.Instance.getAllDocumentViews(doc).forEach(dv => StrCast(dv.Document.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify())));
+ change.removed.forEach((doc: Doc) => DocumentManager.Instance.getAllDocumentViews(doc).forEach(dv => StrCast(dv.Document.layout_fieldKey) === 'layout_icon' && dv.iconify(() => dv.iconify())));
break;
default:
}
});
}
- private _viewRenderedCbs: { doc: Doc; func: (dv: DocumentView) => any }[] = [];
- public AddViewRenderedCb = (doc: Opt<Doc>, func: (dv: DocumentView) => any) => {
+ private _anyViewRenderedCbs: ((dv: DocumentView) => unknown)[] = [];
+ public AddAnyViewRenderedCB = (func: (dv: DocumentView) => unknown) => {
+ this._anyViewRenderedCbs.push(func);
+ };
+ private _viewRenderedCbs: { doc: Doc; func: (dv: DocumentView) => unknown }[] = [];
+ public AddViewRenderedCb = (doc: Opt<Doc>, func: (dv: DocumentView) => unknown) => {
if (doc) {
const dv = DocumentView.LightboxDoc() ? this.getLightboxDocumentView(doc) : this.getDocumentView(doc);
this._viewRenderedCbs.push({ doc, func });
@@ -74,18 +74,20 @@ export class DocumentManager {
return true;
}
} else {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
func(undefined as any);
}
return false;
};
callAddViewFuncs = (view: DocumentView) => {
- const callFuncs = this._viewRenderedCbs.filter(vc => vc.doc === view.Document);
+ const docCallFuncs = this._viewRenderedCbs.filter(vc => vc.doc === view.Document);
+ const callFuncs = docCallFuncs.map(vc => vc.func).concat(this._anyViewRenderedCbs);
if (callFuncs.length) {
- this._viewRenderedCbs = this._viewRenderedCbs.filter(vc => !callFuncs.includes(vc));
+ this._viewRenderedCbs = this._viewRenderedCbs.filter(vc => !docCallFuncs.includes(vc));
const intTimer = setInterval(
() => {
if (!view.ComponentView?.incrementalRendering?.()) {
- callFuncs.forEach(cf => cf.func(view));
+ callFuncs.forEach(cf => cf(view));
clearInterval(intTimer);
}
},
@@ -341,22 +343,24 @@ export class DocumentManager {
// if there's an options.effect, it will be handled from linkFollowHighlight. We delay the start of
// the highlight so that the target document can be somewhat centered so that the effect/highlight will be seen
// bcz: should this delay be an options parameter?
- setTimeout(() => {
- Doc.linkFollowHighlight(viewSpec ? [docView.Document, viewSpec] : docView.Document, undefined, options.effect);
- 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);
- }
- Doc.AddUnHighlightWatcher(() => {
- docView.Document[Animation] = undefined;
- DocumentManager.removeOverlayViews();
- });
- }, (options.zoomTime ?? 0) * 0.5);
+ setTimeout(
+ () => {
+ Doc.linkFollowHighlight(viewSpec ? [docView.Document, viewSpec] : docView.Document, undefined, options.effect);
+ 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);
+ }
+ Doc.AddUnHighlightWatcher(() => {
+ docView.Document[Animation] = undefined;
+ DocumentManager.removeOverlayViews();
+ });
+ },
+ (options.zoomTime ?? 0) * 0.5
+ );
if (options.playMedia) docView.ComponentView?.playFrom?.(NumCast(docView.Document._layout_currentTimecode));
if (options.playAudio) DocumentManager.playAudioAnno(docView.Document);
if (options.toggleTarget && (!options.didMove || docView.Document.hidden)) docView.Document.hidden = !docView.Document.hidden;
- Doc.AddUnHighlightWatcher(() => docView.Document[Animation] = undefined);
}
}
}
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index fda505420..7db13689d 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,4 +1,3 @@
-/* eslint-disable import/no-mutable-exports */
/* eslint-disable no-use-before-define */
/**
* The DragManager handles all dragging interactions that occur entirely within Dash (as opposed to external drag operations from the file system, etc)
@@ -23,13 +22,14 @@ import { DocData } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
import { PrefetchProxy } from '../../fields/Proxy';
import { ScriptField } from '../../fields/ScriptField';
-import { ScriptCast } from '../../fields/Types';
+import { ScriptCast, StrCast } from '../../fields/Types';
import { Docs } from '../documents/Documents';
import { DocumentView } from '../views/nodes/DocumentView';
import { dropActionType } from './DropActionTypes';
import { SnappingManager } from './SnappingManager';
import { UndoManager } from './UndoManager';
+// eslint-disable-next-line @typescript-eslint/no-var-requires
const { contextMenuZindex } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore
/**
@@ -78,7 +78,7 @@ export namespace DragManager {
export let CompleteWindowDrag: Opt<(aborted: boolean) => void>;
export let AbortDrag: () => void = emptyFunction;
export const docsBeingDragged: Doc[] = observable([]);
- export let DocDragData: DocumentDragData | undefined;
+ export let DraggedDocs: Doc[] | undefined;
export function Root() {
const root = document.getElementById('root');
@@ -118,7 +118,7 @@ export namespace DragManager {
// event called when the drag operation has completed (aborted or completed a drop) -- this will be after any drop event has been generated
export class DragCompleteEvent {
- constructor(aborted: boolean, dragData: { [id: string]: any }) {
+ constructor(aborted: boolean, dragData: DocumentDragData | AnchorAnnoDragData | LinkDragData | ColumnDragData) {
this.aborted = aborted;
this.docDragData = dragData instanceof DocumentDragData ? dragData : undefined;
this.annoDragData = dragData instanceof AnchorAnnoDragData ? dragData : undefined;
@@ -167,6 +167,9 @@ export namespace DragManager {
linkSourceGetAnchor: () => Doc;
linkSourceDoc?: Doc;
linkDragView: DocumentView;
+ get canEmbed() {
+ return true;
+ }
}
export class ColumnDragData {
// constructor(colKey: SchemaHeaderField) {
@@ -177,6 +180,9 @@ export namespace DragManager {
this.colIndex = colIndex;
}
colIndex: number;
+ get canEmbed() {
+ return true;
+ }
}
// used by PDFs,Text,Image,Video,Web to conditionally (if the drop completes) create a text annotation when dragging the annotate button from the AnchorMenu when a text/region selection has been made.
// this is pretty clunky and should be rethought out using linkDrag or DocumentDrag
@@ -191,6 +197,9 @@ export namespace DragManager {
offset: number[];
dropAction?: dropActionType;
userDropAction?: dropActionType;
+ get canEmbed() {
+ return true;
+ }
}
const defaultPreDropFunc = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
@@ -208,7 +217,7 @@ export namespace DragManager {
const handler = (e: Event) => dropFunc(e, (e as CustomEvent<DropEvent>).detail);
const preDropHandler = (e: Event) => {
const de = (e as CustomEvent<DropEvent>).detail;
- (preDropFunc ?? defaultPreDropFunc)(e, de, doc.dropAction as any as dropActionType);
+ (preDropFunc ?? defaultPreDropFunc)(e, de, StrCast(doc.dropAction) as dropActionType);
};
element.addEventListener('dashOnDrop', handler);
element.addEventListener('dashPreDrop', preDropHandler);
@@ -220,8 +229,8 @@ export namespace DragManager {
}
// drag a document and drop it (or make an embed/copy on drop)
- export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, onDropCompleted?: (e?: DragCompleteEvent) => any) {
- const addAudioTag = (dropDoc: any) => {
+ export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, onDropCompleted?: (e?: DragCompleteEvent) => unknown) {
+ const addAudioTag = (dropDoc: Doc) => {
dropDoc && !dropDoc.author_date && (dropDoc.author_date = new DateField());
dropDoc instanceof Doc && CreateLinkToActiveAudio(() => dropDoc);
return dropDoc;
@@ -236,7 +245,7 @@ export namespace DragManager {
await Promise.all(
dragData.draggedDocuments.map(async d =>
!dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
- ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
+ ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result as Doc)
: docDragData.dropAction === dropActionType.embed
? Doc.BestEmbedding(d)
: docDragData.dropAction === dropActionType.add
@@ -249,7 +258,7 @@ export namespace DragManager {
)
)
).filter(d => d);
- ![dropActionType.same, dropActionType.proto].includes(docDragData.dropAction as any) &&
+ ![dropActionType.same, dropActionType.proto].includes(StrCast(docDragData.dropAction) as dropActionType) &&
docDragData.droppedDocuments
// .filter(drop => !drop.dragOnlyWithinContainer || ['embed', 'copy'].includes(docDragData.dropAction as any))
.forEach((drop: Doc, i: number) => {
@@ -376,9 +385,18 @@ export namespace DragManager {
options?.dragComplete?.(complete);
endDrag?.();
}
- export function StartDrag(elesIn: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void, dragUndoName?: string) {
- if (dragData.dropAction === 'none' || SnappingManager.ExploreMode) return;
- DocDragData = dragData as DocumentDragData;
+ export function StartDrag(
+ elesIn: HTMLElement[],
+ dragData: DocumentDragData | LinkDragData | ColumnDragData | AnchorAnnoDragData,
+ downX: number,
+ downY: number,
+ options?: DragOptions,
+ finishDrag?: (dropData: DragCompleteEvent) => void,
+ dragUndoName?: string
+ ) {
+ if (SnappingManager.ExploreMode) return;
+ const docDragData = dragData instanceof DocumentDragData ? dragData : undefined;
+ DraggedDocs = docDragData?.draggedDocuments;
const batch = UndoManager.StartBatch(dragUndoName ?? 'document drag');
const eles = elesIn.filter(e => e);
SnappingManager.SetCanEmbed(dragData.canEmbed || false);
@@ -437,8 +455,9 @@ export namespace DragManager {
next && children.push(...Array.from(next.children));
if (next) {
['marker-start', 'marker-mid', 'marker-end'].forEach(field => {
- if (next.localName.startsWith('path') && (next.attributes as any)[field]) {
- next.setAttribute(field, (next.attributes as any)[field].value.replace('#', '#X'));
+ if (next.localName.startsWith('path')) {
+ const item = next.attributes.getNamedItem(field);
+ item && next.setAttribute(field, item.value.replace('#', '#X'));
}
});
if (next.localName.startsWith('marker')) {
@@ -495,7 +514,7 @@ export namespace DragManager {
.map((pb, i) => pb.getContext('2d')!.drawImage(pdfBoxSrc[i], 0, 0));
}
[dragElement, ...Array.from(dragElement.getElementsByTagName('*'))]
- .map(dele => (dele as any).style)
+ .map(dele => (dele as HTMLElement)?.style)
.forEach(style => {
style && (style.pointerEvents = 'none');
});
@@ -536,34 +555,35 @@ export namespace DragManager {
const yFromBottom = elesCont.bottom - downY;
let scrollAwaiter: Opt<NodeJS.Timeout>;
- let startWindowDragTimer: any;
+ let startWindowDragTimer: NodeJS.Timeout | undefined;
const moveHandler = (e: PointerEvent) => {
e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop
- if (dragData instanceof DocumentDragData) {
- dragData.userDropAction = e.ctrlKey && e.altKey ? dropActionType.copy : e.shiftKey ? dropActionType.move : e.ctrlKey ? dropActionType.embed : dragData.defaultDropAction;
- }
- if (['lm_tab', 'lm_title_wrap', 'lm_tabs', 'lm_header'].includes(typeof (e.target as any).className === 'string' ? (e.target as any)?.className : '') && dragData.draggedDocuments.length === 1) {
- if (!startWindowDragTimer) {
- startWindowDragTimer = setTimeout(async () => {
- startWindowDragTimer = undefined;
- dragData.dropAction = dragData.userDropAction || 'same';
- AbortDrag();
- await finishDrag?.(new DragCompleteEvent(true, dragData));
- DragManager.StartWindowDrag?.(e, dragData.droppedDocuments, aborted => {
- if (!aborted && (dragData.dropAction === 'move' || dragData.dropAction === 'same')) {
- dragData.removeDocument?.(dragData.draggedDocuments[0]);
- }
- });
- }, 500);
+ if (docDragData) {
+ docDragData.userDropAction = e.ctrlKey && e.altKey ? dropActionType.copy : e.shiftKey ? dropActionType.move : e.ctrlKey ? dropActionType.embed : docDragData.defaultDropAction;
+ const targClassName = e.target instanceof HTMLElement && typeof e.target.className === 'string' ? e.target.className : '';
+ if (['lm_tab', 'lm_title_wrap', 'lm_tabs', 'lm_header'].includes(targClassName) && docDragData.draggedDocuments.length === 1) {
+ if (!startWindowDragTimer) {
+ startWindowDragTimer = setTimeout(async () => {
+ startWindowDragTimer = undefined;
+ docDragData.dropAction = docDragData.userDropAction || dropActionType.same;
+ AbortDrag();
+ await finishDrag?.(new DragCompleteEvent(true, docDragData));
+ DragManager.StartWindowDrag?.(e, docDragData.droppedDocuments, aborted => {
+ if (!aborted && (docDragData?.dropAction === 'move' || docDragData?.dropAction === 'same')) {
+ docDragData.removeDocument?.(docDragData?.draggedDocuments[0]);
+ }
+ });
+ }, 500);
+ }
+ } else {
+ clearTimeout(startWindowDragTimer);
+ startWindowDragTimer = undefined;
}
- } else {
- clearTimeout(startWindowDragTimer);
- startWindowDragTimer = undefined;
}
const target = document.elementFromPoint(e.x, e.y);
- if (target && !Doc.UserDoc()._noAutoscroll && !options?.noAutoscroll && !dragData.draggedDocuments?.some((d: any) => d._freeform_noAutoPan)) {
+ if (target && !Doc.UserDoc()._noAutoscroll && !options?.noAutoscroll && !(docDragData?.draggedDocuments as Doc[])?.some(d => d._freeform_noAutoPan)) {
const autoScrollHandler = () => {
target.dispatchEvent(
new CustomEvent<React.DragEvent>('dashDragMovePause', {
@@ -587,7 +607,7 @@ export namespace DragManager {
screenX: e.screenX,
screenY: e.screenY,
detail: e.detail,
- view: e.view ? e.view : (new Window() as any),
+ view: { ...(e.view ?? new Window()), styleMedia: { type: '', matchMedium: () => false } }, // bcz: Ugh.. this looks wrong
nativeEvent: new DragEvent('dashDragMovePause'),
currentTarget: target,
target: target,
@@ -596,10 +616,10 @@ export namespace DragManager {
defaultPrevented: true,
eventPhase: e.eventPhase,
isTrusted: true,
- preventDefault: () => 'not implemented for this event' && false,
- isDefaultPrevented: () => 'not implemented for this event' && false,
- stopPropagation: () => 'not implemented for this event' && false,
- isPropagationStopped: () => 'not implemented for this event' && false,
+ preventDefault: () => 'not implemented for this event',
+ isDefaultPrevented: () => false,
+ stopPropagation: () => 'not implemented for this event',
+ isPropagationStopped: () => false,
persist: emptyFunction,
timeStamp: e.timeStamp,
type: 'dashDragMovePause',
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 0314af06b..eb2011b77 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -26,9 +26,10 @@ function makeTemplate(doc: Doc, first: boolean = true): boolean {
if (layoutDoc.layout instanceof Doc) {
return true; // its already a template
}
- const layout = StrCast(layoutDoc.layout).match(/fieldKey={'[^']*'}/)![0];
- const fieldKey = layout.replace("fieldKey={'", '').replace(/'}$/, '');
- const docs = DocListCast(layoutDoc[fieldKey]);
+ const layout = StrCast(layoutDoc.layout).match(/fieldKey={'[^']*'}/)?.[0];
+ const fieldKey = layout?.replace("fieldKey={'", '').replace(/'}$/, '');
+ const docData = fieldKey ? layoutDoc[fieldKey] : undefined;
+ const docs = DocListCast(docData);
let isTemplate = false;
docs.forEach(d => {
if (!StrCast(d.title).startsWith('-')) {
@@ -40,7 +41,7 @@ function makeTemplate(doc: Doc, first: boolean = true): boolean {
if (first && !docs.length) {
// bcz: feels hacky : if the root level document has items, it's not a field template
isTemplate = Doc.MakeMetadataFieldTemplate(doc, layoutDoc[DocData], true) || isTemplate;
- } else if (layoutDoc[fieldKey] instanceof RichTextField || layoutDoc[fieldKey] instanceof ImageField) {
+ } else if (docData instanceof RichTextField || docData instanceof ImageField) {
if (!StrCast(layoutDoc.title).startsWith('-')) {
isTemplate = Doc.MakeMetadataFieldTemplate(layoutDoc, layoutDoc[DocData], true);
}
@@ -110,8 +111,8 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
}
ScriptingGlobals.add(
// eslint-disable-next-line prefer-arrow-callback
- function convertToButtons(dragData: any) {
- convertDropDataToButtons(dragData as DragManager.DocumentDragData);
+ function convertToButtons(dragData: DragManager.DocumentDragData) {
+ convertDropDataToButtons(dragData);
},
'converts the dropped data to buttons',
'(dragData: any)'
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 5701a22c0..9d0817a06 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -1,8 +1,6 @@
-/* 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, computed, makeObservable, observable } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import Select from 'react-select';
@@ -31,7 +29,7 @@ export interface UserOptions {
}
@observer
-export class GroupManager extends ObservableReactComponent<{}> {
+export class GroupManager extends ObservableReactComponent<object> {
// eslint-disable-next-line no-use-before-define
static Instance: GroupManager;
@observable isOpen: boolean = false; // whether the GroupManager is to be displayed or not.
@@ -44,7 +42,7 @@ export class GroupManager extends ObservableReactComponent<{}> {
@observable private buttonColour: '#979797' | 'black' = '#979797';
@observable private groupSort: 'ascending' | 'descending' | 'none' = 'none';
- constructor(props: Readonly<{}>) {
+ constructor(props: Readonly<object>) {
super(props);
makeObservable(this);
GroupManager.Instance = this;
@@ -227,15 +225,6 @@ export class GroupManager extends ObservableReactComponent<{}> {
}
/**
- * Handles changes in the users selected in the "Select users" dropdown.
- * @param selectedOptions
- */
- @action
- handleChange = (selectedOptions: any) => {
- this.selectedUsers = selectedOptions as UserOptions[];
- };
-
- /**
* Creates the group when the enter key has been pressed (when in the input).
* @param e
*/
@@ -309,7 +298,6 @@ export class GroupManager extends ObservableReactComponent<{}> {
<input
ref={this.inputRef}
onKeyDown={this.handleKeyDown}
- // eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus
type="text"
placeholder="Group name"
@@ -323,7 +311,9 @@ export class GroupManager extends ObservableReactComponent<{}> {
className="select-users"
isMulti
options={this.options}
- onChange={this.handleChange}
+ onChange={selectedOptions => {
+ runInAction(() => (this.selectedUsers = Array.from(selectedOptions)));
+ }}
placeholder="Select users"
value={this.selectedUsers}
closeMenuOnSelect={false}
diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx
index da9e1aa28..88d73d742 100644
--- a/src/client/util/GroupMemberView.tsx
+++ b/src/client/util/GroupMemberView.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button, IconButton, Size, Type } from 'browndash-components';
import { action, observable } from 'mobx';
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 52d0223d5..0d0c056a4 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -85,7 +85,7 @@ export namespace HistoryUtil {
const parsers: { [type: string]: (pathname: string[], opts: qs.ParsedQuery) => ParsedUrl | undefined } = {};
const stringifiers: { [type: string]: (state: ParsedUrl) => string } = {};
- type ParserValue = true | 'none' | 'json' | ((value: string) => any);
+ type ParserValue = true | 'none' | 'json' | ((value: string) => string | null | (string | null)[]);
type Parser = {
[key: string]: ParserValue;
@@ -106,7 +106,7 @@ export namespace HistoryUtil {
return value;
}
parsers[type] = (pathname, opts) => {
- const current: any = { type };
+ const current: DocUrl & { [key: string]: null | (string | null)[] | string } = { type: 'doc', docId: '' };
for (const required in requiredFields) {
if (!(required in opts)) {
return undefined;
@@ -148,7 +148,7 @@ export namespace HistoryUtil {
path = customStringifier(state, path);
}
const queryObj = OmitKeys(state, keys).extract;
- const query: any = {};
+ const query: { [key: string]: string | null } = {};
Object.keys(queryObj).forEach(key => {
query[key] = queryObj[key] === null ? null : JSON.stringify(queryObj[key]);
});
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index 8d4eefa7e..266e05f08 100644
--- a/src/client/util/Import & Export/ImageUtils.ts
+++ b/src/client/util/Import & Export/ImageUtils.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-namespace */
import { ClientUtils } from '../../../ClientUtils';
import { Doc } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
diff --git a/src/client/util/Import & Export/ImportMetadataEntry.tsx b/src/client/util/Import & Export/ImportMetadataEntry.tsx
index db1e3d6cd..63dedf820 100644
--- a/src/client/util/Import & Export/ImportMetadataEntry.tsx
+++ b/src/client/util/Import & Export/ImportMetadataEntry.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable no-use-before-define */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed } from 'mobx';
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index a07550e09..4231c2ca8 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -1,3 +1,4 @@
+import { Property } from 'csstype';
import * as React from 'react';
import { Utils } from '../../Utils';
import { Gestures } from '../../pen-gestures/GestureTypes';
@@ -11,7 +12,7 @@ export namespace InteractionUtils {
const ERASER_BUTTON = 5;
- export function makePolygon(shape: string, points: { X: number; Y: number }[]) {
+ export function makePolygon(shape: Gestures, points: { X: number; Y: number }[]) {
// if arrow/line/circle, the two end points should be the starting and the ending point
let left = points[0].X;
let top = points[0].Y;
@@ -19,7 +20,7 @@ export namespace InteractionUtils {
let bottom = points[1].Y;
if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y + 1 === points[0].Y) {
// pointer is up (first and last points are the same)
- if (![Gestures.Arrow, Gestures.Line, Gestures.Circle].includes(shape as any as Gestures)) {
+ if (![Gestures.Arrow, Gestures.Line, Gestures.Circle].includes(shape)) {
// otherwise take max and min
const xs = points.map(p => p.X);
const ys = points.map(p => p.Y);
@@ -98,8 +99,8 @@ export namespace InteractionUtils {
color: string,
width: number,
strokeWidth: number,
- lineJoin: string,
- strokeLineCap: string,
+ lineJoin: Property.StrokeLinejoin,
+ strokeLineCap: Property.StrokeLinecap,
bezier: string,
fill: string,
arrowStart: string,
@@ -108,8 +109,8 @@ export namespace InteractionUtils {
dash: string | undefined,
scalexIn: number,
scaleyIn: number,
- shape: string,
- pevents: string,
+ shape: Gestures,
+ pevents: Property.PointerEvents,
opacity: number,
nodefs: boolean,
downHdlr?: (e: React.PointerEvent) => void,
@@ -154,7 +155,7 @@ export namespace InteractionUtils {
<marker id={`arrowStart${defGuid}`} markerUnits="userSpaceOnUse" orient="auto" overflow="visible" refX={markerStrokeWidth * (arrowLengthFactor - arrowNotchFactor)} refY={0} markerWidth="10" markerHeight="7">
<polygon
style={{ stroke: color }}
- strokeLinejoin={lineJoin as any}
+ strokeLinejoin={lineJoin as 'inherit' | 'round' | 'bevel' | 'miter'}
strokeWidth={(markerStrokeWidth * 2) / 3}
points={`${arrowLengthFactor * markerStrokeWidth} ${-markerStrokeWidth * arrowWidthFactor}, ${markerStrokeWidth * (arrowLengthFactor - arrowNotchFactor)} 0, ${arrowLengthFactor * markerStrokeWidth} ${
markerStrokeWidth * arrowWidthFactor
@@ -166,7 +167,7 @@ export namespace InteractionUtils {
<marker id={`arrowEnd${defGuid}`} markerUnits="userSpaceOnUse" orient="auto" overflow="visible" refX={markerStrokeWidth * arrowNotchFactor} refY={0} markerWidth="10" markerHeight="7">
<polygon
style={{ stroke: color }}
- strokeLinejoin={lineJoin as any}
+ strokeLinejoin={lineJoin as 'inherit' | 'miter' | 'round' | 'bevel'}
strokeWidth={(markerStrokeWidth * 2) / 3}
points={`0 ${-markerStrokeWidth * arrowWidthFactor}, ${markerStrokeWidth * arrowNotchFactor} 0, 0 ${markerStrokeWidth * arrowWidthFactor}, ${arrowLengthFactor * markerStrokeWidth} 0`}
/>
@@ -184,10 +185,10 @@ export namespace InteractionUtils {
filter: mask ? `url(#mask${defGuid})` : undefined,
opacity: 1.0,
// opacity: strokeWidth !== width ? 0.5 : undefined,
- pointerEvents: (pevents as any) === 'all' ? 'visiblepainted' : (pevents as any),
+ pointerEvents: pevents === 'all' ? 'visiblePainted' : pevents,
stroke: color ?? 'rgb(0, 0, 0)',
strokeWidth,
- strokeLinecap: strokeLineCap as any,
+ strokeLinecap: strokeLineCap,
strokeDasharray: dashArray,
transition: 'inherit',
}}
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 9a0edcfec..0a3a0ba49 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -50,7 +50,7 @@ export class LinkFollower {
const backLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_2 as Doc)); // link docs where 'sourceDoc' is link_anchor_2
const fwdLinkWithoutTargetView = fwdLinks.find(l => !getView(DocCast(l.link_anchor_2)));
const backLinkWithoutTargetView = backLinks.find(l => !getView(DocCast(l.link_anchor_1)));
- const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView ?? backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
+ const linkWithoutTargetDoc = traverseBacklink === undefined ? (fwdLinkWithoutTargetView ?? backLinkWithoutTargetView) : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
const linkDocList = linkWithoutTargetDoc && !sourceDoc.followAllLinks ? [linkWithoutTargetDoc] : traverseBacklink === undefined ? fwdLinks.concat(backLinks) : traverseBacklink ? backLinks : fwdLinks;
const followLinks = sourceDoc.followLinkToggle || sourceDoc.followAllLinks ? linkDocList : linkDocList.slice(0, 1);
let count = 0;
@@ -82,7 +82,7 @@ export class LinkFollower {
willZoomCentered: BoolCast(srcAnchor.followLinkZoom, false),
zoomTime: NumCast(srcAnchor.followLinkTransitionTime, 500),
zoomScale: Cast(srcAnchor.followLinkZoomScale, 'number', null),
- easeFunc: StrCast(srcAnchor.followLinkEase, 'ease') as any,
+ easeFunc: StrCast(srcAnchor.followLinkEase, 'ease') as 'ease' | 'linear',
openLocation: StrCast(srcAnchor.followLinkLocation, OpenWhere.lightbox) as OpenWhere,
effect: srcAnchor,
zoomTextSelections: BoolCast(srcAnchor.followLinkZoomText),
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 56d5dce4e..e11482572 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -31,13 +31,13 @@ export class LinkManager {
@observable public currentLink: Opt<Doc> = undefined;
@observable public currentLinkAnchor: Opt<Doc> = undefined;
public static get Instance(): LinkManager {
- return Doc.UserDoc() ? LinkManager._instance ?? new LinkManager() : (undefined as any as LinkManager);
+ return Doc.UserDoc() ? (LinkManager._instance ?? new LinkManager()) : (undefined as unknown as LinkManager);
}
public static Links(doc: Doc | undefined) {
return doc ? LinkManager.Instance.getAllRelatedLinks(doc) : [];
}
- public addLinkDB = async (linkDb: any) => {
+ public addLinkDB = async (linkDb: Doc) => {
await Promise.all(
((await DocListCastAsync(linkDb.data)) ?? []).map(link =>
// makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager
@@ -95,35 +95,24 @@ export class LinkManager {
const watchUserLinkDB = (userLinkDBDoc: Doc) => {
const toRealField = (field: FieldType) => (field instanceof ProxyField ? field.value : field); // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields
if (userLinkDBDoc.data) {
+ // observe pushes/splices on a user link DB 'data' field (should only happen for local changes)
observe(
- userLinkDBDoc.data,
+ userLinkDBDoc.data as unknown as Doc[],
change => {
- // observe pushes/splices on a user link DB 'data' field (should only happen for local changes)
- switch (change.type as any) {
+ switch (change.type) {
case 'splice':
- (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link)));
- (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link)));
+ change.added.forEach(link => addLinkToDoc(toRealField(link)));
+ change.removed.forEach(link => remLinkFromDoc(toRealField(link)));
break;
- case 'update': // let oldValue = change.oldValue;
- default:
- }
- },
- true
- );
- observe(
- userLinkDBDoc,
- 'data', // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link)
- change => {
- switch (change.type as any) {
case 'update':
- Promise.all([...((change.oldValue as any as Doc[]) || []), ...((change.newValue as any as Doc[]) || [])]).then(doclist => {
- const oldDocs = doclist.slice(0, ((change.oldValue as any as Doc[]) || []).length);
- const newDocs = doclist.slice(((change.oldValue as any as Doc[]) || []).length, doclist.length);
+ Promise.all([...((change.oldValue as unknown as Doc[]) || []), ...((change.newValue as unknown as Doc[]) || [])]).then(doclist => {
+ const oldDocs = doclist.slice(0, ((change.oldValue as unknown as Doc[]) || []).length);
+ const newDocs = doclist.slice(((change.oldValue as unknown as Doc[]) || []).length, doclist.length);
const added = newDocs?.filter(link => !(oldDocs || []).includes(link));
const removed = oldDocs?.filter(link => !(newDocs || []).includes(link));
- added?.forEach((link: any) => addLinkToDoc(toRealField(link)));
- removed?.forEach((link: any) => remLinkFromDoc(toRealField(link)));
+ added?.forEach(link => addLinkToDoc(toRealField(link)));
+ removed?.forEach(link => remLinkFromDoc(toRealField(link)));
});
break;
default:
@@ -136,9 +125,9 @@ export class LinkManager {
observe(
this.userLinkDBs,
change => {
- switch (change.type as any) {
+ switch (change.type) {
case 'splice':
- (change as any).added.forEach(watchUserLinkDB);
+ change.added.forEach(watchUserLinkDB);
break;
case 'update': // let oldValue = change.oldValue;
default:
@@ -188,7 +177,7 @@ export class LinkManager {
return [];
}
- const dirLinks = Array.from(anchor[DocData][DirectLinks]).filter(l => Doc.GetProto(anchor) === anchor[DocData] || ['1', '2'].includes(LinkManager.anchorIndex(l, anchor) as any));
+ const dirLinks = Array.from(anchor[DocData][DirectLinks]).filter(l => Doc.GetProto(anchor) === anchor[DocData] || ['1', '2'].includes(LinkManager.anchorIndex(l, anchor) as '0' | '1' | '2'));
const anchorRoot = DocCast(anchor.rootDocument, anchor); // template Doc fields store annotations on the topmost root of a template (not on themselves since the template layout items are only for layout)
const annos = DocListCast(anchorRoot[Doc.LayoutFieldKey(anchor) + '_annotations']);
return Array.from(
@@ -283,7 +272,7 @@ export function UPDATE_SERVER_CACHE() {
ScriptingGlobals.add(
// eslint-disable-next-line prefer-arrow-callback
- function links(doc: any) {
+ function links(doc: Doc) {
return new List(LinkManager.Links(doc));
},
'returns all the links to the document or its annotations',
diff --git a/src/client/util/ProsemirrorCopy/prompt.js b/src/client/util/ProsemirrorCopy/prompt.js
deleted file mode 100644
index b9068195f..000000000
--- a/src/client/util/ProsemirrorCopy/prompt.js
+++ /dev/null
@@ -1,179 +0,0 @@
-const prefix = "ProseMirror-prompt"
-
-export function openPrompt(options) {
- let wrapper = document.body.appendChild(document.createElement("div"))
- wrapper.className = prefix
- wrapper.style.zIndex = 1000;
- wrapper.style.width = 250;
- wrapper.style.textAlign = "center";
-
- let mouseOutside = e => { if (!wrapper.contains(e.target)) close() }
- setTimeout(() => window.addEventListener("mousedown", mouseOutside), 50)
- let close = () => {
- window.removeEventListener("mousedown", mouseOutside)
- if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper)
- }
-
- let domFields = []
- for (let name in options.fields) domFields.push(options.fields[name].render())
-
- let submitButton = document.createElement("button")
- submitButton.type = "submit"
- submitButton.className = prefix + "-submit"
- submitButton.textContent = "OK"
- let cancelButton = document.createElement("button")
- cancelButton.type = "button"
- cancelButton.className = prefix + "-cancel"
- cancelButton.textContent = "Cancel"
- cancelButton.addEventListener("click", close)
-
- let form = wrapper.appendChild(document.createElement("form"))
- let title = document.createElement("h5")
- title.style.marginBottom = 15
- title.style.marginTop = 10
- if (options.title) form.appendChild(title).textContent = options.title
- domFields.forEach(field => {
- form.appendChild(document.createElement("div")).appendChild(field)
- })
- let b = document.createElement("div");
- b.style.marginTop = 15;
- let buttons = form.appendChild(b)
- // buttons.className = prefix + "-buttons"
- buttons.appendChild(submitButton)
- buttons.appendChild(document.createTextNode(" "))
- buttons.appendChild(cancelButton)
-
- let box = wrapper.getBoundingClientRect()
- wrapper.style.top = options.flyout_top + "px"
- wrapper.style.left = options.flyout_left + "px"
-
- let submit = () => {
- let params = getValues(options.fields, domFields)
- if (params) {
- close()
- options.callback(params)
- }
- }
-
- form.addEventListener("submit", e => {
- e.preventDefault()
- submit()
- })
-
- form.addEventListener("keydown", e => {
- if (e.keyCode == 27) {
- e.preventDefault()
- close()
- } else if (e.keyCode == 13 && !(e.ctrlKey || e.metaKey || e.shiftKey)) {
- e.preventDefault()
- submit()
- } else if (e.keyCode == 9) {
- window.setTimeout(() => {
- if (!wrapper.contains(document.activeElement)) close()
- }, 500)
- }
- })
-
- let input = form.elements[0]
- if (input) input.focus()
-}
-
-function getValues(fields, domFields) {
- let result = Object.create(null), i = 0
- for (let name in fields) {
- let field = fields[name], dom = domFields[i++]
- let value = field.read(dom), bad = field.validate(value)
- if (bad) {
- reportInvalid(dom, bad)
- return null
- }
- result[name] = field.clean(value)
- }
- return result
-}
-
-function reportInvalid(dom, message) {
- // FIXME this is awful and needs a lot more work
- let parent = dom.parentNode
- let msg = parent.appendChild(document.createElement("div"))
- msg.style.left = (dom.offsetLeft + dom.offsetWidth + 2) + "px"
- msg.style.top = (dom.offsetTop - 5) + "px"
- msg.className = "ProseMirror-invalid"
- msg.textContent = message
- setTimeout(() => parent.removeChild(msg), 1500)
-}
-
-// ::- The type of field that `FieldPrompt` expects to be passed to it.
-export class Field {
- // :: (Object)
- // Create a field with the given options. Options support by all
- // field types are:
- //
- // **`value`**`: ?any`
- // : The starting value for the field.
- //
- // **`label`**`: string`
- // : The label for the field.
- //
- // **`required`**`: ?bool`
- // : Whether the field is required.
- //
- // **`validate`**`: ?(any) → ?string`
- // : A function to validate the given value. Should return an
- // error message if it is not valid.
- constructor(options) { this.options = options }
-
- // render:: (state: EditorState, props: Object) → dom.Node
- // Render the field to the DOM. Should be implemented by all subclasses.
-
- // :: (dom.Node) → any
- // Read the field's value from its DOM node.
- read(dom) { return dom.value }
-
- // :: (any) → ?string
- // A field-type-specific validation function.
- validateType(_value) { }
-
- validate(value) {
- if (!value && this.options.required)
- return "Required field"
- return this.validateType(value) || (this.options.validate && this.options.validate(value))
- }
-
- clean(value) {
- return this.options.clean ? this.options.clean(value) : value
- }
-}
-
-// ::- A field class for single-line text fields.
-export class TextField extends Field {
- render() {
- let input = document.createElement("input")
- input.type = "text"
- input.placeholder = this.options.label
- input.value = this.options.value || ""
- input.autocomplete = "off"
- input.style.marginBottom = 4
- input.style.border = "1px solid black"
- input.style.padding = "4px 4px"
- return input
- }
-}
-
-
-// ::- A field class for dropdown fields based on a plain `<select>`
-// tag. Expects an option `options`, which should be an array of
-// `{value: string, label: string}` objects, or a function taking a
-// `ProseMirror` instance and returning such an array.
-export class SelectField extends Field {
- render() {
- let select = document.createElement("select")
- this.options.options.forEach(o => {
- let opt = select.appendChild(document.createElement("option"))
- opt.value = o.value
- opt.selected = o.value == this.options.value
- opt.label = o.label
- })
- return select
- }
-}
diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx
index a07ad2047..a01b64eda 100644
--- a/src/client/util/RTFMarkup.tsx
+++ b/src/client/util/RTFMarkup.tsx
@@ -5,7 +5,7 @@ import { MainViewModal } from '../views/MainViewModal';
import { SnappingManager } from './SnappingManager';
@observer
-export class RTFMarkup extends React.Component<{}> {
+export class RTFMarkup extends React.Component<object> {
// eslint-disable-next-line no-use-before-define
static Instance: RTFMarkup;
@observable private isOpen = false; // whether the SharingManager modal is open or not
@@ -14,7 +14,7 @@ export class RTFMarkup extends React.Component<{}> {
this.isOpen = status;
});
- constructor(props: {}) {
+ constructor(props: object) {
super(props);
makeObservable(this);
RTFMarkup.Instance = this;
diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts
index c5afe549c..62a09a8bc 100644
--- a/src/client/util/ReplayMovements.ts
+++ b/src/client/util/ReplayMovements.ts
@@ -7,11 +7,12 @@ import { SnappingManager } from './SnappingManager';
import { Movement, Presentation } from './TrackMovements';
import { ViewBoxInterface } from '../views/ViewBoxInterface';
import { StrCast } from '../../fields/Types';
+import { FieldViewProps } from '../views/nodes/FieldView';
export class ReplayMovements {
private timers: NodeJS.Timeout[] | null;
private videoBoxDisposeFunc: IReactionDisposer | null;
- private videoBox: ViewBoxInterface<any> | null;
+ private videoBox: ViewBoxInterface<FieldViewProps> | null;
private isPlaying: boolean;
// create static instance and getter for global use
@@ -62,7 +63,7 @@ export class ReplayMovements {
this.timers?.map(timer => clearTimeout(timer));
};
- setVideoBox = async (videoBox: ViewBoxInterface<any>) => {
+ setVideoBox = async (videoBox: ViewBoxInterface<FieldViewProps>) => {
if (this.videoBox !== null) {
console.warn('setVideoBox on already videoBox');
}
@@ -147,7 +148,7 @@ export class ReplayMovements {
// generate a set of all unique docIds
const docIdtoFirstMove = new Map<Doc, Movement>();
movements.forEach(move => {
- if (!docIdtoFirstMove.has(move.doc)) docIdtoFirstMove.set(move.doc, move);
+ if (!docIdtoFirstMove.has(move.doc as Doc)) docIdtoFirstMove.set(move.doc as Doc, move);
});
return docIdtoFirstMove;
};
@@ -175,8 +176,8 @@ export class ReplayMovements {
const handleFirstMovements = () => {
// if the first movement is a closed tab, open it
const firstMovement = filteredMovements[0];
- const isClosed = this.getCollectionFFView(firstMovement.doc) === undefined;
- if (isClosed) this.openTab(firstMovement.doc);
+ const isClosed = this.getCollectionFFView(firstMovement.doc as Doc) === undefined;
+ if (isClosed) this.openTab(firstMovement.doc as Doc);
// for the open tabs, set it to the first move
const docIdtoFirstMove = this.getFirstMovements(filteredMovements);
@@ -192,12 +193,12 @@ export class ReplayMovements {
const timeDiff = movement.time - timeViewed * 1000;
return setTimeout(() => {
- const collectionFFView = this.getCollectionFFView(movement.doc);
+ const collectionFFView = this.getCollectionFFView(movement.doc as Doc);
if (collectionFFView) {
this.zoomAndPan(movement, collectionFFView);
} else {
// tab wasn't open - open it and play the movement
- const openedColFFView = this.openTab(movement.doc);
+ const openedColFFView = this.openTab(movement.doc as Doc);
openedColFFView && this.zoomAndPan(movement, openedColFFView);
}
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 6948469cc..c63d3d7cb 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -1,11 +1,7 @@
-/* eslint-disable import/no-unresolved */
-/* eslint-disable import/no-webpack-loader-syntax */
// export const ts = (window as any).ts;
-// // @ts-ignore
// import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts'
// import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts'
-// eslint-disable-next-line node/no-unpublished-import
-import * as typescriptlib from '!!raw-loader!./type_decls.d';
+import typescriptlib from 'type_decls.d';
import * as ts from 'typescript';
import { Doc, FieldType } from '../../fields/Doc';
import { RefField } from '../../fields/RefField';
@@ -16,13 +12,13 @@ export { ts };
export interface ScriptSuccess {
success: true;
- result: any;
+ result: unknown;
}
export interface ScriptError {
success: false;
- error: any;
- result: any;
+ error: unknown;
+ result: unknown;
}
export type ScriptResult = ScriptSuccess | ScriptError;
@@ -34,12 +30,12 @@ export interface CompiledScript {
readonly originalScript: string;
// eslint-disable-next-line no-use-before-define
readonly options: Readonly<ScriptOptions>;
- run(args?: { [name: string]: any }, onError?: (res: any) => void, errorVal?: any): ScriptResult;
+ run(args?: { [name: string]: unknown }, onError?: (res: string) => void, errorVal?: unknown): ScriptResult;
}
export interface CompileError {
compiled: false;
- errors: any[];
+ errors: ts.Diagnostic[];
}
export type CompileResult = CompiledScript | CompileError;
@@ -51,7 +47,7 @@ export function isCompileError(toBeDetermined: CompileResult): toBeDetermined is
}
// eslint-disable-next-line no-use-before-define
-function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult {
+function Run(script: string | undefined, customParams: string[], diagnostics: ts.Diagnostic[], originalScript: string, options: ScriptOptions): CompileResult {
const errors = diagnostics.filter(diag => diag.category === ts.DiagnosticCategory.Error);
if ((options.typecheck !== false && errors.length) || !script) {
return { compiled: false, errors };
@@ -74,8 +70,8 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
if (!compiledFunction) return { compiled: false, errors };
const { capturedVariables = {} } = options;
// eslint-disable-next-line default-param-last
- const run = (args: { [name: string]: any } = {}, onError?: (e: any) => void, errorVal?: any): ScriptResult => {
- const argsArray: any[] = [];
+ const run = (args: { [name: string]: unknown } = {}, onError?: (e: string) => void, errorVal?: ts.Diagnostic): ScriptResult => {
+ const argsArray: unknown[] = [];
// eslint-disable-next-line no-restricted-syntax
for (const name of customParams) {
if (name !== 'this') {
@@ -94,7 +90,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an
return { success: true, result };
} catch (error) {
batch?.end();
- onError?.(script + ' ' + error);
+ onError?.(script + ' ' + (error as string).toString());
return { success: false, error, result: errorVal };
}
};
@@ -111,7 +107,7 @@ class ScriptingCompilerHost {
files: File[] = [];
// getSourceFile(fileName: string, languageVersion: ts.ScriptTarget, onError?: ((message: string) => void) | undefined, shouldCreateNewSourceFile?: boolean | undefined): ts.SourceFile | undefined {
- getSourceFile(fileName: string, languageVersion: any /* , onError?: ((message: string) => void) | undefined, shouldCreateNewSourceFile?: boolean | undefined */): any | undefined {
+ getSourceFile(fileName: string, languageVersion: ts.ScriptTarget | ts.CreateSourceFileOptions /* , onError?: ((message: string) => void) | undefined, shouldCreateNewSourceFile?: boolean | undefined */): ts.SourceFile | undefined {
const contents = this.readFile(fileName);
if (contents !== undefined) {
return ts.createSourceFile(fileName, contents, languageVersion, true);
@@ -165,18 +161,19 @@ export interface ScriptOptions {
requiredType?: string; // does function required a typed return value
addReturn?: boolean; // does the compiler automatically add a return statement
params?: { [name: string]: string }; // list of function parameters and their types
- capturedVariables?: { [name: string]: Doc | number | string | boolean }; // list of captured variables
+ capturedVariables?: { [name: string]: Doc | number | string | boolean | undefined }; // list of captured variables
typecheck?: boolean; // should the compiler perform typechecking
editable?: boolean; // can the script edit Docs
traverser?: TraverserParam;
transformer?: Transformer; // does the editor display a text label by each document that can be used as a captured document reference
- globals?: { [name: string]: any };
+ globals?: { [name: string]: unknown };
}
// function forEachNode(node:ts.Node, fn:(node:any) => void);
function forEachNode(node: ts.Node, onEnter: Traverser, onExit?: Traverser, indentation = '') {
return (
onEnter(node, indentation) ||
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
ts.forEachChild(node, (n: any) => {
forEachNode(n, onEnter, onExit, indentation + ' ');
}) ||
@@ -187,8 +184,9 @@ function forEachNode(node: ts.Node, onEnter: Traverser, onExit?: Traverser, inde
export function CompileScript(script: string, options: ScriptOptions = {}): CompileResult {
const captured = options.capturedVariables ?? {};
const signature = Object.keys(captured).reduce((p, v) => {
- const formatCapture = (obj: any) => `${v}=${obj instanceof RefField ? 'XXX' : obj.toString()}`;
- if (captured[v] instanceof Array) return p + (captured[v] as any).map(formatCapture);
+ const formatCapture = (obj: FieldType | undefined) => `${v}=${obj instanceof RefField ? 'XXX' : obj?.toString()}`;
+ const captureVal = captured[v];
+ if (captureVal instanceof Array) return p + captureVal.map(formatCapture);
return p + formatCapture(captured[v]);
}, '');
const found = ScriptField.GetScriptFieldCache(script + ':' + signature);
@@ -250,7 +248,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
const funcScript = `(function(${paramString})${reqTypes} { ${body} })`;
host.writeFile('file.ts', funcScript);
- if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib.default);
+ if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
const program = ts.createProgram(['file.ts'], {}, host);
const testResult = program.emit();
const outputText = host.readFile('file.js');
diff --git a/src/client/util/ScriptingGlobals.ts b/src/client/util/ScriptingGlobals.ts
index ac524394a..444e8fc0a 100644
--- a/src/client/util/ScriptingGlobals.ts
+++ b/src/client/util/ScriptingGlobals.ts
@@ -2,23 +2,22 @@ import ts from 'typescript';
export { ts };
-const _scriptingGlobals: { [name: string]: any } = {};
-const _scriptingDescriptions: { [name: string]: any } = {};
-const _scriptingParams: { [name: string]: any } = {};
-// eslint-disable-next-line import/no-mutable-exports
-export let scriptingGlobals: { [name: string]: any } = _scriptingGlobals;
+const _scriptingGlobals: { [name: string]: unknown } = {};
+const _scriptingDescriptions: { [name: string]: string } = {};
+const _scriptingParams: { [name: string]: string } = {};
+export let scriptingGlobals: { [name: string]: unknown } = _scriptingGlobals;
+
export namespace ScriptingGlobals {
export function getGlobals() { return Object.keys(_scriptingGlobals); } // prettier-ignore
export function getGlobalObj() { return _scriptingGlobals; } // prettier-ignore
export function getDescriptions() { return _scriptingDescriptions; } // prettier-ignore
export function getParameters() { return _scriptingParams; } // prettier-ignore
- export function add(global: { name: string }): void;
- export function add(name: string, global: any): void;
- export function add(global: { name: string }, decription?: string, params?: string): void;
- export function add(first: any, second?: any, third?: string) {
- let n: any;
- let obj: any;
+ export function add(name: string, namespace_func_or_object: unknown): void;
+ export function add(func: { name: string }, description?: string, params?: string): void;
+ export function add(first: string | { name: string }, second?: unknown, params?: string): void {
+ let n: string = '';
+ let obj: unknown;
if (second !== undefined) {
if (typeof first === 'string') {
@@ -27,32 +26,32 @@ export namespace ScriptingGlobals {
} else {
obj = first;
n = first.name;
- _scriptingDescriptions[n] = second;
- if (third !== undefined) {
- _scriptingParams[n] = third;
+ _scriptingDescriptions[n] = second as string;
+ if (params !== undefined) {
+ _scriptingParams[n] = params;
}
}
- } else if (first && typeof first.name === 'string') {
+ } else if (first instanceof Object && 'name' in first && typeof first.name === 'string') {
n = first.name;
obj = first;
} else {
throw new Error('Must either register an object with a name, or give a name and an object');
}
if (n === undefined || n === 'undefined') {
- return false;
+ return; // false;
}
// eslint-disable-next-line no-prototype-builtins
if (_scriptingGlobals.hasOwnProperty(n)) {
throw new Error(`Global with name ${n} is already registered, choose another name`);
}
_scriptingGlobals[n] = obj;
- return true;
+ return; // true;
}
- export function makeMutableGlobalsCopy(globals?: { [name: string]: any }) {
+ export function makeMutableGlobalsCopy(globals?: { [name: string]: unknown }) {
return { ..._scriptingGlobals, ...(globals || {}) };
}
- export function setScriptingGlobals(globals: { [key: string]: any }) {
+ export function setScriptingGlobals(globals: { [key: string]: unknown }) {
scriptingGlobals = globals;
}
@@ -75,11 +74,12 @@ export namespace ScriptingGlobals {
}
// const types = Object.keys(ts.SyntaxKind).map(kind => ts.SyntaxKind[kind]);
- export function printNodeType(node: any, indentation = '') {
+ export function printNodeType(node: ts.Node, indentation = '') {
console.log(indentation + ts.SyntaxKind[node.kind]);
}
}
-export function scriptingGlobal(constructor: { new (...args: any[]): any }) {
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export function scriptingGlobal(constructor: { new (...args: any[]): unknown }) {
ScriptingGlobals.add(constructor);
}
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 609fedfa9..733eae5f4 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -22,6 +22,7 @@ export namespace SearchUtil {
const results = new ObservableMap<Doc, string[]>();
if (collectionDoc) {
const docs = DocListCast(collectionDoc[Doc.LayoutFieldKey(collectionDoc)]);
+ // eslint-disable-next-line @typescript-eslint/ban-types
const docIDs: String[] = [];
SearchUtil.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => {
const dtype = StrCast(doc.type) as DocumentType;
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 0b942116c..1ab84421c 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -115,7 +115,7 @@ ScriptingGlobals.add(function redo() {
return UndoManager.Redo();
});
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
+ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: Doc[]) {
const docs = SelectionManager.Docs().filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)));
return docs.length ? new List(docs) : prevValue;
});
diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts
index d9d22437c..ccb02fb79 100644
--- a/src/client/util/SerializationHelper.ts
+++ b/src/client/util/SerializationHelper.ts
@@ -1,14 +1,15 @@
import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema } from 'serializr';
+import Context from 'serializr/lib/core/Context';
// import { Field } from '../../fields/Doc';
let serializing = 0;
-export function afterDocDeserialize(cb: (err: any, val: any) => void, err: any, newValue: any) {
+export function afterDocDeserialize(cb: (err: unknown, val: unknown) => void, err: unknown, newValue: unknown) {
serializing++;
cb(err, newValue);
serializing--;
}
-const serializationTypes: { [name: string]: { ctor: { new (): any }; afterDeserialize?: (obj: any) => void | Promise<any> } } = {};
+const serializationTypes: { [name: string]: { ctor: { new (): unknown }; afterDeserialize?: (obj: unknown) => void | Promise<unknown> } } = {};
const reverseMap: { [ctor: string]: string } = {};
export namespace SerializationHelper {
@@ -16,7 +17,7 @@ export namespace SerializationHelper {
return serializing > 0;
}
- export function Serialize(obj: any /* Field */): any {
+ export function Serialize(obj: unknown /* Field */): unknown {
if (obj === undefined || obj === null) {
return null;
}
@@ -37,7 +38,7 @@ export namespace SerializationHelper {
return json;
}
- export async function Deserialize(obj: any): Promise<any> {
+ export async function Deserialize(obj: unknown): Promise<unknown> {
if (obj === undefined || obj === null) {
return undefined;
}
@@ -46,16 +47,17 @@ export namespace SerializationHelper {
return obj;
}
- if (!obj.__type) {
- console.warn("No property 'type' found in JSON.");
+ const objtype = '__type' in obj ? (obj.__type as string) : undefined;
+ if (!objtype) {
+ console.warn(`No property ${objtype} found in JSON.`);
return undefined;
}
- if (!(obj.__type in serializationTypes)) {
- throw Error(`type '${obj.__type}' not registered. Make sure you register it using a @Deserializable decorator`);
+ if (!(objtype in serializationTypes)) {
+ throw Error(`type '${objtype}' not registered. Make sure you register it using a @Deserializable decorator`);
}
- const type = serializationTypes[obj.__type];
+ const type = serializationTypes[objtype];
const value = await new Promise(res => {
deserialize(type.ctor, obj, (err, result) => res(result));
});
@@ -65,11 +67,12 @@ export namespace SerializationHelper {
}
}
-export function Deserializable(classNameForSerializer: string, afterDeserialize?: (obj: any) => void | Promise<any>, constructorArgs?: [string]): (constructor: { new (...args: any[]): any }) => void {
- function addToMap(className: string, Ctor: { new (...args: any[]): any }) {
- const schema = getDefaultModelSchema(Ctor) as any;
- if (schema.targetClass !== Ctor || constructorArgs) {
- setDefaultModelSchema(Ctor, { ...schema, factory: (context: any) => new Ctor(...(constructorArgs ?? []).map(arg => context.json[arg])) });
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export function Deserializable(classNameForSerializer: string, afterDeserialize?: (obj: unknown) => void | Promise<unknown>, constructorArgs?: [string]): (constructor: { new (...args: any[]): any }) => void {
+ function addToMap(className: string, Ctor: { new (...args: unknown[]): unknown }) {
+ const schema = getDefaultModelSchema(Ctor);
+ if (schema && (schema.targetClass !== Ctor || constructorArgs)) {
+ setDefaultModelSchema(Ctor, { ...schema, factory: (context: Context) => new Ctor(...(constructorArgs ?? []).map(arg => context.json[arg])) });
}
if (!(className in serializationTypes)) {
serializationTypes[className] = { ctor: Ctor, afterDeserialize };
@@ -78,12 +81,12 @@ export function Deserializable(classNameForSerializer: string, afterDeserialize?
throw new Error(`Name ${className} has already been registered as deserializable`);
}
}
- return (ctor: { new (...args: any[]): any }) => addToMap(classNameForSerializer, ctor);
+ return (ctor: { new (...args: unknown[]): unknown }) => addToMap(classNameForSerializer, ctor);
}
export function autoObject(): PropSchema {
return custom(
s => SerializationHelper.Serialize(s),
- (json: any, context: any, oldValue: any, cb: (err: any, result: any) => void) => SerializationHelper.Deserialize(json).then(res => cb(null, res))
+ (json: object, context: Context, oldValue: unknown, cb: (err: unknown, result: unknown) => void) => SerializationHelper.Deserialize(json).then(res => cb(null, res))
);
}
diff --git a/src/client/util/ServerStats.tsx b/src/client/util/ServerStats.tsx
index 57363663d..11db5ee5e 100644
--- a/src/client/util/ServerStats.tsx
+++ b/src/client/util/ServerStats.tsx
@@ -6,18 +6,29 @@ import './SharingManager.scss';
import { PingManager } from './PingManager';
import { SettingsManager } from './SettingsManager';
+/**
+ * NOTE: this must be kept in synch with UserStats definition in server's DashStats.ts file
+ * UserStats holds the stats associated with a particular user.
+ */
+interface UserStats {
+ socketId: string;
+ username: string;
+ time: string;
+ operations: number;
+ rate: number;
+}
@observer
-export class ServerStats extends React.Component<{}> {
+export class ServerStats extends React.Component<object> {
// 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;
+ @observable _stats: { socketMap: UserStats[]; currentConnections: number } | undefined = undefined;
// private get linkVisible() {
// return this.targetDoc ? this.targetDoc['acl_' + PublicKey] !== SharingPermissions.None : false;
// }
- constructor(props: {}) {
+ constructor(props: object) {
super(props);
makeObservable(this);
ServerStats.Instance = this;
@@ -41,7 +52,7 @@ export class ServerStats extends React.Component<{}> {
<br />
<span>Active users:{this._stats?.socketMap.length}</span>
- {this._stats?.socketMap.map((user: any) => <p>{user.username}</p>)}
+ {this._stats?.socketMap.map(user => <p key={user.username}>{user.username}</p>)}
</div>
</div>
);
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 278931cdd..fde8869e3 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button, ColorPicker, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components';
import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
@@ -234,6 +232,17 @@ export class SettingsManager extends React.Component<object> {
color={SettingsManager.userColor}
/>
<Toggle
+ formLabel="Recognize Face Images"
+ formLabelPlacement="right"
+ toggleType={ToggleType.SWITCH}
+ onClick={() => {
+ Doc.UserDoc().recognizeFaceImages = !Doc.UserDoc().recognizeFaceImages;
+ }}
+ toggleStatus={BoolCast(Doc.UserDoc().recognizeFaceImages)}
+ size={Size.XSMALL}
+ color={SettingsManager.userColor}
+ />
+ <Toggle
formLabel="Show Full Toolbar"
formLabelPlacement="right"
toggleType={ToggleType.SWITCH}
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index c2a52cae9..117d7935e 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,13 +1,10 @@
-/* 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 */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button, IconButton, Size, Type } from 'browndash-components';
import { concat, intersection } from 'lodash';
import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import Select from 'react-select';
+import Select, { MultiValue } from 'react-select';
import * as RequestPromise from 'request-promise';
import { ClientUtils } from '../../ClientUtils';
import { Utils } from '../../Utils';
@@ -27,6 +24,7 @@ import { SearchUtil } from './SearchUtil';
import './SharingManager.scss';
import { SnappingManager } from './SnappingManager';
import { undoable } from './UndoManager';
+import { LinkManager } from './LinkManager';
export interface User {
email: string;
@@ -64,7 +62,7 @@ interface ValidatedUser {
}
@observer
-export class SharingManager extends React.Component<{}> {
+export class SharingManager extends React.Component<object> {
// eslint-disable-next-line no-use-before-define
public static Instance: SharingManager;
private shareDocumentButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // ref for the share button, used for the position of the popup
@@ -90,7 +88,7 @@ export class SharingManager extends React.Component<{}> {
// return this.targetDoc ? this.targetDoc['acl_' + PublicKey] !== SharingPermissions.None : false;
// }
- constructor(props: {}) {
+ constructor(props: object) {
super(props);
makeObservable(this);
SharingManager.Instance = this;
@@ -108,8 +106,8 @@ export class SharingManager extends React.Component<{}> {
* Handles changes in the users selected in react-select
*/
@action
- handleUsersChange = (selectedOptions: any) => {
- this.selectedUsers = selectedOptions as UserOptions[];
+ handleUsersChange = (selectedOptions: MultiValue<UserOptions> /* , actionMeta: ActionMeta<UserOptions> */) => {
+ this.selectedUsers = Array.from(selectedOptions);
};
/**
@@ -490,12 +488,12 @@ export class SharingManager extends React.Component<{}> {
const docs = await DocServer.GetRefFields(raw.reduce((list, user) => [...list, user.sharingDocumentId, user.linkDatabaseId], [] as string[]));
raw.map(
action((newUser: User) => {
- const sharingDoc = docs[newUser.sharingDocumentId];
- const linkDatabase = docs[newUser.linkDatabaseId];
+ const sharingDoc = docs.get(newUser.sharingDocumentId);
+ const linkDatabase = docs.get(newUser.linkDatabaseId);
if (sharingDoc instanceof Doc && linkDatabase instanceof Doc) {
if (!this.users.find(user => user.user.email === newUser.email)) {
this.users.push({ user: newUser, sharingDoc, linkDatabase, userColor: StrCast(sharingDoc.userColor) });
- // LinkManager.addLinkDB(linkDatabase);
+ LinkManager.Instance.addLinkDB(linkDatabase);
}
}
})
@@ -539,9 +537,8 @@ export class SharingManager extends React.Component<{}> {
// eslint-disable-next-line react/no-unused-class-component-methods
shareWithAddedMember = (group: Doc, emailId: string, retry: boolean = true) => {
const user = this.users.find(({ user: { email } }) => email === emailId)!;
- const self = this;
if (group.docsShared) {
- if (!user) retry && this.populateUsers().then(() => self.shareWithAddedMember(group, emailId, false));
+ if (!user) retry && this.populateUsers().then(() => this.shareWithAddedMember(group, emailId, false));
else {
DocListCastAsync(user.sharingDoc[storage]).then(userdocs =>
DocListCastAsync(group.docsShared).then(dl => {
diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts
index cc0366c5b..95ccc7735 100644
--- a/src/client/util/SnappingManager.ts
+++ b/src/client/util/SnappingManager.ts
@@ -79,5 +79,5 @@ export class SnappingManager {
public static userColor: string | undefined;
public static userVariantColor: string | undefined;
public static userBackgroundColor: string | undefined;
- public static SettingsStyle: any;
+ public static SettingsStyle: CSSStyleSheet | null;
}
diff --git a/src/client/util/TrackMovements.ts b/src/client/util/TrackMovements.ts
index 25a3c9ad8..7da0281c0 100644
--- a/src/client/util/TrackMovements.ts
+++ b/src/client/util/TrackMovements.ts
@@ -9,13 +9,13 @@ export type Movement = {
panX: number;
panY: number;
scale: number;
- doc: Doc;
+ doc: Doc | string;
};
export type Presentation = {
movements: Movement[] | null;
totalTime: number;
- meta: Object | Object[];
+ meta: object | object[];
};
export class TrackMovements {
@@ -142,7 +142,7 @@ export class TrackMovements {
);
};
- start = (meta?: Object) => {
+ start = (meta?: object) => {
this.initTabTracker();
// update the presentation mode
@@ -245,7 +245,7 @@ export class TrackMovements {
// these three will lead to the combined presentation
const combinedMovements: Movement[] = [];
let sumTime = 0;
- const combinedMetas: any[] = [];
+ const combinedMetas: (object | object[])[] = [];
presentations.forEach(presentation => {
const { movements, totalTime, meta } = presentation;
diff --git a/src/client/util/TypedEvent.ts b/src/client/util/TypedEvent.ts
index 9ef2aa8d7..345eff00a 100644
--- a/src/client/util/TypedEvent.ts
+++ b/src/client/util/TypedEvent.ts
@@ -1,5 +1,5 @@
export interface Listener<T> {
- (event: T): any;
+ (event: T): unknown;
}
export interface Disposable {
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 534ffd2c8..ce0e7768b 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -5,7 +5,7 @@ import { Without } from '../../Utils';
import { RichTextField } from '../../fields/RichTextField';
import { SnappingManager } from './SnappingManager';
-function getBatchName(target: any, key: string | symbol): string {
+function getBatchName(target: (...args: unknown[]) => unknown, key: string | symbol): string {
const keyName = key.toString();
if (target?.constructor?.name) {
return `${target.constructor.name}.${keyName}`;
@@ -13,19 +13,19 @@ function getBatchName(target: any, key: string | symbol): string {
return keyName;
}
-function propertyDecorator(target: any, key: string | symbol) {
+function propertyDecorator(target: (...args: unknown[]) => unknown, key: string | symbol) {
Object.defineProperty(target, key, {
configurable: true,
enumerable: false,
get: function () {
return 5;
},
- set: function (value: any) {
+ set: function (value: (...args: unknown[]) => unknown) {
Object.defineProperty(this, key, {
enumerable: false,
writable: true,
configurable: true,
- value: function (...args: any[]) {
+ value: function (...args: unknown[]) {
const batch = UndoManager.StartBatch(getBatchName(target, key));
try {
return value.apply(this, args);
@@ -38,7 +38,8 @@ function propertyDecorator(target: any, key: string | symbol) {
});
}
-export function undoable(fn: (...args: any[]) => any, batchName: string): (...args: any[]) => any {
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export function undoable<T>(fn: (...args: any[]) => T, batchName: string): (...args: unknown[]) => T {
return function (...fargs) {
const batch = UndoManager.StartBatch(batchName);
try {
@@ -50,13 +51,12 @@ export function undoable(fn: (...args: any[]) => any, batchName: string): (...ar
};
}
+// eslint-disable-next-line no-redeclare, @typescript-eslint/no-explicit-any
export function undoBatch(target: any, key: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any;
-// eslint-disable-next-line no-redeclare
-export function undoBatch(fn: (...args: any[]) => any): (...args: any[]) => any;
-// eslint-disable-next-line no-redeclare
-export function undoBatch(target: any, key?: string | symbol, descriptor?: TypedPropertyDescriptor<any>): any {
+// eslint-disable-next-line no-redeclare, @typescript-eslint/no-explicit-any
+export function undoBatch(target: any, key?: string | symbol, descriptor?: TypedPropertyDescriptor<(...args: any[]) => unknown>): any {
if (!key) {
- return function (...fargs: any[]) {
+ return function (...fargs: unknown[]) {
const batch = UndoManager.StartBatch('');
try {
return target.apply(undefined, fargs);
@@ -71,10 +71,10 @@ export function undoBatch(target: any, key?: string | symbol, descriptor?: Typed
}
const oldFunction = descriptor.value;
- descriptor.value = function (...args: any[]) {
+ descriptor.value = function (...args: unknown[]) {
const batch = UndoManager.StartBatch(getBatchName(target, key));
try {
- return oldFunction.apply(this, args);
+ return oldFunction?.apply(this, args);
} finally {
batch.end();
}
@@ -99,12 +99,12 @@ export namespace UndoManager {
export const undoStack: UndoBatch[] = observable([]);
export const redoStack: UndoBatch[] = observable([]);
export const batchCounter = observable.box(0);
- let _fieldPrinter: (val: any) => string = val => val?.toString();
- export function SetFieldPrinter(printer: (val: any) => string) {
+ let _fieldPrinter: (val: unknown) => string = val => val?.toString?.() || '';
+ export function SetFieldPrinter(printer: (val: unknown) => string) {
_fieldPrinter = printer;
}
- export function AddEvent(event: UndoEvent, value?: any): void {
+ export function AddEvent(event: UndoEvent, value?: unknown): void {
if (currentBatch && batchCounter.get() && !undoing) {
SnappingManager.PrintToConsole &&
console.log(
@@ -220,7 +220,7 @@ export namespace UndoManager {
batch.end();
}
}
- export const UndoTempBatch = action((success: any) => {
+ export const UndoTempBatch = action((success: boolean) => {
if (tempEvents && !success) {
undoing = true;
for (let i = tempEvents.length - 1; i >= 0; i--) {
@@ -243,7 +243,6 @@ export namespace UndoManager {
}
undoing = true;
- // eslint-disable-next-line prettier/prettier
commands
.slice()
.reverse()
diff --git a/src/client/util/reportManager/ReportManager.scss b/src/client/util/reportManager/ReportManager.scss
index d82d7fdeb..fd343ac8e 100644
--- a/src/client/util/reportManager/ReportManager.scss
+++ b/src/client/util/reportManager/ReportManager.scss
@@ -96,12 +96,12 @@
transition: all 0.2s ease;
background: transparent;
- &:hover {
- // border-bottom-color: $text-gray;
- }
- &:focus {
- // border-bottom-color: #4476f7;
- }
+ // &:hover {
+ // // border-bottom-color: $text-gray;
+ // }
+ // &:focus {
+ // // border-bottom-color: #4476f7;
+ // }
}
// View issues
diff --git a/src/client/util/reportManager/ReportManager.tsx b/src/client/util/reportManager/ReportManager.tsx
index 2224e642d..c969f9036 100644
--- a/src/client/util/reportManager/ReportManager.tsx
+++ b/src/client/util/reportManager/ReportManager.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/label-has-associated-control */
-/* eslint-disable jsx-a11y/media-has-caption */
/* eslint-disable react/no-unused-class-component-methods */
import { Octokit } from '@octokit/core';
import { Button, Dropdown, DropdownType, IconButton, Type } from 'browndash-components';
@@ -27,7 +25,7 @@ import { BugType, FileData, Priority, ReportForm, ViewState, bugDropdownItems, d
* Class for reporting and viewing Github issues within the app.
*/
@observer
-export class ReportManager extends React.Component<{}> {
+export class ReportManager extends React.Component<object> {
// eslint-disable-next-line no-use-before-define
public static Instance: ReportManager;
@observable private isOpen = false;
@@ -109,7 +107,7 @@ export class ReportManager extends React.Component<{}> {
this.setFetchingIssues(false);
});
- constructor(props: {}) {
+ constructor(props: object) {
super(props);
makeObservable(this);
ReportManager.Instance = this;
diff --git a/src/client/util/reportManager/ReportManagerComponents.tsx b/src/client/util/reportManager/ReportManagerComponents.tsx
index cecebc648..92f877859 100644
--- a/src/client/util/reportManager/ReportManagerComponents.tsx
+++ b/src/client/util/reportManager/ReportManagerComponents.tsx
@@ -1,8 +1,5 @@
/* 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 ReactMarkdown from 'react-markdown';
@@ -98,7 +95,7 @@ export function IssueCard({ issue, onSelect }: IssueCardProps) {
<label className="issue-label">#{issue.number}</label>
<div className="issue-tags">
{issue.labels.map(label => {
- const labelString = typeof label === 'string' ? label : label.name ?? '';
+ const labelString = typeof label === 'string' ? label : (label.name ?? '');
const colors = getLabelColors(labelString);
return <Tag key={labelString} text={labelString} backgroundColor={colors[0]} color={colors[1]} />;
})}
@@ -295,14 +292,16 @@ export function IssueView({ issue }: IssueViewProps) {
<div>
<div className="issue-tags">
{issue.labels.map(label => {
- const labelString = typeof label === 'string' ? label : label.name ?? '';
+ const labelString = typeof label === 'string' ? label : (label.name ?? '');
const colors = getLabelColors(labelString);
return <Tag key={labelString} text={labelString} backgroundColor={colors[0]} color={colors[1]} fontSize="12px" />;
})}
</div>
</div>
)}
- <ReactMarkdown children={issueBody} className="issue-content" remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]} />
+ <ReactMarkdown className="issue-content" remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
+ {issueBody}
+ </ReactMarkdown>
</div>
);
}
diff --git a/src/client/util/reportManager/reportManagerSchema.ts b/src/client/util/reportManager/reportManagerSchema.ts
index 171c24393..7162371e3 100644
--- a/src/client/util/reportManager/reportManagerSchema.ts
+++ b/src/client/util/reportManager/reportManagerSchema.ts
@@ -66,7 +66,7 @@ export interface Issue {
*/
url: string;
user: null | TentacledSimpleUser;
- [property: string]: any;
+ [property: string]: unknown;
}
/**
@@ -94,7 +94,7 @@ export interface PurpleSimpleUser {
subscriptions_url: string;
type: string;
url: string;
- [property: string]: any;
+ [property: string]: unknown;
}
/**
@@ -122,7 +122,7 @@ export interface AssigneeElement {
subscriptions_url: string;
type: string;
url: string;
- [property: string]: any;
+ [property: string]: unknown;
}
/**
@@ -164,7 +164,7 @@ export interface FluffySimpleUser {
subscriptions_url: string;
type: string;
url: string;
- [property: string]: any;
+ [property: string]: unknown;
}
export interface LabelObject {
@@ -175,7 +175,7 @@ export interface LabelObject {
name?: string;
node_id?: string;
url?: string;
- [property: string]: any;
+ [property: string]: unknown;
}
/**
@@ -207,7 +207,7 @@ export interface Milestone {
title: string;
updated_at: Date;
url: string;
- [property: string]: any;
+ [property: string]: unknown;
}
/**
@@ -235,7 +235,7 @@ export interface MilestoneSimpleUser {
subscriptions_url: string;
type: string;
url: string;
- [property: string]: any;
+ [property: string]: unknown;
}
/**
@@ -288,7 +288,7 @@ export interface GitHubApp {
slug?: string;
updated_at: Date;
webhook_secret?: null | string;
- [property: string]: any;
+ [property: string]: unknown;
}
/**
@@ -316,7 +316,7 @@ export interface GitHubAppSimpleUser {
subscriptions_url: string;
type: string;
url: string;
- [property: string]: any;
+ [property: string]: unknown;
}
/**
@@ -336,7 +336,7 @@ export interface PullRequest {
merged_at?: Date | null;
patch_url: null | string;
url: null | string;
- [property: string]: any;
+ [property: string]: unknown;
}
export interface ReactionRollup {
@@ -350,7 +350,7 @@ export interface ReactionRollup {
rocket: number;
total_count: number;
url: string;
- [property: string]: any;
+ [property: string]: unknown;
}
/**
@@ -562,7 +562,7 @@ export interface Repository {
* Whether to require contributors to sign off on web-based commits
*/
web_commit_signoff_required?: boolean;
- [property: string]: any;
+ [property: string]: unknown;
}
/**
@@ -575,7 +575,7 @@ export interface LicenseSimple {
node_id: string;
spdx_id: null | string;
url: null | string;
- [property: string]: any;
+ [property: string]: unknown;
}
/**
@@ -628,7 +628,7 @@ export interface RepositorySimpleUser {
subscriptions_url: string;
type: string;
url: string;
- [property: string]: any;
+ [property: string]: unknown;
}
/**
@@ -656,7 +656,7 @@ export interface OwnerObject {
subscriptions_url: string;
type: string;
url: string;
- [property: string]: any;
+ [property: string]: unknown;
}
export interface RepositoryPermissions {
@@ -665,7 +665,7 @@ export interface RepositoryPermissions {
pull: boolean;
push: boolean;
triage?: boolean;
- [property: string]: any;
+ [property: string]: unknown;
}
/**
@@ -809,7 +809,7 @@ export interface TemplateRepository {
use_squash_pr_title_as_default?: boolean;
visibility?: string;
watchers_count?: number;
- [property: string]: any;
+ [property: string]: unknown;
}
export interface Owner {
@@ -831,7 +831,7 @@ export interface Owner {
subscriptions_url?: string;
type?: string;
url?: string;
- [property: string]: any;
+ [property: string]: unknown;
}
export interface TemplateRepositoryPermissions {
@@ -840,7 +840,7 @@ export interface TemplateRepositoryPermissions {
pull?: boolean;
push?: boolean;
triage?: boolean;
- [property: string]: any;
+ [property: string]: unknown;
}
export enum StateReason {
@@ -874,5 +874,5 @@ export interface TentacledSimpleUser {
subscriptions_url: string;
type: string;
url: string;
- [property: string]: any;
+ [property: string]: unknown;
}
diff --git a/src/client/util/reportManager/reportManagerUtils.ts b/src/client/util/reportManager/reportManagerUtils.ts
index f14967e0a..d51418cbe 100644
--- a/src/client/util/reportManager/reportManagerUtils.ts
+++ b/src/client/util/reportManager/reportManagerUtils.ts
@@ -3,6 +3,7 @@
import { Octokit } from '@octokit/core';
import { Networking } from '../../Network';
import { Issue } from './reportManagerSchema';
+import { Upload } from '../../../server/SharedMediaTypes';
// enums and interfaces
@@ -53,7 +54,7 @@ export const emptyReportForm = {
* Fetches issues from Github.
* @returns array of all issues
*/
-export const getAllIssues = async (octokit: Octokit): Promise<any[]> => {
+export const getAllIssues = async (octokit: Octokit): Promise<unknown[]> => {
const res = await octokit.request('GET /repos/{owner}/{repo}/issues', {
owner: 'brown-dash',
repo: 'Dash-Web',
@@ -103,7 +104,10 @@ export const fileLinktoServerLink = (fileLink: string): string => {
* @param link response from file upload
* @returns server file path
*/
-export const getServerPath = (link: any): string => link.result.accessPaths.agnostic.server as string;
+export const getServerPath = (link: Upload.FileResponse<Upload.FileInformation>): string => {
+ if (link.result instanceof Error) return '';
+ return link.result.accessPaths.agnostic.server;
+};
/**
* Uploads media files to the server.
@@ -114,11 +118,11 @@ export const uploadFilesToServer = async (mediaFiles: FileData[]): Promise<strin
// need to always upload to browndash
const links = await Networking.UploadFilesToServer(mediaFiles.map(file => ({ file: file.file })));
return (links ?? []).map(getServerPath).map(fileLinktoServerLink);
- } catch (err) {
- if (err instanceof Error) {
- alert(err.message);
+ } catch (result) {
+ if (result instanceof Error) {
+ alert(result.message);
} else {
- alert(err);
+ alert(result);
}
}
return undefined;
diff --git a/src/client/util/request-image-size.ts b/src/client/util/request-image-size.ts
index 48cb6e3a5..c619192ed 100644
--- a/src/client/util/request-image-size.ts
+++ b/src/client/util/request-image-size.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-var-requires */
/**
* request-image-size: Detect image dimensions via request.
* Licensed under the MIT license.
@@ -9,43 +10,36 @@
* https://github.com/jo/http-image-size
*/
-const request = require('request');
-const imageSize = require('image-size');
+// const imageSize = require('image-size');
const HttpError = require('standard-http-error');
+import * as request from 'request';
+import { imageSize } from 'image-size';
+import { ISizeCalculationResult } from 'image-size/dist/types/interface';
-module.exports = function requestImageSize(options: any) {
- let opts: any = {
- encoding: null,
- };
-
- if (options && typeof options === 'object') {
- opts = Object.assign(options, opts);
- } else if (options && typeof options === 'string') {
- opts = {
- uri: options,
- ...opts,
- };
- } else {
+module.exports = function requestImageSize(url: string) {
+ if (!url) {
return Promise.reject(new Error('You should provide an URI string or a "request" options object.'));
}
- opts.encoding = null;
-
return new Promise((resolve, reject) => {
- const req = request(opts);
+ const req = request(url);
- req.on('response', (res: any) => {
+ req.on('response', res => {
if (res.statusCode >= 400) {
reject(new HttpError(res.statusCode, res.statusMessage));
return;
}
let buffer = Buffer.from([]);
- let size: any;
+ let size: ISizeCalculationResult;
- res.on('data', (chunk: any) => {
+ res.on('data', chunk => {
buffer = Buffer.concat([buffer, chunk]);
+ });
+ res.on('error', reject);
+
+ res.on('end', () => {
try {
size = imageSize(buffer);
if (size) {
@@ -54,19 +48,12 @@ module.exports = function requestImageSize(options: any) {
}
} catch (err) {
/* empty */
- console.log("Error: ", err)
+ console.log('Error: ', err);
}
- });
-
- res.on('error', reject);
-
- res.on('end', () => {
if (!size) {
reject(new Error('Image has no size'));
return;
}
-
- size.downloaded = buffer.length;
resolve(size);
});
});
diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d
deleted file mode 100644
index 1a93bbe59..000000000
--- a/src/client/util/type_decls.d
+++ /dev/null
@@ -1,224 +0,0 @@
-//@ts-ignore
-declare type PropertyKey = string | number | symbol;
-interface Array<T> {
- length: number;
- toString(): string;
- toLocaleString(): string;
- pop(): T | undefined;
- push(...items: T[]): number;
- concat(...items: ConcatArray<T>[]): T[];
- concat(...items: (T | ConcatArray<T>)[]): T[];
- join(separator?: string): string;
- reverse(): T[];
- shift(): T | undefined;
- slice(start?: number, end?: number): T[];
- sort(compareFn?: (a: T, b: T) => number): this;
- splice(start: number, deleteCount?: number): T[];
- splice(start: number, deleteCount: number, ...items: T[]): T[];
- unshift(...items: T[]): number;
- indexOf(searchElement: T, fromIndex?: number): number;
- lastIndexOf(searchElement: T, fromIndex?: number): number;
- every(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean;
- some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean;
- forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
- map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
- filter<S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];
- filter(callbackfn: (value: T, index: number, array: T[]) => any, thisArg?: any): T[];
- reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
- reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
- reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
- reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
- reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
- reduceRight<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
-
- [n: number]: T;
-}
-
-interface Function {
- apply(this: Function, thisArg: any, argArray?: any): any;
- call(this: Function, thisArg: any, ...argArray: any[]): any;
- bind(this: Function, thisArg: any, ...argArray: any[]): any;
- toString(): string;
-
- prototype: any;
- readonly length: number;
-
- // Non-standard extensions
- arguments: any;
- caller: Function;
-}
-interface Boolean {
- valueOf(): boolean;
-}
-interface Number {
- toString(radix?: number): string;
- toFixed(fractionDigits?: number): string;
- toExponential(fractionDigits?: number): string;
- toPrecision(precision?: number): string;
- valueOf(): number;
-}
-interface IArguments {
- [index: number]: any;
- length: number;
- callee: Function;
-}
-interface RegExp {
- readonly flags: string;
- readonly sticky: boolean;
- readonly unicode: boolean;
-}
-interface Date {
- now() : string;
-}
-interface String {
- codePointAt(pos: number): number | undefined;
- includes(searchString: string, position?: number): boolean;
- endsWith(searchString: string, endPosition?: number): boolean;
- normalize(form: "NFC" | "NFD" | "NFKC" | "NFKD"): string;
- normalize(form?: string): string;
- repeat(count: number): string;
- replace(a:any, b:any):string; // bcz: fix this
- startsWith(searchString: string, position?: number): boolean;
- anchor(name: string): string;
- big(): string;
- blink(): string;
- bold(): string;
- fixed(): string;
- fontcolor(color: string): string;
- fontsize(size: number): string;
- fontsize(size: string): string;
- italics(): string;
- link(url: string): string;
- small(): string;
- strike(): string;
- sub(): string;
- sup(): string;
-}
-interface Object {
- constructor: Function;
- toString(): string;
- toLocaleString(): string;
- valueOf(): Object;
- hasOwnProperty(v: PropertyKey): boolean;
- isPrototypeOf(v: Object): boolean;
- propertyIsEnumerable(v: PropertyKey): boolean;
-}
-interface ConcatArray<T> {
- readonly length: number;
- readonly [n: number]: T;
- join(separator?: string): string;
- slice(start?: number, end?: number): T[];
-}
-interface URL {
- hash: string;
- host: string;
- hostname: string;
- href: string;
- readonly origin: string;
- password: string;
- pathname: string;
- port: string;
- protocol: string;
- search: string;
- username: string;
- toJSON(): string;
-}
-interface PromiseLike<T> {
- then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): PromiseLike<TResult1 | TResult2>;
-}
-interface Promise<T> {
- then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
- catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
-}
-
-declare const Update: unique symbol;
-declare const Self: unique symbol;
-declare const SelfProxy: unique symbol;
-declare const DataSym: unique symbol;
-declare const HandleUpdate: unique symbol;
-declare const Id: unique symbol;
-declare const OnUpdate: unique symbol;
-declare const Parent: unique symbol;
-declare const Copy: unique symbol;
-declare const ToScriptString: unique symbol;
-
-declare abstract class RefField {
- readonly [Id]: FieldId;
-
- constructor();
-}
-
-declare type FieldId = string;
-
-declare abstract class ObjectField {
- abstract [Copy](): ObjectField;
-}
-
-declare abstract class URLField extends ObjectField {
- readonly url: URL;
-
- constructor(url: string);
- constructor(url: URL);
-}
-
-declare class RichTextField extends URLField {
- [Copy](): ObjectField;
- constructor(data:string, text: string);
-}
-declare class AudioField extends URLField { [Copy](): ObjectField; }
-declare class VideoField extends URLField { [Copy](): ObjectField; }
-declare class ImageField extends URLField { [Copy](): ObjectField; }
-declare class WebField extends URLField { [Copy](): ObjectField; }
-declare class PdfField extends URLField { [Copy](): ObjectField; }
-
-declare const ComputedField: any;
-declare const CompileScript: any;
-
-// @ts-ignore
-declare type Extract<T, U> = T extends U ? T : never;
-declare type Field = number | string | boolean | ObjectField | RefField;
-declare type FieldWaiting<T extends RefField = RefField> = T extends undefined ? never : Promise<T | undefined>;
-declare type FieldResult<T extends Field = Field> = Opt<T> | FieldWaiting<Extract<T, RefField>>;
-
-declare type Opt<T> = T | undefined;
-declare class Doc extends RefField {
- constructor();
-
- [key: string]: FieldResult;
- // [ToScriptString](): string;
-}
-
-declare class List<T extends Field> extends ObjectField {
- constructor(fields?: T[]);
- [index: number]: T | (T extends RefField ? Promise<T> : never);
- [Copy](): ObjectField;
-}
-
-declare class InkField extends ObjectField {
- constructor(data:Array<{X:number, Y:number}>);
- [Copy](): ObjectField;
-}
-
-// @ts-ignore
-declare const console: any;
-
-interface DocumentOptions { }
-
-declare const Docs: {
- ImageDocument(url: string, options?: DocumentOptions): Doc;
- VideoDocument(url: string, options?: DocumentOptions): Doc;
- TextDocument(options?: DocumentOptions): Doc;
- PdfDocument(url: string, options?: DocumentOptions): Doc;
- WebDocument(url: string, options?: DocumentOptions): Doc;
- HtmlDocument(html: string, options?: DocumentOptions): Doc;
- MapDocument(url: string, options?: DocumentOptions): Doc;
- KVPDocument(document: Doc, options?: DocumentOptions): Doc;
- FreeformDocument(documents: Doc[], options?: DocumentOptions): Doc;
- SchemaDocument(columns: string[], documents: Doc[], options?: DocumentOptions): Doc;
- TreeDocument(documents: Doc[], options?: DocumentOptions): Doc;
- StackingDocument(documents: Doc[], options?: DocumentOptions): Doc;
-};
-
-declare function idToDoc(id:string):any;
-declare function assignDoc(doc:Doc, field:any, id:any):string;
-declare function d(...args:any[]):any;