aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/CalendarManager.tsx5
-rw-r--r--src/client/util/CaptureManager.tsx17
-rw-r--r--src/client/util/CurrentUserUtils.ts24
-rw-r--r--src/client/util/DictationManager.ts63
-rw-r--r--src/client/util/GroupMemberView.tsx32
-rw-r--r--src/client/util/History.ts34
-rw-r--r--src/client/util/Import & Export/ImportMetadataEntry.tsx40
-rw-r--r--src/client/util/KeyCodes.ts2
-rw-r--r--src/client/util/LinkFollower.ts2
-rw-r--r--src/client/util/PingManager.ts6
-rw-r--r--src/client/util/RTFMarkup.tsx25
-rw-r--r--src/client/util/ReplayMovements.ts22
-rw-r--r--src/client/util/ScriptManager.ts10
-rw-r--r--src/client/util/Scripting.ts4
-rw-r--r--src/client/util/SelectionManager.ts16
-rw-r--r--src/client/util/ServerStats.tsx45
-rw-r--r--src/client/util/SharingManager.tsx5
-rw-r--r--src/client/util/TrackMovements.ts24
-rw-r--r--src/client/util/Transform.ts14
-rw-r--r--src/client/util/TypedEvent.ts18
-rw-r--r--src/client/util/bezierFit.ts6
-rw-r--r--src/client/util/reportManager/ReportManagerComponents.tsx131
-rw-r--r--src/client/util/reportManager/reportManagerSchema.ts1
-rw-r--r--src/client/util/reportManager/reportManagerUtils.ts17
24 files changed, 318 insertions, 245 deletions
diff --git a/src/client/util/CalendarManager.tsx b/src/client/util/CalendarManager.tsx
index 46aa4d238..8e3936d34 100644
--- a/src/client/util/CalendarManager.tsx
+++ b/src/client/util/CalendarManager.tsx
@@ -54,7 +54,6 @@ export class CalendarManager extends ObservableReactComponent<{}> {
@observable private targetDoc: Doc | undefined = undefined; // the target document
@observable private targetDocView: DocumentView | undefined = undefined; // the DocumentView of the target doc
@observable private dialogueBoxOpacity = 1; // for the modal
- @observable private overlayOpacity = 0.4; // for the modal
@observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
@@ -205,14 +204,12 @@ export class CalendarManager extends ObservableReactComponent<{}> {
if (docs.length) {
docs.forEach(doc => doc && Doc.BrushDoc(doc));
this.dialogueBoxOpacity = 0.1;
- this.overlayOpacity = 0.1;
}
})}
onPointerLeave={action(() => {
if (docs.length) {
docs.forEach(doc => doc && Doc.UnBrushDoc(doc));
this.dialogueBoxOpacity = 1;
- this.overlayOpacity = 0.4;
}
})}>
{contents}
@@ -352,6 +349,6 @@ export class CalendarManager extends ObservableReactComponent<{}> {
}
render() {
- return <MainViewModal contents={this.calendarInterface} isDisplayed={this.isOpen} interactive dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} closeOnExternalClick={this.close} />;
+ return <MainViewModal contents={this.calendarInterface} isDisplayed={this.isOpen} interactive dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} closeOnExternalClick={this.close} />;
}
}
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index 2939ba581..a03c80f2a 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -1,3 +1,5 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
@@ -13,18 +15,22 @@ import { SelectionManager } from './SelectionManager';
@observer
export class CaptureManager extends React.Component<{}> {
+ // eslint-disable-next-line no-use-before-define
public static Instance: CaptureManager;
static _settingsStyle = addStyleSheet();
@observable _document: any = undefined;
@observable isOpen: boolean = false; // whether the CaptureManager is to be displayed or not.
+ // eslint-disable-next-line react/sort-comp
constructor(props: {}) {
super(props);
makeObservable(this);
CaptureManager.Instance = this;
}
- public close = action(() => (this.isOpen = false));
+ public close = action(() => {
+ this.isOpen = false;
+ });
public open = action((doc: Doc) => {
this.isOpen = true;
this._document = doc;
@@ -99,15 +105,15 @@ export class CaptureManager extends React.Component<{}> {
<div className="capture-interface">
<div className="capture-t1">
<div className="recordButtonOutline" style={{}}>
- <div className="recordButtonInner" style={{}}></div>
+ <div className="recordButtonInner" style={{}} />
</div>
Conversation Capture
</div>
- <div className="capture-t2"></div>
+ <div className="capture-t2" />
{this.visibilityContent}
{this.linksContent}
<div className="close-button" onClick={this.close}>
- <FontAwesomeIcon icon={'times'} color="black" size={'lg'} />
+ <FontAwesomeIcon icon="times" color="black" size="lg" />
</div>
{this.closeButtons}
</div>
@@ -119,11 +125,10 @@ export class CaptureManager extends React.Component<{}> {
<MainViewModal
contents={this.captureInterface}
isDisplayed={this.isOpen}
- interactive={true}
+ interactive
closeOnExternalClick={this.close}
dialogueBoxStyle={{ width: '500px', height: '350px', border: 'none', background: 'whitesmoke' }}
overlayStyle={{ background: 'black' }}
- overlayDisplayedOpacity={0.6}
/>
);
}
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index acbd0c0b9..65dce34a5 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -67,7 +67,7 @@ export class CurrentUserUtils {
// initializes experimental advanced template views - slideView, headerView
static setupUserDocumentCreatorButtons(doc: Doc, userDocTemplates: Opt<Doc>) {
- const userTemplates = DocListCast(userDocTemplates?.data).filter(doc => !Doc.IsSystem(doc));
+ const userTemplates = DocListCast(userDocTemplates?.data).filter(fdoc => !Doc.IsSystem(fdoc));
const reqdOpts:DocumentOptions = {
title: "User Tools", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true, hidden: false,
_dragOnlyWithinContainer: true, layout_hideContextMenu: true, isSystem: true, _forceActive: true,
@@ -87,7 +87,7 @@ export class CurrentUserUtils {
{ opts: { title: "Open Detail On Right", targetScriptKey: "onChildDoubleClick"}, script: `openDoc(this.doubleClickView.${OpenWhere.addRight})`}];
const reqdClickList = reqdTempOpts.map(opts => {
const allOpts = {...reqdClickOpts, ...opts.opts};
- const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(doc => doc.title === opts.opts.title): undefined;
+ const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(fdoc => fdoc.title === opts.opts.title): undefined;
return DocUtils.AssignOpts(clickDoc, allOpts) ?? Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, allOpts),allOpts);
});
@@ -110,7 +110,7 @@ export class CurrentUserUtils {
const reqdClickList = reqdTempOpts.map(opts => {
const title = opts.opts.title?.toString();
const allOpts = {...reqdClickOpts, ...opts.opts};
- const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(doc => doc.title === title): undefined;
+ const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(fdoc => fdoc.title === title): undefined;
const script = ScriptField.MakeScript(opts.script, {heading:Doc.name, checked:"boolean", containingTreeView:Doc.name});
const scriptDoc = Docs.Create.ScriptingDocument(script, allOpts, title)
return DocUtils.AssignOpts(clickDoc, allOpts) ?? MakeTemplate(scriptDoc);
@@ -130,7 +130,7 @@ export class CurrentUserUtils {
{ title: "Topic", backgroundColor: "lightblue", icon: "book-open" , _layout_showTitle: "title"}];
const reqdNoteList = [...reqdTempOpts.map(opts => {
const reqdOpts = {...opts, isSystem:true, width:200, layout_autoHeight: true, layout_fitWidth: true};
- const noteTemp = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.title === opts.title): undefined;
+ const noteTemp = tempNotes ? DocListCast(tempNotes.data).find(fdoc => fdoc.title === opts.title): undefined;
return DocUtils.AssignOpts(noteTemp, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts));
}), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))];
@@ -249,7 +249,7 @@ export class CurrentUserUtils {
Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true, _height: 100, isSystem: true, _text_fontFamily: StrCast(Doc.UserDoc().fontFamily), _text_fontSize: StrCast(Doc.UserDoc().fontSize) })
], {...opts, title: "Slide View Template"}));
const plotlyApi = () => {
- let plotly = Doc.MyPublishedDocs.find(doc => doc.title === "@plotly");
+ let plotly = Doc.MyPublishedDocs.find(fdoc => fdoc.title === "@plotly");
if (!plotly) {
plotly = Docs.Create.TextDocument(
`await import("https://cdn.plot.ly/plotly-2.27.0.min.js");
@@ -289,7 +289,7 @@ export class CurrentUserUtils {
return slide;
}
const mermaidsApi = () => {
- let mermaids = Doc.MyPublishedDocs.find(doc => doc.title === "@mermaids");
+ let mermaids = Doc.MyPublishedDocs.find(fdoc => fdoc.title === "@mermaids");
if (!mermaids) {
mermaids = Docs.Create.TextDocument(
`const mdef = (await import("https://cdn.jsdelivr.net/npm/mermaid@10.8.0/dist/mermaid.esm.min.mjs")).default;
@@ -416,7 +416,7 @@ pie title Minerals in my tap water
/// Initalizes the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
static setupCreatorButtons(doc: Doc, dragCreatorDoc?:Doc):Doc {
const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => {
- const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
+ const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(fdoc => fdoc.title === reqdOpts.title): undefined;
const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit,
_width: 60, _height: 60, _dragOnlyWithinContainer: true,
btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, isSystem: true,
@@ -460,7 +460,7 @@ pie title Minerals in my tap water
this.setupLeftSidebarPanel(doc);
const myLeftSidebarMenu = DocCast(doc[field]);
const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, toolTip, hidden, scripts, funcs }) => {
- const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
+ const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(fdoc => fdoc.title === title) : undefined;
const reqdBtnOpts:DocumentOptions = {
title, icon, target, toolTip, hidden, btnType: ButtonType.MenuButton, isSystem: true, undoIgnoreFields: new List<string>(['height', 'data_columnHeaders']), dontRegisterView: true,
_width: 60, _height: 60, _dragOnlyWithinContainer: true,
@@ -614,7 +614,7 @@ pie title Minerals in my tap water
static setupDockedButtons(doc: Doc, field="myDockedBtns") {
const dockedBtns = DocCast(doc[field]);
const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string|undefined}, funcs?: {[key:string]:string}) =>
- DocUtils.AssignScripts(DocUtils.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === opts.title), opts) ??
+ DocUtils.AssignScripts(DocUtils.AssignOpts(DocListCast(dockedBtns?.data)?.find(fdoc => fdoc.title === opts.title), opts) ??
CurrentUserUtils.createToolButton(opts), scripts, funcs);
const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet
@@ -780,7 +780,7 @@ pie title Minerals in my tap water
childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: !params.scripts?.onClick,
linearView_SubMenu: true, linearView_Expandable: true, embedContainer: menuDoc};
- const items = (menuBtnDoc?:Doc) => !menuBtnDoc ? [] : subMenu.map(sub => this.setupContextMenuBtn(sub, menuBtnDoc) );
+ const items = (menutBtn?:Doc) => !menutBtn ? [] : subMenu.map(sub => this.setupContextMenuBtn(sub, menutBtn) );
const creator = params.btnType === ButtonType.MultiToggleButton ? this.multiToggleList : this.linearButtonList;
const btnDoc = DocUtils.AssignScripts( DocUtils.AssignDocField(menuDoc, StrCast(params.title),
(opts) => creator(opts, items(menuBtnDoc)), reqdSubMenuOpts, items(menuBtnDoc)), params.scripts, params.funcs);
@@ -802,7 +802,7 @@ pie title Minerals in my tap water
Doc.UserDoc().workspaceReplayingState = undefined;
const dockedBtns = DocCast(doc[field]);
const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string|undefined}, funcs?: {[key:string]:any}) =>
- DocUtils.AssignScripts(DocUtils.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === opts.title), opts) ??
+ DocUtils.AssignScripts(DocUtils.AssignOpts(DocListCast(dockedBtns?.data)?.find(fdoc => fdoc.title === opts.title), opts) ??
CurrentUserUtils.createToolButton(opts), scripts, funcs);
const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet
@@ -964,7 +964,7 @@ pie title Minerals in my tap water
case FInfoFieldType.Doc: opts.fieldValues = new List<Doc>(options.values as any); break;
default: opts.fieldValues = new List<string>(options.values as any); break;// string, pointerEvents, dimUnit, dropActionType
}
- DocUtils.AssignDocField(infos, pair[0], opts => Doc.assign(new Doc(), OmitKeys(opts,["values"]).omit), opts);
+ DocUtils.AssignDocField(infos, pair[0], docOpts => Doc.assign(new Doc(), OmitKeys(docOpts,["values"]).omit), opts);
}
});
}
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 207d3ea0b..08fd80882 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -1,3 +1,4 @@
+/* eslint-disable no-use-before-define */
import * as interpreter from 'words-to-numbers';
// @ts-ignore bcz: how are you supposed to include these definitions since dom-speech-recognition isn't a module?
import type {} from '@types/dom-speech-recognition';
@@ -6,7 +7,7 @@ import { Doc, Opt } from '../../fields/Doc';
import { List } from '../../fields/List';
import { RichTextField } from '../../fields/RichTextField';
import { listSpec } from '../../fields/Schema';
-import { Cast, CastCtor, DocCast } from '../../fields/Types';
+import { Cast, CastCtor } from '../../fields/Types';
import { AudioField, ImageField } from '../../fields/URLField';
import { DocumentType } from '../documents/DocumentTypes';
import { Docs } from '../documents/Documents';
@@ -14,6 +15,7 @@ import { DictationOverlay } from '../views/DictationOverlay';
import { DocumentView, OpenWhere } from '../views/nodes/DocumentView';
import { SelectionManager } from './SelectionManager';
import { UndoManager } from './UndoManager';
+import { DocData } from '../../fields/DocSymbols';
/**
* This namespace provides a singleton instance of a manager that
@@ -64,9 +66,10 @@ export namespace DictationManager {
export let isListening = false;
let isManuallyStopped = false;
- let current: string | undefined = undefined;
+ let current: string | undefined;
let sessionResults: string[] = [];
+ // eslint-disable-next-line new-cap
const recognizer: Opt<SpeechRecognition> = webkitSpeechRecognition ? new webkitSpeechRecognition() : undefined;
export type InterimResultHandler = (results: string) => any;
@@ -87,7 +90,7 @@ export namespace DictationManager {
let pendingListen: Promise<string> | string | undefined;
export const listen = async (options?: Partial<ListeningOptions>) => {
- if (pendingListen instanceof Promise) return pendingListen.then(pl => innerListen(options));
+ if (pendingListen instanceof Promise) return pendingListen.then(() => innerListen(options));
return innerListen(options);
};
const innerListen = async (options?: Partial<ListeningOptions>) => {
@@ -150,29 +153,29 @@ export namespace DictationManager {
recognizer.start();
- return new Promise<string>((resolve, reject) => {
+ return new Promise<string>(resolve => {
recognizer.onerror = (e: any) => {
// e is SpeechRecognitionError but where is that defined?
if (!(indefinite && e.error === 'no-speech')) {
recognizer.stop();
resolve(e);
- //reject(e);
}
};
recognizer.onresult = (e: SpeechRecognitionEvent) => {
current = synthesize(e, intra);
- let matchedTerminator: string | undefined;
- if (options?.terminators && (matchedTerminator = options.terminators.find(end => (current ? current.trim().toLowerCase().endsWith(end.toLowerCase()) : false)))) {
+ const matchedTerminator = options?.terminators?.find(end => (current ? current.trim().toLowerCase().endsWith(end.toLowerCase()) : false));
+ if (options?.terminators && matchedTerminator) {
current = matchedTerminator;
recognizer.abort();
return complete();
}
!isManuallyStopped && handler?.(current);
- //isManuallyStopped && complete();
+ // isManuallyStopped && complete()
+ return undefined;
};
- recognizer.onend = (e: Event) => {
+ recognizer.onend = () => {
if (!indefinite || isManuallyStopped) {
return complete();
}
@@ -182,6 +185,7 @@ export namespace DictationManager {
current = undefined;
}
recognizer.start();
+ return undefined;
};
const complete = () => {
@@ -202,7 +206,7 @@ export namespace DictationManager {
});
};
- export const stop = (salvageSession = true) => {
+ export const stop = (/* salvageSession = true */) => {
if (!isListening || !recognizer) {
return;
}
@@ -212,7 +216,7 @@ export namespace DictationManager {
};
const synthesize = (e: SpeechRecognitionEvent, delimiter?: string) => {
- const results = e.results;
+ const { results } = e;
const transcripts: string[] = [];
for (let i = 0; i < results.length; i++) {
transcripts.push(results.item(i).item(0).transcript.trim());
@@ -233,22 +237,25 @@ export namespace DictationManager {
export const RegisterIndependent = (key: string, value: IndependentEntry) => Independent.set(key, value);
export const RegisterDependent = (entry: DependentEntry) => Dependent.push(entry);
- export const execute = async (phrase: string) => {
- return UndoManager.RunInBatch(async () => {
+ export const execute = async (phrase: string) =>
+ UndoManager.RunInBatch(async () => {
console.log('PHRASE: ' + phrase);
const targets = SelectionManager.Views;
if (!targets || !targets.length) {
- return;
+ return undefined;
}
+ // eslint-disable-next-line no-param-reassign
phrase = phrase.toLowerCase();
const entry = Independent.get(phrase);
if (entry) {
let success = false;
- const restrictTo = entry.restrictTo;
+ const { restrictTo } = entry;
+ // eslint-disable-next-line no-restricted-syntax
for (const target of targets) {
if (!restrictTo || validate(target, restrictTo)) {
+ // eslint-disable-next-line no-await-in-loop
await entry.action(target);
success = true;
}
@@ -256,16 +263,19 @@ export namespace DictationManager {
return success;
}
- for (const entry of Dependent) {
- const regex = entry.expression;
+ // eslint-disable-next-line no-restricted-syntax
+ for (const depEntry of Dependent) {
+ const regex = depEntry.expression;
const matches = regex.exec(phrase);
regex.lastIndex = 0;
if (matches !== null) {
let success = false;
- const restrictTo = entry.restrictTo;
+ const { restrictTo } = depEntry;
+ // eslint-disable-next-line no-restricted-syntax
for (const target of targets) {
if (!restrictTo || validate(target, restrictTo)) {
- await entry.action(target, matches);
+ // eslint-disable-next-line no-await-in-loop
+ await depEntry.action(target, matches);
success = true;
}
}
@@ -275,7 +285,6 @@ export namespace DictationManager {
return false;
}, 'Execute Command');
- };
const ConstructorMap = new Map<DocumentType, CastCtor>([
[DocumentType.COL, listSpec(Doc)],
@@ -294,6 +303,7 @@ export namespace DictationManager {
};
const validate = (target: DocumentView, types: DocumentType[]) => {
+ // eslint-disable-next-line no-restricted-syntax
for (const type of types) {
if (tryCast(target, type)) {
return true;
@@ -318,7 +328,9 @@ export namespace DictationManager {
[
'clear',
{
- action: (target: DocumentView) => (Doc.GetProto(target.Document).data = new List()),
+ action: (target: DocumentView) => {
+ Doc.GetProto(target.Document).data = new List();
+ },
restrictTo: [DocumentType.COL],
},
],
@@ -328,7 +340,7 @@ export namespace DictationManager {
{
action: (target: DocumentView) => {
const newBox = Docs.Create.TextDocument('', { _width: 400, _height: 200, title: 'My Outline', _layout_autoHeight: true });
- const proto = DocCast(newBox.proto);
+ const proto = newBox[DocData];
const prompt = 'Press alt + r to start dictating here...';
const head = 3;
const anchor = head + prompt.length;
@@ -341,7 +353,7 @@ export namespace DictationManager {
],
]);
- const Dependent = new Array<DependentEntry>(
+ const Dependent = [
{
expression: /create (\w+) documents of type (image|nested collection)/g,
action: (target: DocumentView, matches: RegExpExecArray) => {
@@ -361,6 +373,7 @@ export namespace DictationManager {
case 'nested collection':
created = Docs.Create.FreeformDocument([], {});
break;
+ default:
}
created && Doc.AddDocToList(dataDoc, fieldKey, created);
}
@@ -375,7 +388,7 @@ export namespace DictationManager {
mode && (target.Document._type_collection = mode);
},
restrictTo: [DocumentType.COL],
- }
- );
+ },
+ ];
}
}
diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx
index 894583711..c703c3f98 100644
--- a/src/client/util/GroupMemberView.tsx
+++ b/src/client/util/GroupMemberView.tsx
@@ -1,4 +1,7 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Button, IconButton, Size, Type } from 'browndash-components';
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -8,7 +11,6 @@ import { StrCast } from '../../fields/Types';
import { MainViewModal } from '../views/MainViewModal';
import { GroupManager, UserOptions } from './GroupManager';
import './GroupMemberView.scss';
-import { Button, IconButton, Size, Type } from 'browndash-components';
import { SettingsManager } from './SettingsManager';
interface GroupMemberViewProps {
@@ -38,20 +40,23 @@ export class GroupMemberView extends React.Component<GroupMemberViewProps> {
className="group-title"
style={{ marginLeft: !hasEditAccess ? '-14%' : 0 }}
value={StrCast(this.group.title || this.group.groupName)}
- onChange={e => (this.group.title = e.currentTarget.value)}
- disabled={!hasEditAccess}></input>
- <div className={'memberView-closeButton'}>
- <Button icon={<FontAwesomeIcon icon={'times'} size={'lg'} />} onClick={action(this.props.onCloseButtonClick)} color={StrCast(Doc.UserDoc().userColor)} />
+ onChange={e => {
+ this.group.title = e.currentTarget.value;
+ }}
+ disabled={!hasEditAccess}
+ />
+ <div className="memberView-closeButton">
+ <Button icon={<FontAwesomeIcon icon="times" size="lg" />} onClick={action(this.props.onCloseButtonClick)} color={StrCast(Doc.UserDoc().userColor)} />
</div>
{GroupManager.Instance.hasEditAccess(this.group) ? (
<div className="group-buttons">
<div style={{ border: StrCast(Doc.UserDoc().userColor) }}>
<Select
className="add-member-dropdown"
- isSearchable={true}
+ isSearchable
options={options}
onChange={selectedOption => GroupManager.Instance.addMemberToGroup(this.group, (selectedOption as UserOptions).value)}
- placeholder={'Add members'}
+ placeholder="Add members"
value={null}
styles={{
control: () => ({
@@ -80,7 +85,12 @@ export class GroupMemberView extends React.Component<GroupMemberViewProps> {
</div>
</div>
) : null}
- <div className="sort-emails" style={{ paddingTop: hasEditAccess ? 0 : 35 }} onClick={action(() => (this.memberSort = this.memberSort === 'ascending' ? 'descending' : this.memberSort === 'descending' ? 'none' : 'ascending'))}>
+ <div
+ className="sort-emails"
+ style={{ paddingTop: hasEditAccess ? 0 : 35 }}
+ onClick={action(() => {
+ this.memberSort = this.memberSort === 'ascending' ? 'descending' : this.memberSort === 'descending' ? 'none' : 'ascending';
+ })}>
Emails {this.memberSort === 'ascending' ? '↑' : this.memberSort === 'descending' ? '↓' : ''} {/* → */}
</div>
</div>
@@ -90,8 +100,8 @@ export class GroupMemberView extends React.Component<GroupMemberViewProps> {
<div className="editing-row" key={member}>
<div className="user-email">{member}</div>
{hasEditAccess ? (
- <div className={'remove-button'} onClick={() => GroupManager.Instance.removeMemberFromGroup(this.group, member)}>
- <IconButton icon={<FontAwesomeIcon icon={'trash-alt'} />} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} onClick={() => GroupManager.Instance.removeMemberFromGroup(this.group, member)} />
+ <div className="remove-button" onClick={() => GroupManager.Instance.removeMemberFromGroup(this.group, member)}>
+ <IconButton icon={<FontAwesomeIcon icon="trash-alt" />} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} onClick={() => GroupManager.Instance.removeMemberFromGroup(this.group, member)} />
</div>
) : null}
</div>
@@ -102,6 +112,6 @@ export class GroupMemberView extends React.Component<GroupMemberViewProps> {
}
render() {
- return <MainViewModal isDisplayed={true} interactive={true} contents={this.editingInterface} dialogueBoxStyle={{ width: 400, height: 250 }} closeOnExternalClick={this.props.onCloseButtonClick} />;
+ return <MainViewModal isDisplayed interactive contents={this.editingInterface} dialogueBoxStyle={{ width: 400, height: 250 }} closeOnExternalClick={this.props.onCloseButtonClick} />;
}
}
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index b500a5af9..52d0223d5 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -1,3 +1,9 @@
+/* eslint-disable no-use-before-define */
+/* eslint-disable no-empty */
+/* eslint-disable no-continue */
+/* eslint-disable guard-for-in */
+/* eslint-disable no-restricted-syntax */
+/* eslint-disable no-param-reassign */
import * as qs from 'query-string';
import { Doc } from '../../fields/Doc';
import { OmitKeys, ClientUtils } from '../../ClientUtils';
@@ -32,6 +38,7 @@ export namespace HistoryUtil {
case 'doc':
onDocUrl(url);
break;
+ default:
}
}
}
@@ -124,11 +131,11 @@ export namespace HistoryUtil {
const val = customParser(pathname, opts, current);
if (val === null) {
return undefined;
- } else if (val === undefined) {
+ }
+ if (val === undefined) {
return current;
- } else {
- return val;
}
+ return val;
}
return current;
};
@@ -142,26 +149,27 @@ export namespace HistoryUtil {
}
const queryObj = OmitKeys(state, keys).extract;
const query: any = {};
- Object.keys(queryObj).forEach(key => (query[key] = queryObj[key] === null ? null : JSON.stringify(queryObj[key])));
+ Object.keys(queryObj).forEach(key => {
+ query[key] = queryObj[key] === null ? null : JSON.stringify(queryObj[key]);
+ });
const queryString = qs.stringify(query);
return path + (queryString ? `?${queryString}` : '');
};
}
addParser('doc', {}, { readonly: true, initializers: true, nro: true, sharing: true }, (pathname, opts, current) => {
- if (pathname.length !== 2) return undefined;
-
- current.initializers = current.initializers || {};
- const docId = pathname[1];
- current.docId = docId;
- });
- addStringifier('doc', ['initializers', 'readonly', 'nro'], (state, current) => {
- return `${current}/${state.docId}`;
+ if (pathname.length === 2) {
+ current.initializers = current.initializers || {};
+ const docId = pathname[1];
+ current.docId = docId;
+ }
+ return undefined;
});
+ addStringifier('doc', ['initializers', 'readonly', 'nro'], (state, current) => `${current}/${state.docId}`);
export function parseUrl(location: Location | URL): ParsedUrl | undefined {
const pathname = location.pathname.substring(1);
- const search = location.search;
+ const { search } = location;
const opts = search.length ? qs.parse(search, { sort: false }) : {};
const pathnameSplit = pathname.split('/');
diff --git a/src/client/util/Import & Export/ImportMetadataEntry.tsx b/src/client/util/Import & Export/ImportMetadataEntry.tsx
index 58a09b9c9..db1e3d6cd 100644
--- a/src/client/util/Import & Export/ImportMetadataEntry.tsx
+++ b/src/client/util/Import & Export/ImportMetadataEntry.tsx
@@ -1,10 +1,13 @@
-import * as React from 'react';
-import { observer } from 'mobx-react';
-import { EditableView } from '../../views/EditableView';
-import { action, computed } from 'mobx';
+/* 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';
+import { observer } from 'mobx-react';
+import * as React from 'react';
import { Doc } from '../../../fields/Doc';
-import { StrCast, BoolCast } from '../../../fields/Types';
+import { BoolCast, StrCast } from '../../../fields/Types';
+import { EditableView } from '../../views/EditableView';
interface KeyValueProps {
Document: Doc;
@@ -93,18 +96,27 @@ export default class ImportMetadataEntry extends React.Component<KeyValueProps>
alignItems: 'center',
alignContent: 'center',
}}>
- <input onChange={e => (this.onDataDoc = e.target.checked)} ref={this.checkRef} style={{ margin: '0 10px 0 15px' }} type="checkbox" title={'Add to Data Document?'} checked={this.onDataDoc} />
- <div className={'key_container'} style={keyValueStyle}>
- <EditableView ref={this.keyRef} contents={this.key} SetValue={this.updateKey} GetValue={() => ''} oneLine={true} />
+ <input
+ onChange={e => {
+ this.onDataDoc = e.target.checked;
+ }}
+ ref={this.checkRef}
+ style={{ margin: '0 10px 0 15px' }}
+ type="checkbox"
+ title="Add to Data Document?"
+ checked={this.onDataDoc}
+ />
+ <div className="key_container" style={keyValueStyle}>
+ <EditableView ref={this.keyRef} contents={this.key} SetValue={this.updateKey} GetValue={() => ''} oneLine />
</div>
- <div className={'value_container'} style={keyValueStyle}>
- <EditableView ref={this.valueRef} contents={this.value} SetValue={this.updateValue} GetValue={() => ''} oneLine={true} />
+ <div className="value_container" style={keyValueStyle}>
+ <EditableView ref={this.valueRef} contents={this.value} SetValue={this.updateValue} GetValue={() => ''} oneLine />
</div>
- <div onClick={() => this.props.remove(this)} title={'Delete Entry'}>
+ <div onClick={() => this.props.remove(this)} title="Delete Entry">
<FontAwesomeIcon
- icon={'plus'}
- color={'red'}
- size={'1x'}
+ icon="plus"
+ color="red"
+ size="1x"
style={{
marginLeft: 15,
marginRight: 15,
diff --git a/src/client/util/KeyCodes.ts b/src/client/util/KeyCodes.ts
index de2457a5a..fb21f78ee 100644
--- a/src/client/util/KeyCodes.ts
+++ b/src/client/util/KeyCodes.ts
@@ -133,4 +133,4 @@ export class KeyCodes {
public static NUM_9: number = 57;
public static SUBTRACT: number = 189;
public static ADD: number = 187;
-} \ No newline at end of file
+}
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 85bada8c9..09262f5ac 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -44,7 +44,7 @@ export class LinkFollower {
public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, finished?: () => void, traverseBacklink?: boolean) {
const getView = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(DocCast(doc.layout_unrendered ? doc.annotationOn : doc));
- const isAnchor = (sourceDoc: Doc, anchor: Doc) => Doc.AreProtosEqual(anchor, sourceDoc) || Doc.AreProtosEqual(anchor.annotationOn as Doc, sourceDoc);
+ const isAnchor = (source: Doc, anchor: Doc) => Doc.AreProtosEqual(anchor, source) || Doc.AreProtosEqual(anchor.annotationOn as Doc, source);
const linkDocs = link ? [link] : LinkManager.Links(sourceDoc);
const fwdLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_1 as Doc)); // link docs where 'sourceDoc' is link_anchor_1
const backLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_2 as Doc)); // link docs where 'sourceDoc' is link_anchor_2
diff --git a/src/client/util/PingManager.ts b/src/client/util/PingManager.ts
index 7638e2ce0..e5e69c5ac 100644
--- a/src/client/util/PingManager.ts
+++ b/src/client/util/PingManager.ts
@@ -1,8 +1,10 @@
import { action, makeObservable, observable, runInAction } from 'mobx';
import { Networking } from '../Network';
import { CurrentUserUtils } from './CurrentUserUtils';
+
export class PingManager {
// create static instance and getter for global use
+ // eslint-disable-next-line no-use-before-define
@observable static _instance: PingManager;
@observable IsBeating = true;
static get Instance(): PingManager {
@@ -29,7 +31,9 @@ export class PingManager {
sendPing = async (): Promise<void> => {
try {
const res = await Networking.PostToServer('/ping', { date: new Date() });
- runInAction(() => (CurrentUserUtils.ServerVersion = res.message));
+ runInAction(() => {
+ CurrentUserUtils.ServerVersion = res.message;
+ });
!this.IsBeating && this.setIsBeating(true);
} catch {
if (this.IsBeating) {
diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx
index 35b1579df..05fb849fd 100644
--- a/src/client/util/RTFMarkup.tsx
+++ b/src/client/util/RTFMarkup.tsx
@@ -6,23 +6,16 @@ import { SettingsManager } from './SettingsManager';
@observer
export class RTFMarkup extends React.Component<{}> {
+ // eslint-disable-next-line no-use-before-define
static Instance: RTFMarkup;
@observable private isOpen = false; // whether the SharingManager modal is open or not
- @action
- public open = () => (this.isOpen = true);
-
- @action
- public close = () => (this.isOpen = false);
-
constructor(props: {}) {
super(props);
makeObservable(this);
RTFMarkup.Instance = this;
}
- @observable _stats: { [key: string]: any } | undefined = undefined;
-
/**
* @returns the main interface of the SharingManager.
*/
@@ -30,11 +23,11 @@ export class RTFMarkup extends React.Component<{}> {
return (
<div style={{ background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor, textAlign: 'initial', height: '100%' }}>
<p>
- <b style={{ fontSize: 'larger' }}>{`(@wiki:phrase)`}</b>
+ <b style={{ fontSize: 'larger' }}>(@wiki:phrase)</b>
{` display wikipedia page for entered text (terminate with carriage return)`}
</p>
<p>
- <b style={{ fontSize: 'larger' }}>{`(( any text ))`}</b>
+ <b style={{ fontSize: 'larger' }}>(( any text ))</b>
{` submit text to Chat GPT to have results appended afterward`}
</p>
<p>
@@ -129,13 +122,23 @@ export class RTFMarkup extends React.Component<{}> {
);
}
+ @action
+ public open = () => {
+ this.isOpen = true;
+ };
+
+ @action
+ public close = () => {
+ this.isOpen = false;
+ };
+
render() {
return (
<MainViewModal
dialogueBoxStyle={{ backgroundColor: SettingsManager.userBackgroundColor, alignContent: 'normal', color: SettingsManager.userColor, padding: '16px' }}
contents={this.cheatSheet}
isDisplayed={this.isOpen}
- interactive={true}
+ interactive
closeOnExternalClick={this.close}
/>
);
diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts
index b881f18b4..2c8fdf483 100644
--- a/src/client/util/ReplayMovements.ts
+++ b/src/client/util/ReplayMovements.ts
@@ -14,6 +14,7 @@ export class ReplayMovements {
private isPlaying: boolean;
// create static instance and getter for global use
+ // eslint-disable-next-line no-use-before-define
@observable static _instance: ReplayMovements;
static get Instance(): ReplayMovements {
return ReplayMovements._instance;
@@ -90,7 +91,7 @@ export class ReplayMovements {
loadPresentation = (presentation: Presentation) => {
const { movements } = presentation;
if (movements === null) {
- throw '[recordingApi.ts] followMovements() failed: no presentation data';
+ throw new Error('[recordingApi.ts] followMovements() failed: no presentation data');
}
movements.forEach((movement, i) => {
@@ -106,9 +107,7 @@ export class ReplayMovements {
// returns undefined if the docView isn't open on the screen
getCollectionFFView = (doc: Doc) => {
const isInView = DocumentManager.Instance.getDocumentView(doc);
- if (isInView) {
- return isInView.ComponentView as CollectionFreeFormView;
- }
+ return isInView?.ComponentView as CollectionFreeFormView;
};
// will open the doc in a tab then return the CollectionFFView that holds it
@@ -136,9 +135,9 @@ export class ReplayMovements {
if (movements === null) return new Map();
// generate a set of all unique docIds
const docIdtoFirstMove = new Map<Doc, Movement>();
- for (const move of movements) {
+ movements.forEach(move => {
if (!docIdtoFirstMove.has(move.doc)) docIdtoFirstMove.set(move.doc, move);
- }
+ });
return docIdtoFirstMove;
};
@@ -151,10 +150,10 @@ export class ReplayMovements {
// console.info('playMovements', presentation, timeViewed, docIdtoDoc);
if (presentation.movements === null || presentation.movements.length === 0) {
- //|| this.playFFView === null) {
- return new Error('[recordingApi.ts] followMovements() failed: no presentation data');
+ // || this.playFFView === null) {
+ return '[recordingApi.ts] followMovements() failed: no presentation data';
}
- if (this.isPlaying) return;
+ if (this.isPlaying) return undefined;
this.isPlaying = true;
Doc.UserDoc().presentationMode = 'watching';
@@ -170,10 +169,10 @@ export class ReplayMovements {
// for the open tabs, set it to the first move
const docIdtoFirstMove = this.getFirstMovements(filteredMovements);
- for (const [doc, firstMove] of docIdtoFirstMove) {
+ Array.from(docIdtoFirstMove).forEach(([doc, firstMove]) => {
const colFFView = this.getCollectionFFView(doc);
if (colFFView) this.zoomAndPan(firstMove, colFFView);
- }
+ });
};
handleFirstMovements();
@@ -197,5 +196,6 @@ export class ReplayMovements {
}
}, timeDiff);
});
+ return undefined;
};
}
diff --git a/src/client/util/ScriptManager.ts b/src/client/util/ScriptManager.ts
index 87509f2ea..9158f6c0b 100644
--- a/src/client/util/ScriptManager.ts
+++ b/src/client/util/ScriptManager.ts
@@ -7,8 +7,10 @@ import { ScriptingGlobals } from './ScriptingGlobals';
export class ScriptManager {
static _initialized = false;
+ // eslint-disable-next-line no-use-before-define
private static _instance: ScriptManager;
public static get Instance(): ScriptManager {
+ // eslint-disable-next-line no-return-assign
return this._instance || (this._instance = new this());
}
private constructor() {
@@ -58,14 +60,16 @@ export class ScriptManager {
const params = Cast(scriptDoc['data-params'], listSpec('string'), []);
const paramNames = params.reduce((o: string, p: string) => {
+ let out = o;
if (params.indexOf(p) === params.length - 1) {
- o = o + p.split(':')[0].trim();
+ out += p.split(':')[0].trim();
} else {
- o = o + p.split(':')[0].trim() + ',';
+ out += p.split(':')[0].trim() + ',';
}
- return o;
+ return out;
}, '' as string);
+ // eslint-disable-next-line no-new-func
const f = new Function(paramNames, StrCast(scriptDoc.script));
Object.defineProperty(f, 'name', { value: StrCast(scriptDoc.name), writable: false });
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index de5e8b92e..f7d7ba6a4 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -124,7 +124,7 @@ class ScriptingCompilerHost {
return 'node_modules/typescript/lib/lib.d.ts'; // No idea what this means...
}
writeFile(fileName: string, content: string) {
- const file = this.files.find(file => file.fileName === fileName);
+ const file = this.files.find(f => f.fileName === fileName);
if (file) {
file.content = content;
} else {
@@ -147,7 +147,7 @@ class ScriptingCompilerHost {
return this.files.some(file => file.fileName === fileName);
}
readFile(fileName: string): string | undefined {
- const file = this.files.find(file => file.fileName === fileName);
+ const file = this.files.find(f => f.fileName === fileName);
if (file) {
return file.content;
}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 36b926053..1328e90e9 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -11,6 +11,7 @@ import { ScriptingGlobals } from './ScriptingGlobals';
import { UndoManager } from './UndoManager';
export class SelectionManager {
+ // eslint-disable-next-line no-use-before-define
private static _manager: SelectionManager;
private static get Instance() {
return SelectionManager._manager ?? new SelectionManager();
@@ -61,7 +62,9 @@ export class SelectionManager {
dv.IsSelected = false;
dv._props.whenChildContentsActiveChanged(false);
});
- runInAction(() => (this.Instance.SelectedViews.length = 0));
+ runInAction(() => {
+ this.Instance.SelectedViews.length = 0;
+ });
if (found) this.SelectView(found, false);
};
@@ -71,17 +74,20 @@ export class SelectionManager {
public static get Docs() { return this.Instance.SelectedViews.map(dv => dv.Document).filter(doc => doc?._type_collection !== CollectionViewType.Docking); } // prettier-ignore
}
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, expertMode: boolean, checkContext?: boolean) {
if (Doc.noviceMode && expertMode) return false;
if (type === 'tab') {
return SelectionManager.Views.lastElement()?._props.renderDepth === 0;
}
- let selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(SelectionManager.SelectedSchemaDoc ?? SelectionManager.Docs.lastElement());
+ const selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(SelectionManager.SelectedSchemaDoc ?? SelectionManager.Docs.lastElement());
return selected?.type === type || selected?.type_collection === type || !type;
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function deselectAll() {
SelectionManager.DeselectAll();
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function undo() {
SelectionManager.DeselectAll();
return UndoManager.Undo();
@@ -89,17 +95,19 @@ ScriptingGlobals.add(function undo() {
export function ShowUndoStack() {
SelectionManager.DeselectAll();
- var buffer = '';
+ let buffer = '';
UndoManager.undoStack.forEach((batch, i) => {
buffer += 'Batch => ' + UndoManager.undoStackNames[i] + '\n';
- ///batch.forEach(undo => (buffer += ' ' + undo.prop + '\n'));
+ // /batch.forEach(undo => (buffer += ' ' + undo.prop + '\n'));
});
alert(buffer);
}
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function redo() {
SelectionManager.DeselectAll();
return UndoManager.Redo();
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
const docs = SelectionManager.Views.map(dv => dv.Document).filter(
d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null))
diff --git a/src/client/util/ServerStats.tsx b/src/client/util/ServerStats.tsx
index c8df9182d..891561245 100644
--- a/src/client/util/ServerStats.tsx
+++ b/src/client/util/ServerStats.tsx
@@ -1,44 +1,28 @@
-import { action, computed, makeObservable, observable } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { MainViewModal } from '../views/MainViewModal';
import './SharingManager.scss';
import { PingManager } from './PingManager';
-import { StrCast } from '../../fields/Types';
-import { Doc } from '../../fields/Doc';
import { SettingsManager } from './SettingsManager';
@observer
export class ServerStats extends React.Component<{}> {
+ // eslint-disable-next-line no-use-before-define
public static Instance: ServerStats;
@observable private isOpen = false; // whether the SharingManager modal is open or not
+ @observable _stats: { [key: string]: any } | undefined = undefined;
// private get linkVisible() {
// return this.targetDoc ? this.targetDoc["acl-" + PublicKey] !== SharingPermissions.None : false;
// }
- @action
- public open = async () => {
- /**
- * Populates the list of users.
- */
- fetch('/stats').then((res: Response) => res.text().then(action(stats => (this._stats = JSON.parse(stats)))));
-
- this.isOpen = true;
- };
-
- public close = action(() => {
- this.isOpen = false;
- });
-
constructor(props: {}) {
super(props);
makeObservable(this);
ServerStats.Instance = this;
}
- @observable _stats: { [key: string]: any } | undefined = undefined;
-
/**
* @returns the main interface of the SharingManager.
*/
@@ -63,7 +47,28 @@ export class ServerStats extends React.Component<{}> {
);
}
+ // eslint-disable-next-line react/sort-comp
+ public close = action(() => {
+ this.isOpen = false;
+ });
+ public open = async () => {
+ /**
+ * Populates the list of users.
+ */
+ fetch('/stats').then((res: Response) =>
+ res.text().then(
+ action(stats => {
+ this._stats = JSON.parse(stats);
+ })
+ )
+ );
+
+ runInAction(() => {
+ this.isOpen = true;
+ });
+ };
+
render() {
- return <MainViewModal contents={this.sharingInterface} isDisplayed={this.isOpen} interactive={true} closeOnExternalClick={this.close} />;
+ return <MainViewModal contents={this.sharingInterface} isDisplayed={this.isOpen} interactive closeOnExternalClick={this.close} />;
}
}
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 6676e4e03..5b4ac5aff 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -78,7 +78,6 @@ export class SharingManager extends React.Component<{}> {
@observable private targetDocView: DocumentView | undefined = undefined; // the DocumentView of the document being shared
// @observable private copied = false;
@observable private dialogueBoxOpacity = 1; // for the modal
- @observable private overlayOpacity = 0.4; // for the modal
@observable private selectedUsers: UserOptions[] | null = null; // users (individuals/groups) selected to share with
@observable private permissions: SharingPermissions = SharingPermissions.Edit; // the permission with which to share with other users
@observable private individualSort: 'ascending' | 'descending' | 'none' = 'none'; // sorting options for the list of individuals
@@ -661,14 +660,12 @@ export class SharingManager extends React.Component<{}> {
if (docs.length) {
docs.forEach(doc => doc && Doc.BrushDoc(doc));
this.dialogueBoxOpacity = 0.1;
- this.overlayOpacity = 0.1;
}
})}
onPointerLeave={action(() => {
if (docs.length) {
docs.forEach(doc => doc && Doc.UnBrushDoc(doc));
this.dialogueBoxOpacity = 1;
- this.overlayOpacity = 0.4;
}
})}>
{contents}
@@ -742,6 +739,6 @@ export class SharingManager extends React.Component<{}> {
}
render() {
- return <MainViewModal contents={this.sharingInterface} isDisplayed={this.isOpen} interactive dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} closeOnExternalClick={this.close} />;
+ return <MainViewModal contents={this.sharingInterface} isDisplayed={this.isOpen} interactive dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} closeOnExternalClick={this.close} />;
}
}
diff --git a/src/client/util/TrackMovements.ts b/src/client/util/TrackMovements.ts
index f9c2d522f..25a3c9ad8 100644
--- a/src/client/util/TrackMovements.ts
+++ b/src/client/util/TrackMovements.ts
@@ -1,8 +1,7 @@
-import { IReactionDisposer, makeObservable, observable, observe, reaction } from 'mobx';
+import { IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { NumCast } from '../../fields/Types';
import { Doc, DocListCast } from '../../fields/Doc';
import { CollectionDockingView } from '../views/collections/CollectionDockingView';
-import { Id } from '../../fields/FieldSymbols';
import { CollectionViewType } from '../documents/DocumentTypes';
export type Movement = {
@@ -33,6 +32,7 @@ export class TrackMovements {
private tabChangeDisposeFunc: IReactionDisposer | null;
// create static instance and getter for global use
+ // eslint-disable-next-line no-use-before-define
@observable static _instance: TrackMovements;
static get Instance(): TrackMovements {
return TrackMovements._instance;
@@ -92,12 +92,13 @@ export class TrackMovements {
// so that the size comparisons are correct, we must filter to only the FFViews
const isFFView = (doc: Doc) => doc && doc._type_collection === CollectionViewType.Freeform;
const tabbedFFViews = new Set<Doc>();
- for (const DashDoc of tabbedDocs) {
+ tabbedDocs.forEach(DashDoc => {
if (isFFView(DashDoc)) tabbedFFViews.add(DashDoc);
- }
+ });
// new tab was added - need to add it
if (tabbedFFViews.size > this.recordingFFViews.size) {
+ // eslint-disable-next-line no-restricted-syntax
for (const DashDoc of tabbedDocs) {
if (!this.recordingFFViews.has(DashDoc)) {
if (isFFView(DashDoc)) {
@@ -111,6 +112,7 @@ export class TrackMovements {
}
// tab was removed - need to remove it from recordingFFViews
else if (tabbedFFViews.size < this.recordingFFViews.size) {
+ // eslint-disable-next-line no-restricted-syntax
for (const [doc] of this.recordingFFViews) {
if (!tabbedFFViews.has(doc)) {
this.removeRecordingFFView(doc);
@@ -208,11 +210,11 @@ export class TrackMovements {
return;
}
- for (const [id, disposeFunc] of this.recordingFFViews) {
+ Array.from(this.recordingFFViews).forEach(([id, disposeFunc]) => {
// console.info('calling dispose func : docId', id);
disposeFunc();
- this.recordingFFViews.delete(id);
- }
+ this.recordingFFViews?.delete(id);
+ });
};
private trackMovement = (panX: number, panY: number, doc: Doc, scale: number = 0) => {
@@ -241,9 +243,9 @@ export class TrackMovements {
// method that concatenates an array of presentatations into one
public concatPresentations = (presentations: Presentation[]): Presentation => {
// these three will lead to the combined presentation
- let combinedMovements: Movement[] = [];
+ const combinedMovements: Movement[] = [];
let sumTime = 0;
- let combinedMetas: any[] = [];
+ const combinedMetas: any[] = [];
presentations.forEach(presentation => {
const { movements, totalTime, meta } = presentation;
@@ -251,9 +253,7 @@ export class TrackMovements {
// update movements if they had one
if (movements) {
// add the summed time to the movements
- const addedTimeMovements = movements.map(move => {
- return { ...move, time: move.time + sumTime };
- });
+ const addedTimeMovements = movements.map(move => ({ ...move, time: move.time + sumTime }));
// concat the movements already in the combined presentation with these new ones
combinedMovements.push(...addedTimeMovements);
}
diff --git a/src/client/util/Transform.ts b/src/client/util/Transform.ts
index dca37c960..1a07dd6ae 100644
--- a/src/client/util/Transform.ts
+++ b/src/client/util/Transform.ts
@@ -116,20 +116,14 @@ export class Transform {
preTransformed = (transform: Transform): Transform => this.copy().preTransform(transform);
- transformPoint = (x: number, y: number): [number, number] => {
- x *= this._scale;
- x += this._translateX;
- y *= this._scale;
- y += this._translateY;
- return [x, y];
- };
+ transformPoint = (x: number, y: number): [number, number] => [x * this._scale + this._translateX, y * this._scale + this._translateY];
transformDirection = (x: number, y: number): [number, number] => [x * this._scale, y * this._scale];
transformBounds(x: number, y: number, width: number, height: number): { x: number; y: number; width: number; height: number } {
- [x, y] = this.transformPoint(x, y);
- [width, height] = this.transformDirection(width, height);
- return { x, y, width, height };
+ const [tx, ty] = this.transformPoint(x, y);
+ const [twidth, theight] = this.transformDirection(width, height);
+ return { x: tx, y: ty, width: twidth, height: theight };
}
inverse = () => new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale, -this._rotate);
diff --git a/src/client/util/TypedEvent.ts b/src/client/util/TypedEvent.ts
index 90fd299c1..9ef2aa8d7 100644
--- a/src/client/util/TypedEvent.ts
+++ b/src/client/util/TypedEvent.ts
@@ -14,27 +14,27 @@ export class TypedEvent<T> {
on = (listener: Listener<T>): Disposable => {
this.listeners.push(listener);
return {
- dispose: () => this.off(listener)
+ dispose: () => this.off(listener),
};
- }
+ };
once = (listener: Listener<T>): void => {
this.listenersOncer.push(listener);
- }
+ };
off = (listener: Listener<T>) => {
const callbackIndex = this.listeners.indexOf(listener);
if (callbackIndex > -1) this.listeners.splice(callbackIndex, 1);
- }
+ };
emit = (event: T) => {
/** Update any general listeners */
- this.listeners.forEach((listener) => listener(event));
+ this.listeners.forEach(listener => listener(event));
/** Clear the `once` queue */
- this.listenersOncer.forEach((listener) => listener(event));
+ this.listenersOncer.forEach(listener => listener(event));
this.listenersOncer = [];
- }
+ };
- pipe = (te: TypedEvent<T>): Disposable => this.on((e) => te.emit(e));
-} \ No newline at end of file
+ pipe = (te: TypedEvent<T>): Disposable => this.on(e => te.emit(e));
+}
diff --git a/src/client/util/bezierFit.ts b/src/client/util/bezierFit.ts
index 6bbf55e5a..d6f3f2340 100644
--- a/src/client/util/bezierFit.ts
+++ b/src/client/util/bezierFit.ts
@@ -1,3 +1,4 @@
+/* eslint-disable no-use-before-define */
/* eslint-disable prefer-destructuring */
/* eslint-disable no-param-reassign */
/* eslint-disable camelcase */
@@ -242,6 +243,7 @@ function splitCubic(p: Point[], t: number, left: Point[], right: Point[]) {
* is robust: a near-tangential intersection will yield zero or two
* intersections.
*/
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
function recursively_intersect(a: Point[], t0: number, t1: number, deptha: number, b: Point[], u0: number, u1: number, depthb: number, parameters: number[][]) {
if (deptha > 0) {
const a1 = new Array<Point>(4);
@@ -538,8 +540,8 @@ function FitCubic(d: Point[], first: number, last: number, tHat1: Point, tHat2:
for (let i = 0; i < maxIterations; i++) {
const uPrime = ReparameterizeBezier(d, first, last, u, bezCurve); // Improved parameter values
GenerateBezier(d, first, last, uPrime, tHat1, tHat2, bezCurve);
- const { maxError } = ComputeMaxError(d, first, last, bezCurve, uPrime);
- if (maxError < error) {
+ const { maxError: maximumError } = ComputeMaxError(d, first, last, bezCurve, uPrime);
+ if (maximumError < error) {
result.push(bezCurve[1]);
result.push(bezCurve[2]);
result.push(bezCurve[3]);
diff --git a/src/client/util/reportManager/ReportManagerComponents.tsx b/src/client/util/reportManager/ReportManagerComponents.tsx
index 1e226bf6d..cecebc648 100644
--- a/src/client/util/reportManager/ReportManagerComponents.tsx
+++ b/src/client/util/reportManager/ReportManagerComponents.tsx
@@ -1,9 +1,15 @@
+/* eslint-disable react/require-default-props */
+/* eslint-disable prefer-destructuring */
+/* eslint-disable jsx-a11y/label-has-associated-control */
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+/* eslint-disable no-use-before-define */
import * as React from 'react';
-import { Issue } from './reportManagerSchema';
-import { darkColors, dashBlue, getLabelColors, isDarkMode, lightColors } from './reportManagerUtils';
import ReactMarkdown from 'react-markdown';
import rehypeRaw from 'rehype-raw';
import remarkGfm from 'remark-gfm';
+import { darkColors, dashBlue, getLabelColors, isDarkMode, lightColors } from './reportManagerUtils';
+import { Issue } from './reportManagerSchema';
import { StrCast } from '../../../fields/Types';
import { Doc } from '../../../fields/Doc';
@@ -18,7 +24,7 @@ interface FilterProps<T> {
}
// filter ui for issues (horizontal list of tags)
-export const Filter = <T extends string>({ items, activeValue, setActiveValue }: FilterProps<T>) => {
+export function Filter<T extends string>({ items, activeValue, setActiveValue }: FilterProps<T>) {
// establishing theme
const darkMode = isDarkMode(StrCast(Doc.UserDoc().userBackgroundColor));
const colors = darkMode ? darkColors : lightColors;
@@ -28,7 +34,7 @@ export const Filter = <T extends string>({ items, activeValue, setActiveValue }:
return (
<div className="issues-filter">
<Tag
- text={'All'}
+ text="All"
onClick={() => {
setActiveValue(null);
}}
@@ -38,25 +44,23 @@ export const Filter = <T extends string>({ items, activeValue, setActiveValue }:
borderColor={activeValue === null ? StrCast(Doc.UserDoc().userColor) : colors.border}
border
/>
- {items.map(item => {
- return (
- <Tag
- key={item}
- text={item}
- onClick={() => {
- setActiveValue(item);
- }}
- fontSize="12px"
- backgroundColor={activeValue === item ? StrCast(Doc.UserDoc().userColor) : 'transparent'}
- color={activeValue === item ? activeTagTextColor : colors.textGrey}
- border
- borderColor={activeValue === item ? StrCast(Doc.UserDoc().userColor) : colors.border}
- />
- );
- })}
+ {items.map(item => (
+ <Tag
+ key={item}
+ text={item}
+ onClick={() => {
+ setActiveValue(item);
+ }}
+ fontSize="12px"
+ backgroundColor={activeValue === item ? StrCast(Doc.UserDoc().userColor) : 'transparent'}
+ color={activeValue === item ? activeTagTextColor : colors.textGrey}
+ border
+ borderColor={activeValue === item ? StrCast(Doc.UserDoc().userColor) : colors.border}
+ />
+ ))}
</div>
);
-};
+}
interface IssueCardProps {
issue: Issue;
@@ -64,7 +68,7 @@ interface IssueCardProps {
}
// Component for the issue cards list on the left
-export const IssueCard = ({ issue, onSelect }: IssueCardProps) => {
+export function IssueCard({ issue, onSelect }: IssueCardProps) {
const [textColor, setTextColor] = React.useState('');
const [bgColor, setBgColor] = React.useState('transparent');
const [borderColor, setBorderColor] = React.useState('transparent');
@@ -103,14 +107,14 @@ export const IssueCard = ({ issue, onSelect }: IssueCardProps) => {
<h3 className="issue-title">{issue.title}</h3>
</div>
);
-};
+}
interface IssueViewProps {
issue: Issue;
}
// Detailed issue view that displays on the right
-export const IssueView = ({ issue }: IssueViewProps) => {
+export function IssueView({ issue }: IssueViewProps) {
const [issueBody, setIssueBody] = React.useState('');
// Parses the issue body into a formatted markdown (main functionality is replacing urls with tags)
@@ -127,15 +131,16 @@ export const IssueView = ({ issue }: IssueViewProps) => {
parts.map(async part => {
if (imgTagRegex.test(part) || videoTagRegex.test(part) || audioTagRegex.test(part)) {
return `\n${await parseFileTag(part)}\n`;
- } else if (fileRegex.test(part)) {
+ }
+ if (fileRegex.test(part)) {
const tag = await parseDashFiles(part);
return tag;
- } else if (localRegex.test(part)) {
+ }
+ if (localRegex.test(part)) {
const tag = await parseLocalFiles(part);
return tag;
- } else {
- return part;
}
+ return part;
})
);
@@ -143,7 +148,7 @@ export const IssueView = ({ issue }: IssueViewProps) => {
};
// Extracts the src from an image tag and either returns the raw url if not accessible or a new image tag
- const parseFileTag = async (tag: string): Promise<string> => {
+ const parseFileTag = async (tag: string): Promise<string | undefined> => {
const regex = /src="([^"]+)"/;
let url = '';
const match = tag.match(regex);
@@ -160,18 +165,19 @@ export const IssueView = ({ issue }: IssueViewProps) => {
case '.png':
case '.jpeg':
case '.gif':
- return await getDisplayedFile(url, 'image');
+ return getDisplayedFile(url, 'image');
// video
case '.mp4':
case '.mpeg':
case '.webm':
case '.mov':
- return await getDisplayedFile(url, 'video');
- //audio
+ return getDisplayedFile(url, 'video');
+ // audio
case '.mp3':
case '.wav':
case '.ogg':
- return await getDisplayedFile(url, 'audio');
+ return getDisplayedFile(url, 'audio');
+ default:
}
return tag;
};
@@ -183,14 +189,15 @@ export const IssueView = ({ issue }: IssueViewProps) => {
const dashAudioRegex = /https:\/\/browndash\.com\/files[/\\]audio/;
if (dashImgRegex.test(url)) {
- return await getDisplayedFile(url, 'image');
- } else if (dashVideoRegex.test(url)) {
- return await getDisplayedFile(url, 'video');
- } else if (dashAudioRegex.test(url)) {
- return await getDisplayedFile(url, 'audio');
- } else {
- return url;
+ return getDisplayedFile(url, 'image');
}
+ if (dashVideoRegex.test(url)) {
+ return getDisplayedFile(url, 'video');
+ }
+ if (dashAudioRegex.test(url)) {
+ return getDisplayedFile(url, 'audio');
+ }
+ return url;
};
// Returns the corresponding HTML tag for a src url
@@ -200,31 +207,37 @@ export const IssueView = ({ issue }: IssueViewProps) => {
const dashAudioRegex = /http:\/\/localhost:1050\.com\/files[/\\]audio/;
if (imgRegex.test(url)) {
- return await getDisplayedFile(url, 'image');
- } else if (dashVideoRegex.test(url)) {
- return await getDisplayedFile(url, 'video');
- } else if (dashAudioRegex.test(url)) {
- return await getDisplayedFile(url, 'audio');
- } else {
- return url;
+ return getDisplayedFile(url, 'image');
+ }
+ if (dashVideoRegex.test(url)) {
+ return getDisplayedFile(url, 'video');
+ }
+ if (dashAudioRegex.test(url)) {
+ return getDisplayedFile(url, 'audio');
}
+ return url;
};
- const getDisplayedFile = async (url: string, fileType: 'image' | 'video' | 'audio'): Promise<string> => {
+ const getDisplayedFile = async (url: string, fileType: 'image' | 'video' | 'audio'): Promise<string | undefined> => {
switch (fileType) {
- case 'image':
+ case 'image': {
const imgValid = await isImgValid(url);
if (!imgValid) return `\n${url} (This image could not be loaded)\n`;
return `\n${url}\n<img width="100%" alt="Issue asset" src=${url} />\n`;
- case 'video':
+ }
+ case 'video': {
const videoValid = await isVideoValid(url);
if (!videoValid) return `\n${url} (This video could not be loaded)\n`;
return `\n${url}\n<video class="report-default-video" width="100%" controls alt="Issue asset" src=${url} />\n`;
- case 'audio':
+ }
+ case 'audio': {
const audioValid = await isAudioValid(url);
if (!audioValid) return `\n${url} (This audio could not be loaded)\n`;
return `\n${url}\n<audio src=${url} controls />\n`;
+ }
+ default:
}
+ return undefined;
};
// Loads an image and returns a promise that resolves as whether the image is valid or not
@@ -270,7 +283,7 @@ export const IssueView = ({ issue }: IssueViewProps) => {
<div className="issue-view">
<span className="issue-label">
Issue{' '}
- <a className="issue-link" href={issue.html_url} target="_blank">
+ <a className="issue-link" href={issue.html_url} target="_blank" rel="noreferrer">
#{issue.number}
</a>
</span>
@@ -292,7 +305,7 @@ export const IssueView = ({ issue }: IssueViewProps) => {
<ReactMarkdown children={issueBody} className="issue-content" remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]} />
</div>
);
-};
+}
interface TagProps {
text: string;
@@ -305,7 +318,7 @@ interface TagProps {
}
// Small tag for labels of the issue
-export const Tag = ({ text, color, backgroundColor, fontSize, border, borderColor, onClick }: TagProps) => {
+export function Tag({ text, color, backgroundColor, fontSize, border, borderColor, onClick }: TagProps) {
return (
<div
onClick={onClick ?? (() => {})}
@@ -314,14 +327,14 @@ export const Tag = ({ text, color, backgroundColor, fontSize, border, borderColo
{text}
</div>
);
-};
+}
interface FormInputProps {
value: string;
placeholder: string;
onChange: (val: string) => void;
}
-export const FormInput = ({ value, placeholder, onChange }: FormInputProps) => {
+export function FormInput({ value, placeholder, onChange }: FormInputProps) {
const [inputBorderColor, setInputBorderColor] = React.useState('');
return (
@@ -349,9 +362,9 @@ export const FormInput = ({ value, placeholder, onChange }: FormInputProps) => {
}}
/>
);
-};
+}
-export const FormTextArea = ({ value, placeholder, onChange }: FormInputProps) => {
+export function FormTextArea({ value, placeholder, onChange }: FormInputProps) {
const [textAreaBorderColor, setTextAreaBorderColor] = React.useState('');
return (
@@ -378,4 +391,4 @@ export const FormTextArea = ({ value, placeholder, onChange }: FormInputProps) =
}}
/>
);
-};
+}
diff --git a/src/client/util/reportManager/reportManagerSchema.ts b/src/client/util/reportManager/reportManagerSchema.ts
index 9a1c7c3e9..171c24393 100644
--- a/src/client/util/reportManager/reportManagerSchema.ts
+++ b/src/client/util/reportManager/reportManagerSchema.ts
@@ -1,3 +1,4 @@
+/* eslint-disable no-use-before-define */
/**
* Issue interface schema from Github.
*/
diff --git a/src/client/util/reportManager/reportManagerUtils.ts b/src/client/util/reportManager/reportManagerUtils.ts
index 22e5eebbb..f14967e0a 100644
--- a/src/client/util/reportManager/reportManagerUtils.ts
+++ b/src/client/util/reportManager/reportManagerUtils.ts
@@ -63,9 +63,8 @@ export const getAllIssues = async (octokit: Octokit): Promise<any[]> => {
// 200 status means success
if (res.status === 200) {
return res.data;
- } else {
- throw new Error('Error getting issues');
}
+ throw new Error('Error getting issues');
};
/**
@@ -104,9 +103,7 @@ export const fileLinktoServerLink = (fileLink: string): string => {
* @param link response from file upload
* @returns server file path
*/
-export const getServerPath = (link: any): string => {
- return link.result.accessPaths.agnostic.server as string;
-};
+export const getServerPath = (link: any): string => link.result.accessPaths.agnostic.server as string;
/**
* Uploads media files to the server.
@@ -124,6 +121,7 @@ export const uploadFilesToServer = async (mediaFiles: FileData[]): Promise<strin
alert(err);
}
}
+ return undefined;
};
// helper functions
@@ -141,18 +139,16 @@ export const passesTagFilter = (issue: Issue, priorityFilter: string | null, bug
passesPriority = issue.labels.some(label => {
if (typeof label === 'string') {
return label === priorityFilter;
- } else {
- return label.name === priorityFilter;
}
+ return label.name === priorityFilter;
});
}
if (bugFilter) {
passesBug = issue.labels.some(label => {
if (typeof label === 'string') {
return label === bugFilter;
- } else {
- return label.name === bugFilter;
}
+ return label.name === bugFilter;
});
}
return passesPriority && passesBug;
@@ -217,7 +213,8 @@ export const bugColors: { [key: string]: string[] } = {
export const getLabelColors = (label: string): string[] => {
if (prioritySet.has(label as Priority)) {
return priorityColors[label];
- } else if (bugSet.has(label as BugType)) {
+ }
+ if (bugSet.has(label as BugType)) {
return bugColors[label];
}
return ['#0f73f6', '#ffffff'];