diff options
Diffstat (limited to 'src')
58 files changed, 267 insertions, 268 deletions
diff --git a/src/Utils.ts b/src/Utils.ts index 53f02d64d..38a59d524 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -1,8 +1,7 @@ import * as v4 from 'uuid/v4'; import * as v5 from 'uuid/v5'; -import { ColorState } from 'react-color'; +import { ColorResult } from 'react-color'; import * as rp from 'request-promise'; -import { Socket } from 'socket.io'; import { DocumentType } from './client/documents/DocumentTypes'; import { Colors } from './client/views/global/globalEnums'; import { Message } from './server/Message'; @@ -139,7 +138,7 @@ export namespace Utils { return (number < 16 ? '0' : '') + number.toString(16).toUpperCase(); } - export function colorString(color: ColorState) { + export function colorString(color: ColorResult) { return color.hex.startsWith('#') && color.hex.length < 8 ? color.hex + (color.rgb.a ? decimalToHexString(Math.round(color.rgb.a * 255)) : 'ff') : color.hex; } @@ -398,14 +397,14 @@ export namespace Utils { }; } - export function Emit<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T) { + export function Emit<T>(socket: SocketIOClient.Socket, message: Message<T>, args: T) { log('Emit', message.Name, args, false); socket.emit(message.Message, args); } - export function EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T): Promise<any>; - export function EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn: (args: any) => any): void; - export function EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn?: (args: any) => any): void | Promise<any> { + export function EmitCallback<T>(socket: SocketIOClient.Socket, message: Message<T>, args: T): Promise<any>; + export function EmitCallback<T>(socket: SocketIOClient.Socket, message: Message<T>, args: T, fn: (args: any) => any): void; + export function EmitCallback<T>(socket: SocketIOClient.Socket, message: Message<T>, args: T, fn?: (args: any) => any): void | Promise<any> { log('Emit', message.Name, args, false); if (fn) { socket.emit(message.Message, args, loggingCallback('Receiving', fn, message.Name)); @@ -414,21 +413,21 @@ export namespace Utils { } } - export function AddServerHandler<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, handler: (args: T) => any) { + export function AddServerHandler<T>(socket: SocketIOClient.Socket, message: Message<T>, handler: (args: T) => any) { socket.on(message.Message, loggingCallback('Incoming', handler, message.Name)); } - export function AddServerHandlerCallback<T>(socket: Socket, message: Message<T>, handler: (args: [T, (res: any) => any]) => any) { + export function AddServerHandlerCallback<T>(socket: SocketIOClient.Socket, message: Message<T>, handler: (args: [T, (res: any) => any]) => any) { socket.on(message.Message, (arg: T, fn: (res: any) => any) => { log('S receiving', message.Name, arg, true); handler([arg, loggingCallback('S sending', fn, message.Name)]); }); } - export type RoomHandler = (socket: Socket, room: string) => any; - export type UsedSockets = Socket | SocketIOClient.Socket; + export type RoomHandler = (socket: SocketIOClient.Socket, room: string) => any; + export type UsedSockets = SocketIOClient.Socket; export type RoomMessage = 'create or join' | 'created' | 'joined'; - export function AddRoomHandler(socket: Socket, message: RoomMessage, handler: RoomHandler) { - socket.on(message, room => handler(socket, room)); + export function AddRoomHandler(socket: SocketIOClient.Socket, message: RoomMessage, handler: RoomHandler) { + socket.on(message, (room: any) => handler(socket, room)); } } diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts index 551dca073..c8f381cc0 100644 --- a/src/client/apis/google_docs/GoogleApiClientUtils.ts +++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts @@ -84,7 +84,7 @@ export namespace GoogleApiClientUtils { }; try { const schema: docs_v1.Schema$Document = await Networking.PostToServer(path, parameters); - return schema.documentId; + return schema.documentId === null ? undefined : schema.documentId; } catch { return undefined; } @@ -195,7 +195,7 @@ export namespace GoogleApiClientUtils { const title = document.title; let bodyLines = Utils.extractText(document).text.split("\n"); options.removeNewlines && (bodyLines = bodyLines.filter(line => line.length)); - return { title, bodyLines }; + return { title: title ?? "", bodyLines }; } }); }; diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 6bde7989b..85634bc8b 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -1,4 +1,4 @@ -import { Configuration, OpenAIApi } from 'openai'; +import { ClientOptions, OpenAI } from 'openai'; enum GPTCallType { SUMMARY = 'summary', @@ -29,17 +29,17 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => { if (callType === GPTCallType.SUMMARY) inputText += '.'; const opts: GPTCallOpts = callTypeMap[callType]; try { - const configuration = new Configuration({ + const configuration:ClientOptions ={ apiKey: process.env.OPENAI_KEY, - }); - const openai = new OpenAIApi(configuration); - const response = await openai.createCompletion({ + }; + const openai = new OpenAI(configuration); + const response = await openai.completions.create({ model: opts.model, max_tokens: opts.maxTokens, temperature: opts.temp, prompt: `${opts.prompt}${inputText}`, }); - return response.data.choices[0].text; + return response.choices[0].text; } catch (err) { console.log(err); return 'Error connecting with API.'; @@ -48,16 +48,16 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => { const gptImageCall = async (prompt: string, n?: number) => { try { - const configuration = new Configuration({ + const configuration:ClientOptions = { apiKey: process.env.OPENAI_KEY, - }); - const openai = new OpenAIApi(configuration); - const response = await openai.createImage({ + }; + const openai = new OpenAI(configuration); + const response = await openai.images.generate({ prompt: prompt, n: n ?? 1, size: '1024x1024', }); - return response.data.data.map(data => data.url); + return response.data.map((data:any) => data.url); // return response.data.data[0].url; } catch (err) { console.error(err); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 929660ff4..df81e12f0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1299,7 +1299,7 @@ export namespace DocUtils { // links are not a field value, so handled here. value is an expression of form ([field=]idToDoc("...")) const allLinks = LinkManager.Instance.getAllRelatedLinks(doc); const matchLink = (value: string, anchor: Doc) => { - const linkedToExp = value?.split('='); + const linkedToExp = (value ?? "").split('='); if (linkedToExp.length === 1) return Field.toScriptString(anchor) === value; return Field.toScriptString(DocCast(anchor[linkedToExp[0]])) === linkedToExp[1]; }; @@ -1909,7 +1909,7 @@ export namespace DocUtils { const generatedDocuments: Doc[] = []; Networking.UploadYoutubeToServer(videoId, overwriteDoc?.[Id]).then(upfiles => { const { - source: { name, type }, + source: { newFilename, originalFilename, mimetype }, result, } = upfiles.lastElement(); if ((result as any).message) { @@ -1918,7 +1918,7 @@ export namespace DocUtils { overwriteDoc.loadingError = (result as any).message; LoadingBox.removeCurrentlyLoading(overwriteDoc); } - } else name && processFileupload(generatedDocuments, name, type, result, options, overwriteDoc); + } else newFilename && processFileupload(generatedDocuments, newFilename, mimetype??"", result, options, overwriteDoc); }); } @@ -1938,10 +1938,10 @@ export namespace DocUtils { const upfiles = await Networking.UploadFilesToServer(fileNoGuidPairs); for (const { - source: { name, type }, + source: { newFilename, mimetype }, result, } of upfiles) { - name && type && processFileupload(generatedDocuments, name, type, result, options); + newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options); } return generatedDocuments; } @@ -1951,15 +1951,15 @@ export namespace DocUtils { // Since this file has an overwriteDoc, we can set the client tracking guid to the overwriteDoc's guid. Networking.UploadFilesToServer([{ file, guid: overwriteDoc[Id] }]).then(upfiles => { const { - source: { name, type }, + source: { newFilename, mimetype }, result, - } = upfiles.lastElement() ?? { source: { name: '', type: '' }, result: { message: 'upload failed' } }; + } = upfiles.lastElement() ?? { source: { newFilename: '', mimetype: '' }, result: { message: 'upload failed' } }; if ((result as any).message) { if (overwriteDoc) { overwriteDoc.loadingError = (result as any).message; LoadingBox.removeCurrentlyLoading(overwriteDoc); } - } else name && type && processFileupload(generatedDocuments, name, type, result, options, overwriteDoc); + } else newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options, overwriteDoc); }); } diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 9113fd832..30e448797 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -21,7 +21,7 @@ import { DocumentManager } from '../DocumentManager'; import './DirectoryImportBox.scss'; import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from './ImportMetadataEntry'; import * as React from 'react'; -import _ = require('lodash'); +import * as _ from 'lodash'; const unsupported = ['text/html', 'text/plain']; @@ -119,7 +119,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> { await Promise.all( uploads.map(async response => { const { - source: { type }, + source: { mimetype }, result, } = response; if (result instanceof Error) { @@ -127,7 +127,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> { } const { accessPaths, exifData } = result; const path = Utils.prepend(accessPaths.agnostic.client); - const document = type && (await DocUtils.DocumentFromType(type, path, { _width: 300 })); + const document = mimetype && (await DocUtils.DocumentFromType(mimetype, path, { _width: 300 })); const { data, error } = exifData; if (document) { Doc.GetProto(document).exif = error || Doc.Get.FromJson({ data }); diff --git a/src/client/util/reportManager/ReportManagerComponents.tsx b/src/client/util/reportManager/ReportManagerComponents.tsx index e870c073d..1e226bf6d 100644 --- a/src/client/util/reportManager/ReportManagerComponents.tsx +++ b/src/client/util/reportManager/ReportManagerComponents.tsx @@ -289,7 +289,7 @@ export const IssueView = ({ issue }: IssueViewProps) => { </div> </div> )} - <ReactMarkdown children={issueBody} className="issue-content" linkTarget={'_blank'} remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]} /> + <ReactMarkdown children={issueBody} className="issue-content" remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]} /> </div> ); }; diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 6e6c8a5f1..a64722a0b 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -1,6 +1,6 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { action, computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc } from '../../fields/Doc'; @@ -28,9 +28,9 @@ import { GoogleRef } from './nodes/formattedText/FormattedTextBox'; import { PinProps } from './nodes/trails'; import { TemplateMenu } from './TemplateMenu'; import * as React from 'react'; -const higflyout = require('@hig/flyout'); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; +// import * as higflyout from '@hig/flyout'; +// export const { anchorPoints } = higflyout; +// export const Flyout = higflyout.default; const cloud: IconProp = 'cloud-upload-alt'; const fetch: IconProp = 'sync-alt'; @@ -423,7 +423,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV return !view0 ? null : ( <Tooltip title={<div className="dash-tooltip">Show metadata panel</div>}> <div className="documentButtonBar-linkFlyout"> - <Flyout + {/* <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={ <MetadataEntryMenu @@ -437,7 +437,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV <div className={'documentButtonBar-linkButton-' + 'empty'} onPointerDown={e => e.stopPropagation()}> {<FontAwesomeIcon className="documentdecorations-icon" icon="tag" />} </div> - </Flyout> + </Flyout> */} </div> </Tooltip> ); @@ -501,7 +501,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV return !view0 ? null : ( <Tooltip title={<div className="dash-tooltip">Tap to Customize Layout. Drag an embedding</div>} open={this._tooltipOpen} onClose={action(() => (this._tooltipOpen = false))} placement="bottom"> <div className="documentButtonBar-linkFlyout" ref={this._dragRef} onPointerEnter={action(() => !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))}> - <Flyout + {/* <Flyout anchorPoint={anchorPoints.LEFT_TOP} onOpen={action(() => (this._embedDown = true))} onClose={action(() => (this._embedDown = false))} @@ -516,7 +516,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV <div className={'documentButtonBar-linkButton-empty'} ref={this._dragRef} onPointerDown={this.onTemplateButton}> <FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" /> </div> - </Flyout> + </Flyout> */} </div> </Tooltip> ); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index b8c77d8d5..b4c19df2d 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { IconButton } from 'browndash-components'; import { action, computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -34,7 +34,7 @@ import { DocumentView, OpenWhereMod } from './nodes/DocumentView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { ImageBox } from './nodes/ImageBox'; import * as React from 'react'; -import _ = require('lodash'); +import * as _ from 'lodash'; @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> { diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 9f0a257ca..5f0df3c5f 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -178,7 +178,7 @@ export class GestureOverlay extends React.Component<React.PropsWithChildren<Gest newPoints.pop(); const controlPoints: { X: number; Y: number }[] = []; - const bezierCurves = fitCurve(newPoints, 10); + const bezierCurves = (fitCurve as any)(newPoints, 10); for (const curve of bezierCurves) { controlPoints.push({ X: curve[0][0], Y: curve[0][1] }); controlPoints.push({ X: curve[1][0], Y: curve[1][1] }); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 86ae67085..2c3cd8eac 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -2,12 +2,12 @@ import * as React from 'react'; import { IconLookup } from '@fortawesome/fontawesome-svg-core'; import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Checkbox, Tooltip } from '@material-ui/core'; +import { Checkbox, Tooltip } from '@mui/material'; import { Colors, EditableText, IconButton, NumberInput, Size, Slider, Type } from 'browndash-components'; import { concat } from 'lodash'; import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; -import { ColorState, SketchPicker } from 'react-color'; +import { ColorResult, SketchPicker } from 'react-color'; import * as Icons from 'react-icons/bs'; //{BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs" import { Doc, DocListCast, Field, FieldResult, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc'; import { AclAdmin, DocAcl, DocData } from '../../fields/DocSymbols'; @@ -875,7 +875,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { return ( <SketchPicker onChange={undoable( - action((color: ColorState) => setter(color.hex)), + action((color: ColorResult) => setter(color.hex)), 'set stroke color property' )} presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']} diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 8bd0539ed..4a08081a1 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -1,6 +1,6 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { Dropdown, DropdownType, IconButton, IListItemProps, Shadows, Size, Type } from 'browndash-components'; import { action, runInAction, untracked } from 'mobx'; import { extname } from 'path'; @@ -320,9 +320,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps color={SettingsManager.userColor} background={showFilterIcon} items={[ ...(dashView ? [dashView]: []), ...(props?.docViewPath?.()??[]), ...(props?.DocumentView?[props?.DocumentView?.()]:[])] - .filter(dv => StrListCast(dv.Document.childFilters).length || StrListCast(dv.Document.childRangeFilters).length) + .filter(dv => StrListCast(dv?.Document.childFilters).length || StrListCast(dv?.Document.childRangeFilters).length) .map(dv => ({ - text: StrCast(dv.Document.title), + text: StrCast(dv?.Document.title), val: dv as any, style: {color:SettingsManager.userColor, background:SettingsManager.userBackgroundColor}, } as IListItemProps)) } diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss index 333ba9f32..a654596bd 100644 --- a/src/client/views/collections/CollectionDockingView.scss +++ b/src/client/views/collections/CollectionDockingView.scss @@ -276,7 +276,7 @@ z-index: 20; } /*# sourceMappingURL=goldenlayout-base.css.map */ -@import '../../../../node_modules/golden-layout/src/css/goldenlayout-dark-theme.css'; +// @import '../../../../node_modules/golden-layout/src/css/goldenlayout-dark-theme.css'; .lm_title { -webkit-appearance: none; @@ -683,7 +683,7 @@ ul.lm_tabs::before { .flexlayout__tab_button:hover .flexlayout__tab_button_trailing, .flexlayout__tab_button--selected .flexlayout__tab_button_trailing { - background: transparent url('../../../../node_modules/flexlayout-react/images/close_white.png') no-repeat center; + // background: transparent url('../../../../node_modules/flexlayout-react/images/close_white.png') no-repeat center; } .flexlayout__tab_button_overflow { @@ -696,7 +696,7 @@ ul.lm_tabs::before { font-size: 10px; color: lightgray; font-family: Arial, sans-serif; - background: transparent url('../../../../node_modules/flexlayout-react/images/more.png') no-repeat left; + // background: transparent url('../../../../node_modules/flexlayout-react/images/more.png') no-repeat left; } .flexlayout__tabset_header { @@ -751,7 +751,7 @@ ul.lm_tabs::before { height: 20px; border: none; outline-width: 0; - background: transparent url('../../../../node_modules/flexlayout-react/images/maximize.png') no-repeat center; + // background: transparent url('../../../../node_modules/flexlayout-react/images/maximize.png') no-repeat center; } .flexlayout__tab_toolbar_button-max { @@ -759,7 +759,7 @@ ul.lm_tabs::before { height: 20px; border: none; outline-width: 0; - background: transparent url('../../../../node_modules/flexlayout-react/images/restore.png') no-repeat center; + // background: transparent url('../../../../node_modules/flexlayout-react/images/restore.png') no-repeat center; } .flexlayout__popup_menu_item { @@ -879,7 +879,7 @@ ul.lm_tabs::before { .flexlayout__border_button:hover .flexlayout__border_button_trailing, .flexlayout__border_button--selected .flexlayout__border_button_trailing { - background: transparent url('../../../../node_modules/flexlayout-react/images/close_white.png') no-repeat center; + // background: transparent url('../../../../node_modules/flexlayout-react/images/close_white.png') no-repeat center; } .flexlayout__border_toolbar_left { diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 7724c786d..61a5738ec 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -18,9 +18,9 @@ import { EditableView } from '../EditableView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { CollectionStackingView } from './CollectionStackingView'; import './CollectionStackingView.scss'; -const higflyout = require('@hig/flyout'); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; +// import * as higflyout from '@hig/flyout'; +// export const { anchorPoints } = higflyout; +// export const Flyout = higflyout.default; interface CMVFieldRowProps { rows: () => number; @@ -338,11 +338,11 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr )} {noChrome || evContents === `NO ${key.toUpperCase()} VALUE` ? null : ( <div className="collectionStackingView-sectionOptions" onPointerDown={e => e.stopPropagation()}> - <Flyout anchorPoint={anchorPoints.RIGHT_TOP} content={this.renderMenu()}> + {/* <Flyout anchorPoint={anchorPoints.RIGHT_TOP} content={this.renderMenu()}> <button className="collectionStackingView-sectionOptionButton"> <FontAwesomeIcon icon="ellipsis-v" size="lg" /> </button> - </Flyout> + </Flyout> */} </div> )} </div> diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 2ac9c7bfb..268879bd4 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { Toggle, ToggleType, Type } from 'browndash-components'; import { action, computed, Lambda, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 545734e5b..ba00c4fa0 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import { CursorProperty } from 'csstype'; import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, Field, Opt } from '../../../fields/Doc'; @@ -44,7 +43,6 @@ export class CollectionNoteTakingView extends CollectionSubView() { notetakingCategoryField = 'NotetakingCategory'; public DividerWidth = 16; @observable docsDraggedRowCol: number[] = []; - @observable _cursor: CursorProperty = 'grab'; @observable _scroll = 0; @computed get chromeHidden() { return BoolCast(this.layoutDoc.chromeHidden) || this.props.onBrowseClick?.() ? true : false; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index e5f71a2f8..bc428c22f 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { CursorProperty } from 'csstype'; +import * as CSS from 'csstype'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, Opt } from '../../../fields/Doc'; @@ -59,7 +59,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection // map of node headers to their heights. Used in Masonry @observable _heightMap = new Map<string, number>(); // Assuming that this is the current css cursor style - @observable _cursor: CursorProperty = 'ew-resize'; + @observable _cursor: CSS.Property.Cursor = 'ew-resize'; // gets reset whenever we scroll. Not sure what it is @observable _scroll = 0; // used to force the document decoration to update when scrolling // does this mean whether the browser is hidden? Or is chrome something else entirely? diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index fac5e849b..4995925c8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -32,7 +32,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo componentWillUnmount() { this._anchorDisposer?.(); } - @action timeout = action(() => Date.now() < this._start++ + 1000 && (this._timeout = setTimeout(this.timeout, 25))); + @action timeout: any = action(() => Date.now() < this._start++ + 1000 && (this._timeout = setTimeout(this.timeout, 25))); componentDidMount() { this._anchorDisposer = reaction( () => [ diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx index d231197fb..825bd5f19 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx @@ -1,14 +1,11 @@ -import * as React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; -import { observer } from 'mobx-react'; -import { unimplementedFunction } from '../../../../Utils'; -import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; import { IconButton } from 'browndash-components'; -import { StrCast } from '../../../../fields/Types'; -import { Doc } from '../../../../fields/Doc'; import { computed } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { unimplementedFunction } from '../../../../Utils'; import { SettingsManager } from '../../../util/SettingsManager'; +import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; @observer export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> { diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index a367e3e73..0ee5f3b40 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { Toggle, ToggleType, Type } from 'browndash-components'; import { action, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 042acff05..12db7fa4b 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -1,6 +1,6 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc } from '../../../fields/Doc'; diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 8b9c5ae67..1fb26c99b 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { action, computed, IReactionDisposer, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { DateField } from '../../../fields/DateField'; diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx index 852d39800..b4ba51814 100644 --- a/src/client/views/nodes/ColorBox.tsx +++ b/src/client/views/nodes/ColorBox.tsx @@ -1,9 +1,8 @@ import * as React from 'react'; import { action } from 'mobx'; import { observer } from 'mobx-react'; -import { ColorState, SketchPicker } from 'react-color'; +import { ColorResult, SketchPicker } from 'react-color'; import { Doc } from '../../../fields/Doc'; -import { Height } from '../../../fields/DocSymbols'; import { InkTool } from '../../../fields/InkField'; import { NumCast, StrCast } from '../../../fields/Types'; import { DashColor } from '../../../Utils'; @@ -25,7 +24,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() { @undoBatch @action - static switchColor(color: ColorState) { + static switchColor(color: ColorResult) { SetActiveInkColor(color.hex); SelectionManager.Views().map(view => { diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index f9e7a4617..c2e03744e 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -1,3 +1,4 @@ +import { Checkbox } from '@mui/material'; import { ColorPicker, EditableText, Size, Type } from 'browndash-components'; import * as d3 from 'd3'; import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; @@ -12,7 +13,6 @@ import { Docs } from '../../../../documents/Documents'; import { undoable } from '../../../../util/UndoManager'; import { PinProps, PresBox } from '../../trails'; import './Chart.scss'; -import { Checkbox } from '@material-ui/core'; export interface PieChartProps { Document: Doc; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index b0c94a06a..54cfba506 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -1,6 +1,5 @@ import { computed } from 'mobx'; import { observer } from 'mobx-react'; -import { MouseEventHandler } from 'react-select'; import { Doc, Opt } from '../../../fields/Doc'; import { AclPrivate, DocData } from '../../../fields/DocSymbols'; import { ScriptField } from '../../../fields/ScriptField'; @@ -171,7 +170,7 @@ export class DocumentContentsView extends React.Component< ...this.props, Document: this.layoutDoc ?? this.props.Document, TemplateDataDocument: templateDataDoc instanceof Promise ? undefined : templateDataDoc, - onClick: onClick as any as MouseEventHandler, // pass onClick script as if it were a real function -- it will be interpreted properly in the HTMLtag + onClick: onClick as any as React.MouseEventHandler, // pass onClick script as if it were a real function -- it will be interpreted properly in the HTMLtag onInput: onInput as any as React.FormEventHandler, }; return { diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx index 49592d993..e8d8c05e3 100644 --- a/src/client/views/nodes/DocumentIcon.tsx +++ b/src/client/views/nodes/DocumentIcon.tsx @@ -4,7 +4,7 @@ import { DocumentView } from './DocumentView'; import { DocumentManager } from '../../util/DocumentManager'; import { Transformer, ts } from '../../util/Scripting'; import { Field } from '../../../fields/Doc'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { action, observable } from 'mobx'; import { Id } from '../../../fields/FieldSymbols'; import { factory } from 'typescript'; diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 9d233ead5..90b044374 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { action, computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc } from '../../../fields/Doc'; @@ -16,7 +16,7 @@ import { LinkDescriptionPopup } from './LinkDescriptionPopup'; import { TaskCompletionBox } from './TaskCompletedBox'; import { PinProps } from './trails'; import * as React from 'react'; -import _ = require('lodash'); +import * as _ from 'lodash'; interface DocumentLinksButtonProps { View: DocumentView; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 336891a18..dbfeec1c3 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { extname } from 'path'; diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 8f3c75f54..3cda70648 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -14,7 +14,7 @@ import './KeyValueBox.scss'; import './KeyValuePair.scss'; import * as React from 'react'; import { DocCast } from '../../../fields/Types'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { DocumentOptions, FInfo } from '../../documents/Documents'; // Represents one row in a key value plane diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index e1421878b..71f9c000b 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -84,7 +84,18 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor); } - fitTextToBox = (r: any) => { + fitTextToBox = (r: any): NodeJS.Timeout | { + rotateText: null; + fontSizeFactor: number; + minimumFontSize: number; + maximumFontSize: number; + limitingDimension: string; + horizontalAlign: string; + verticalAlign: string; + textAlign: string; + singleLine: boolean; + whiteSpace: string; + } => { const singleLine = BoolCast(this.layoutDoc._singleLine, true); const params = { rotateText: null, @@ -143,9 +154,9 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps & LabelBoxProp paddingBottom: NumCast(this.layoutDoc._yPadding), width: this.props.PanelWidth(), height: this.props.PanelHeight(), - whiteSpace: typeof boxParams !== 'number' && boxParams.singleLine ? 'pre' : 'pre-wrap', + whiteSpace: !(boxParams instanceof NodeJS.Timeout) && boxParams.singleLine ? 'pre' : 'pre-wrap', }}> - <span style={{ width: typeof boxParams !== 'number' && boxParams.singleLine ? '' : '100%' }} ref={action((r: any) => this.fitTextToBox(r))}> + <span style={{ width: !(boxParams instanceof NodeJS.Timeout) && boxParams.singleLine ? '' : '100%' }} ref={action((r: any) => this.fitTextToBox(r))}> {label.startsWith('#') ? null : label.replace(/([^a-zA-Z])/g, '$1\u200b')} </span> </div> diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index b5fb6db86..4df8e792e 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import wiki from 'wikijs'; diff --git a/src/client/views/nodes/TaskCompletedBox.tsx b/src/client/views/nodes/TaskCompletedBox.tsx index a17b1dee9..9aab8c5a9 100644 --- a/src/client/views/nodes/TaskCompletedBox.tsx +++ b/src/client/views/nodes/TaskCompletedBox.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { observer } from 'mobx-react'; import './TaskCompletedBox.scss'; import { observable, action } from 'mobx'; -import { Fade } from '@material-ui/core'; +import { Fade } from '@mui/material'; @observer export class TaskCompletionBox extends React.Component<{}> { diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 16622c258..a395296d0 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { action, computed, IReactionDisposer, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as ReactDOM from 'react-dom/client'; diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx index 85a33614c..75238a6ce 100644 --- a/src/client/views/nodes/formattedText/EquationView.tsx +++ b/src/client/views/nodes/formattedText/EquationView.tsx @@ -101,7 +101,7 @@ export class EquationViewInternal extends React.Component<IEquationViewInternal> <EquationEditor ref={this._ref} value={StrCast(this._textBoxDoc[this._fieldKey], 'y=')} - onChange={str => (this._textBoxDoc[this._fieldKey] = str)} + onChange={(str: any) => (this._textBoxDoc[this._fieldKey] = str)} autoCommands="pi theta sqrt sum prod alpha beta gamma rho" autoOperatorNames="sin cos tan" spaceBehavesLikeTab={true} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 2315cdccb..0f9dd6f8b 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1,7 +1,6 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; -import { setCORS } from 'google-translate-api-browser'; +import { Tooltip } from '@mui/material'; import { isEqual } from 'lodash'; import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -71,10 +70,8 @@ import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu'; import { RichTextRules } from './RichTextRules'; import { schema } from './schema_rts'; import { SummaryView } from './SummaryView'; -import applyDevTools = require('prosemirror-dev-tools'); +import * as applyDevTools from 'prosemirror-dev-tools'; import * as React from 'react'; -// setting up cors-anywhere server address -const translate = setCORS('http://cors-anywhere.herokuapp.com/'); export const GoogleRef = 'googleDocId'; type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void; @@ -797,7 +794,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps ?.trim() .split(' ') .filter(h => h); - const anchorDoc = Array.from(hrefs).lastElement().replace(Doc.localServerPath(), '').split('?')[0]; + const anchorDoc = Array.from(hrefs ?? []) + .lastElement() + .replace(Doc.localServerPath(), '') + .split('?')[0]; const deleteMarkups = undoBatch(() => { const sel = editor.state.selection; editor.dispatch(editor.state.tr.removeMark(sel.from, sel.to, editor.state.schema.marks.linkAnchor)); @@ -1308,7 +1308,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps if (this._editorView && reference) { const content = await RichTextUtils.GoogleDocs.Export(this._editorView.state); const response = await GoogleApiClientUtils.Docs.write({ reference, content, mode }); - response && (this.dataDoc[GoogleRef] = response.documentId); + response?.documentId && (this.dataDoc[GoogleRef] = response.documentId); const pushSuccess = response !== undefined && !('errors' in response); dataDoc.googleDocUnchanged = pushSuccess; DocumentButtonBar.Instance.startPushOutcome(pushSuccess); @@ -1783,23 +1783,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps const state = this._editorView!.state; const curText = state.doc.textBetween(0, state.doc.content.size, ' \n'); - if (this.layoutDoc[this.SidebarKey + '_type_collection'] === 'translation' && !this.fieldKey.includes('translation') && curText.endsWith(' ') && curText !== this._lastText) { - try { - translate(curText, { from: 'en', to: 'es' }).then((result1: any) => { - setTimeout( - () => - translate(result1.text, { from: 'es', to: 'en' }).then((result: any) => { - const tb = this._sidebarTagRef.current as FormattedTextBox; - tb._editorView?.dispatch(tb._editorView!.state.tr.insertText(result1.text + '\r\n\r\n' + result.text)); - }), - 1000 - ); - }); - } catch (e: any) { - console.log(e.message); - } - this._lastText = curText; - } if (StrCast(this.Document.title).startsWith('@') && !this.dataDoc.title_custom) { UndoManager.RunInBatch(() => { this.dataDoc.title_custom = true; diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 206d96923..7ce06cf7f 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { lift, wrapIn } from 'prosemirror-commands'; diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 6753d5051..87e1b69c3 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -438,7 +438,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD // disable once edited has been clicked (doesn't make sense to change after first edit) disabled={edited} checked={isNewCollection} - onChange={e => { + onChange={() => { setIsNewCollection(prev => !prev); }} /> @@ -513,7 +513,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD defaultValue={150} size="small" valueLabelDisplay="auto" - onChange={(e, val) => { + onChange={(e: any, val: any) => { setCursorData(prev => ({ ...prev, width: val as number })); }} /> @@ -571,7 +571,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD <div> <TextField value={input} - onChange={e => setInput(e.target.value)} + onChange={(e: any) => setInput(e.target.value)} disabled={isBrushing} type="text" label="Prompt" diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 786988d8b..1317ca814 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, DocListCast, FieldResult, NumListCast, Opt, StrListCast } from '../../../../fields/Doc'; diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 0effb2ccc..cd6beac57 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, DocListCast, Opt } from '../../../../fields/Doc'; diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 35c27d81b..2d8e9e2a2 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components'; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction } from 'mobx'; import { observer } from 'mobx-react'; -import { ColorState } from 'react-color'; +import { ColorResult } from 'react-color'; import { Doc, Opt } from '../../../fields/Doc'; import { returnFalse, setupMoveUpEvents, unimplementedFunction, Utils } from '../../../Utils'; import { gptAPICall, GPTCallType } from '../../apis/gpt/GPT'; @@ -139,13 +139,10 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { } @action changeHighlightColor = (color: string) => { - const col: ColorState = { + const col: ColorResult = { hex: color, - hsl: { a: 0, h: 0, s: 0, l: 0, source: '' }, - hsv: { a: 0, h: 0, s: 0, v: 0, source: '' }, - rgb: { a: 0, r: 0, b: 0, g: 0, source: '' }, - oldHue: 0, - source: '', + hsl: { a: 0, h: 0, s: 0, l: 0 }, + rgb: { a: 0, r: 0, b: 0, g: 0 }, }; this.highlightColor = Utils.colorString(col); }; diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 74464181f..3172ff9c0 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -1,4 +1,4 @@ -import { Tooltip } from '@material-ui/core'; +import { Tooltip } from '@mui/material'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/server/ActionUtilities.ts b/src/server/ActionUtilities.ts index bc8fd6f87..55b50cc12 100644 --- a/src/server/ActionUtilities.ts +++ b/src/server/ActionUtilities.ts @@ -4,9 +4,9 @@ import { createWriteStream, exists, mkdir, readFile, unlink, writeFile } from 'f import * as nodemailer from "nodemailer"; import { MailOptions } from "nodemailer/lib/json-transport"; import * as path from 'path'; -import * as rimraf from "rimraf"; +import { rimraf } from "rimraf"; import { ExecOptions } from 'shelljs'; -import Mail = require('nodemailer/lib/mailer'); +import * as Mail from 'nodemailer/lib/mailer'; const projectRoot = path.resolve(__dirname, "../../"); export function pathFromRoot(relative?: string) { @@ -103,8 +103,10 @@ export const createIfNotExists = async (path: string) => { }; export async function Prune(rootDirectory: string): Promise<boolean> { - const error = await new Promise<Error>(resolve => rimraf(rootDirectory, resolve)); - return error === null; + // const error = await new Promise<Error>(resolve => rimraf(rootDirectory).then(resolve)); + await new Promise<void>(resolve => rimraf(rootDirectory).then(() => resolve())); + // return error === null; + return true; } export const Destroy = (mediaPath: string) => new Promise<boolean>(resolve => unlink(mediaPath, error => resolve(error === null))); diff --git a/src/server/ApiManagers/DeleteManager.ts b/src/server/ApiManagers/DeleteManager.ts index 46c0d8a8a..56d8aff60 100644 --- a/src/server/ApiManagers/DeleteManager.ts +++ b/src/server/ApiManagers/DeleteManager.ts @@ -2,7 +2,7 @@ import ApiManager, { Registration } from "./ApiManager"; import { Method, _permission_denied } from "../RouteManager"; import { WebSocket } from "../websocket"; import { Database } from "../database"; -import rimraf = require("rimraf"); +import { rimraf } from "rimraf"; import { filesDirectory } from ".."; import { DashUploadUtils } from "../DashUploadUtils"; import { mkdirSync } from "fs"; diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index 391f67bbb..42b674ad1 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -11,7 +11,7 @@ import RouteSubscriber from '../RouteSubscriber'; import { AcceptableMedia, Upload } from '../SharedMediaTypes'; import ApiManager, { Registration } from './ApiManager'; import { SolrManager } from './SearchManager'; -import v4 = require('uuid/v4'); +import * as v4 from 'uuid/v4'; import { DashVersion } from '../../fields/DocSymbols'; const AdmZip = require('adm-zip'); const imageDataUri = require('image-data-uri'); @@ -63,7 +63,7 @@ export default class UploadManager extends ApiManager { method: Method.POST, subscription: '/uploadFormData', secureHandler: async ({ req, res }) => { - const form = new formidable.IncomingForm(); + const form = new formidable.IncomingForm({ keepExtensions: true, uploadDir: pathToDirectory(Directory.parsed_files) }); let fileguids = ''; let filesize = ''; form.on('field', (e: string, value: string) => { @@ -77,19 +77,19 @@ export default class UploadManager extends ApiManager { fileguids.split(';').map(guid => DashUploadUtils.uploadProgress.set(guid, `upload starting`)); form.on('progress', e => fileguids.split(';').map(guid => DashUploadUtils.uploadProgress.set(guid, `read:(${Math.round((100 * +e) / +filesize)}%) ${e} of ${filesize}`))); - form.keepExtensions = true; - form.uploadDir = pathToDirectory(Directory.parsed_files); return new Promise<void>(resolve => { form.parse(req, async (_err, _fields, files) => { const results: Upload.FileResponse[] = []; if (_err?.message) { results.push({ source: { + filepath: '', + originalFilename: 'none', + newFilename: 'none', + mimetype: 'text', size: 0, - path: 'none', - name: 'none', - type: 'none', - toJSON: () => ({ name: 'none', path: '' }), + hashAlgorithm: 'md5', + toJSON: () => ({ name: 'none', size: 0, length: 0, mtime: new Date(), filepath: '', originalFilename: 'none', newFilename: 'none', mimetype: 'text' }), }, result: { name: 'failed upload', message: `${_err.message}` }, }); @@ -98,8 +98,8 @@ export default class UploadManager extends ApiManager { for (const key in files) { const f = files[key]; - if (!Array.isArray(f)) { - const result = await DashUploadUtils.upload(f, key); // key is the guid used by the client to track upload progress. + if (f) { + const result = await DashUploadUtils.upload(f[0], key); // key is the guid used by the client to track upload progress. result && !(result.result instanceof Error) && results.push(result); } } @@ -197,8 +197,7 @@ export default class UploadManager extends ApiManager { method: Method.POST, subscription: '/uploadDoc', secureHandler: ({ req, res }) => { - const form = new formidable.IncomingForm(); - form.keepExtensions = true; + const form = new formidable.IncomingForm({ keepExtensions: true }); // let path = req.body.path; const ids: { [id: string]: string } = {}; let remap = true; @@ -245,15 +244,16 @@ export default class UploadManager extends ApiManager { }; return new Promise<void>(resolve => { form.parse(req, async (_err, fields, files) => { - remap = fields.remap !== 'false'; + remap = Object.keys(fields).some(key => key === 'remap' && !fields.remap?.includes('false')); //.remap !== 'false'; // bcz: looking to see if the field 'remap' is set to 'false' let id: string = ''; let docids: string[] = []; let linkids: string[] = []; try { for (const name in files) { const f = files[name]; - const path_2 = Array.isArray(f) ? '' : f.path; - const zip = new AdmZip(path_2); + if (!f) continue; + const path_2 = f[0]; // what about the rest of the array? are we guaranteed only one value is set? + const zip = new AdmZip(path_2.filepath); zip.getEntries().forEach((entry: any) => { let entryName = entry.entryName.replace(/%%%/g, '/'); if (!entryName.startsWith('files/')) { @@ -299,7 +299,7 @@ export default class UploadManager extends ApiManager { } catch (e) { console.log(e); } - unlink(path_2, () => {}); + unlink(path_2.filepath, () => {}); } SolrManager.update(); res.send(JSON.stringify({ id, docids, linkids } || 'error')); diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts index 8b7994eac..9252202b0 100644 --- a/src/server/ApiManagers/UserManager.ts +++ b/src/server/ApiManagers/UserManager.ts @@ -32,7 +32,7 @@ export default class UserManager extends ApiManager { secureHandler: async ({ user, req, res }) => { const result: any = {}; user.cacheDocumentIds = req.body.cacheDocumentIds; - user.save(err => { + user.save().then(undefined, err => { if (err) { result.error = [{ msg: 'Error while caching documents' }]; } @@ -125,7 +125,7 @@ export default class UserManager extends ApiManager { user.passwordResetExpires = undefined; } - user.save(err => { + user.save().then(undefined, err => { if (err) { result.error = [{ msg: 'Error while saving new password' }]; } diff --git a/src/server/DashSession/DashSessionAgent.ts b/src/server/DashSession/DashSessionAgent.ts index 2450798aa..1ef7a131d 100644 --- a/src/server/DashSession/DashSessionAgent.ts +++ b/src/server/DashSession/DashSessionAgent.ts @@ -8,7 +8,7 @@ import { launchServer, onWindows } from '..'; import { readdirSync, statSync, createWriteStream, readFileSync, unlinkSync } from 'fs'; import * as Archiver from 'archiver'; import { resolve } from 'path'; -import * as rimraf from 'rimraf'; +import { rimraf } from 'rimraf'; import { AppliedSessionAgent, ExitHandler } from './Session/agents/applied_session_agent'; import { ServerWorker } from './Session/agents/server_worker'; import { Monitor } from './Session/agents/monitor'; diff --git a/src/server/DashSession/Session/agents/applied_session_agent.ts b/src/server/DashSession/Session/agents/applied_session_agent.ts index 8339a06dc..2037e93e5 100644 --- a/src/server/DashSession/Session/agents/applied_session_agent.ts +++ b/src/server/DashSession/Session/agents/applied_session_agent.ts @@ -1,7 +1,8 @@ -import { isMaster } from "cluster"; +import * as _cluster from "cluster"; import { Monitor } from "./monitor"; import { ServerWorker } from "./server_worker"; -import { Utilities } from "../utilities/utilities"; +const cluster = _cluster as any; +const isMaster = cluster.isPrimary; export type ExitHandler = (reason: Error | boolean) => void | Promise<void>; @@ -15,13 +16,13 @@ export abstract class AppliedSessionAgent { private launched = false; public killSession = (reason: string, graceful = true, errorCode = 0) => { - const target = isMaster ? this.sessionMonitor : this.serverWorker; + const target = cluster.default.isPrimary ? this.sessionMonitor : this.serverWorker; target.killSession(reason, graceful, errorCode); } private sessionMonitorRef: Monitor | undefined; public get sessionMonitor(): Monitor { - if (!isMaster) { + if (!cluster.default.isPrimary) { this.serverWorker.emit("kill", { graceful: false, reason: "Cannot access the session monitor directly from the server worker thread.", diff --git a/src/server/DashSession/Session/agents/monitor.ts b/src/server/DashSession/Session/agents/monitor.ts index 9cb5ab576..0f469285e 100644 --- a/src/server/DashSession/Session/agents/monitor.ts +++ b/src/server/DashSession/Session/agents/monitor.ts @@ -1,7 +1,8 @@ import { ExitHandler } from "./applied_session_agent"; import { Configuration, configurationSchema, defaultConfig, Identifiers, colorMapping } from "../utilities/session_config"; import Repl, { ReplAction } from "../utilities/repl"; -import { isWorker, setupMaster, on, Worker, fork } from "cluster"; +import * as _cluster from "cluster"; +import { Worker } from "cluster"; import { manage, MessageHandler, ErrorLike } from "./promisified_ipc_manager"; import { red, cyan, white, yellow, blue } from "colors"; import { exec, ExecOptions } from "child_process"; @@ -10,6 +11,11 @@ import { Utilities } from "../utilities/utilities"; import { readFileSync } from "fs"; import IPCMessageReceiver from "./process_message_router"; import { ServerWorker } from "./server_worker"; +const cluster = _cluster as any; +const isWorker = cluster.isWorker; +const setupMaster = cluster.setupPrimary; +const on = cluster.on; +const fork = cluster.fork; /** * Validates and reads the configuration file, accordingly builds a child process factory @@ -20,7 +26,7 @@ export class Monitor extends IPCMessageReceiver { private finalized = false; private exitHandlers: ExitHandler[] = []; private readonly config: Configuration; - private activeWorker: Worker | undefined; + private activeWorker: Worker| undefined; private key: string | undefined; private repl: Repl; @@ -281,8 +287,11 @@ export class Monitor extends IPCMessageReceiver { pollingIntervalSeconds: polling.intervalSeconds, session_key: key }); - Monitor.IPCManager = manage(this.activeWorker.process, this.handlers); + if (this.activeWorker) { + Monitor.IPCManager = manage(this.activeWorker.process, this.handlers); + } this.mainLog(cyan(`spawned new server worker with process id ${this.activeWorker?.process.pid}`)); + } } diff --git a/src/server/DashSession/Session/agents/server_worker.ts b/src/server/DashSession/Session/agents/server_worker.ts index 634b0113d..d8b3ee80b 100644 --- a/src/server/DashSession/Session/agents/server_worker.ts +++ b/src/server/DashSession/Session/agents/server_worker.ts @@ -1,4 +1,4 @@ -import { isMaster } from "cluster"; +import cluster from "cluster"; import { green, red, white, yellow } from "colors"; import { get } from "request-promise"; import { ExitHandler } from "./applied_session_agent"; @@ -22,7 +22,7 @@ export class ServerWorker extends IPCMessageReceiver { private serverPort: number; private isInitialized = false; public static Create(work: Function) { - if (isMaster) { + if (cluster.isPrimary) { console.error(red("cannot create a worker on the monitor process.")); process.exit(1); } else if (++ServerWorker.count > 1) { diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index fa29cb7c1..643626ae9 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -119,14 +119,14 @@ export namespace DashUploadUtils { }; } - function resolveExistingFile(name: string, pat: string, directory: Directory, type?: string, duration?: number, rawText?: string) { - const data = { size: 0, path: path.basename(pat), name, type: type ?? '' }; - const file = { ...data, toJSON: () => ({ ...data, filename: data.path.replace(/.*\//, ''), mtime: duration?.toString(), mime: '', toJson: () => undefined as any }) }; + function resolveExistingFile(name: string, pat: string, directory: Directory, type?: string | null, duration?: number, rawText?: string): Upload.FileResponse<Upload.FileInformation> { + const data = { size: 0, filepath: path.basename(pat), name, type: type ?? '', originalFilename: name, newFilename: name, mimetype: '', hashAlgorithm: 'md5' as 'md5' }; + const file = { ...data, toJSON: () => ({ ...data, length: 0, filename: data.filepath.replace(/.*\//, ''), mtime: new Date(), mime: '', toJson: () => undefined as any }) }; return { source: file, result: { accessPaths: { - agnostic: getAccessPaths(directory, data.path), + agnostic: getAccessPaths(directory, data.filepath), }, rawText, duration, @@ -144,8 +144,8 @@ export namespace DashUploadUtils { export function uploadYoutube(videoId: string, overwriteId: string): Promise<Upload.FileResponse> { return new Promise<Upload.FileResponse<Upload.FileInformation>>((res, rej) => { const name = videoId; - const path = name.replace(/^-/, '__') + '.mp4'; - const finalPath = serverPathToFile(Directory.videos, path); + const filepath = name.replace(/^-/, '__') + '.mp4'; + const finalPath = serverPathToFile(Directory.videos, filepath); if (existsSync(finalPath)) { uploadProgress.set(overwriteId, 'computing duration'); exec(`yt-dlp -o ${finalPath} "https://www.youtube.com/watch?v=${videoId}" --get-duration`, (error: any, stdout: any, stderr: any) => { @@ -155,7 +155,7 @@ export namespace DashUploadUtils { }); } else { uploadProgress.set(overwriteId, 'starting download'); - const ytdlp = spawn(`yt-dlp`, ['-o', path, `https://www.youtube.com/watch?v=${videoId}`, '--max-filesize', '100M', '-f', 'mp4']); + const ytdlp = spawn(`yt-dlp`, ['-o', filepath, `https://www.youtube.com/watch?v=${videoId}`, '--max-filesize', '100M', '-f', 'mp4']); ytdlp.stdout.on('data', (data: any) => uploadProgress.set(overwriteId, data.toString())); @@ -170,20 +170,20 @@ export namespace DashUploadUtils { res({ source: { size: 0, - path, - name, + path: filepath, + newFilename: name, type: '', - toJSON: () => ({ name, path }), + toJSON: () => ({ newFilename: name, filepath }), }, result: { name: 'failed youtube query', message: `Could not archive video. ${code ? errors : uploadProgress.get(videoId)}` }, }); } else { uploadProgress.set(overwriteId, 'computing duration'); - exec(`yt-dlp-o ${path} "https://www.youtube.com/watch?v=${videoId}" --get-duration`, (error: any, stdout: any, stderr: any) => { + exec(`yt-dlp-o ${filepath} "https://www.youtube.com/watch?v=${videoId}" --get-duration`, (error: any, stdout: any, stderr: any) => { const time = Array.from(stdout.trim().split(':')).reverse(); const duration = (time.length > 2 ? Number(time[2]) * 1000 * 60 : 0) + (time.length > 1 ? Number(time[1]) * 60 : 0) + (time.length > 0 ? Number(time[0]) : 0); - const data = { size: 0, path, name, type: 'video/mp4' }; - const file = { ...data, toJSON: () => ({ ...data, filename: data.path.replace(/.*\//, ''), mtime: duration.toString(), mime: '', toJson: () => undefined as any }) }; + const data = { size: 0, filepath, name, mimetype: '', originalFilename: name, newFilename: name, hashAlgorithm: 'md5' as 'md5', type: 'video/mp4' }; + const file = { ...data, toJSON: () => ({ ...data, length: 0, filename: data.filepath.replace(/.*\//, ''), mtime: new Date(), mime: '', hashAlgorithm: 'md5' as 'md5', toJson: () => undefined as any }) }; res(MoveParsedFile(file, Directory.videos)); }); } @@ -194,7 +194,7 @@ export namespace DashUploadUtils { export async function upload(file: File, overwriteGuid?: string): Promise<Upload.FileResponse> { const isAzureOn = usingAzure(); - const { type, path, name } = file; + const { mimetype: type, filepath, originalFilename } = file; const types = type?.split('/') ?? []; // uploadProgress.set(overwriteGuid ?? name, 'uploading'); // If the client sent a guid it uses to track upload progress, use that guid. Otherwise, use the file's name. @@ -205,7 +205,7 @@ export namespace DashUploadUtils { switch (category) { case 'image': if (imageFormats.includes(format)) { - const result = await UploadImage(path, basename(path)); + const result = await UploadImage(filepath, basename(filepath)); return { source: file, result }; } fs.unlink(path, () => {}); @@ -214,19 +214,19 @@ export namespace DashUploadUtils { if (format.includes('x-matroska')) { console.log('case video'); await new Promise(res => - ffmpeg(file.path) + ffmpeg(file.filepath) .videoCodec('copy') // this will copy the data instead of reencode it - .save(file.path.replace('.mkv', '.mp4')) + .save(file.filepath.replace('.mkv', '.mp4')) .on('end', res) .on('error', (e: any) => console.log(e)) ); - file.path = file.path.replace('.mkv', '.mp4'); + file.filepath = file.filepath.replace('.mkv', '.mp4'); format = '.mp4'; } if (format.includes('quicktime')) { let abort = false; await new Promise<void>(res => - ffmpeg.ffprobe(file.path, (err: any, metadata: any) => { + ffmpeg.ffprobe(file.filepath, (err: any, metadata: any) => { if (metadata.streams.some((stream: any) => stream.codec_name === 'hevc')) { abort = true; } @@ -280,18 +280,18 @@ export namespace DashUploadUtils { } async function UploadPdf(file: File) { - const fileKey = (await md5File(file.path)) + '.pdf'; + const fileKey = (await md5File(file.filepath)) + '.pdf'; const textFilename = `${fileKey.substring(0, fileKey.length - 4)}.txt`; if (fExists(fileKey, Directory.pdfs) && fExists(textFilename, Directory.text)) { - fs.unlink(file.path, () => {}); + fs.unlink(file.filepath, () => {}); return new Promise<Upload.FileResponse>(res => { const textFilename = `${fileKey.substring(0, fileKey.length - 4)}.txt`; const readStream = createReadStream(serverPathToFile(Directory.text, textFilename)); var rawText = ''; - readStream.on('data', chunk => (rawText += chunk.toString())).on('end', () => res(resolveExistingFile(file.name, fileKey, Directory.pdfs, file.type, undefined, rawText))); + readStream.on('data', chunk => (rawText += chunk.toString())).on('end', () => res(resolveExistingFile(file.originalFilename ?? '', fileKey, Directory.pdfs, file.mimetype, undefined, rawText))); }); } - const dataBuffer = readFileSync(file.path); + const dataBuffer = readFileSync(file.filepath); const result: ParsedPDF | any = await parse(dataBuffer).catch((e: any) => e); if (!result.code) { await new Promise<void>((resolve, reject) => { @@ -300,11 +300,11 @@ export namespace DashUploadUtils { }); return MoveParsedFile(file, Directory.pdfs, undefined, result?.text, undefined, fileKey); } - return { source: file, result: { name: 'faile pdf pupload', message: `Could not upload (${file.name}).${result.message}` } }; + return { source: file, result: { name: 'faile pdf pupload', message: `Could not upload (${file.originalFilename}).${result.message}` } }; } async function UploadCsv(file: File) { - const { path: sourcePath } = file; + const { filepath: sourcePath } = file; // read the file as a string const data = readFileSync(sourcePath, 'utf8'); // split the string into an array of lines @@ -462,12 +462,12 @@ export namespace DashUploadUtils { * to appear in the new location */ export async function MoveParsedFile(file: formidable.File, destination: Directory, suffix: string | undefined = undefined, text?: string, duration?: number, targetName?: string): Promise<Upload.FileResponse> { - const { path: sourcePath } = file; - let name = targetName ?? path.basename(sourcePath); + const { filepath } = file; + let name = targetName ?? path.basename(filepath); suffix && (name += suffix); return new Promise(resolve => { const destinationPath = serverPathToFile(destination, name); - rename(sourcePath, destinationPath, error => { + rename(filepath, destinationPath, error => { resolve({ source: file, result: error diff --git a/src/server/GarbageCollector.ts b/src/server/GarbageCollector.ts index c60880882..423c719c2 100644 --- a/src/server/GarbageCollector.ts +++ b/src/server/GarbageCollector.ts @@ -65,7 +65,7 @@ async function GarbageCollect(full: boolean = true) { // await new Promise(res => setTimeout(res, 3000)); const cursor = await Database.Instance.query({}, { userDocumentId: 1 }, 'users'); const users = await cursor.toArray(); - const ids: string[] = [...users.map(user => user.userDocumentId), ...users.map(user => user.sharingDocumentId), ...users.map(user => user.linkDatabaseId)]; + const ids: string[] = [...users.map((user:any) => user.userDocumentId), ...users.map((user:any) => user.sharingDocumentId), ...users.map((user:any) => user.linkDatabaseId)]; const visited = new Set<string>(); const files: { [name: string]: string[] } = {}; @@ -95,7 +95,7 @@ async function GarbageCollect(full: boolean = true) { const notToDelete = Array.from(visited); const toDeleteCursor = await Database.Instance.query({ _id: { $nin: notToDelete } }, { _id: 1 }); - const toDelete: string[] = (await toDeleteCursor.toArray()).map(doc => doc._id); + const toDelete: string[] = (await toDeleteCursor.toArray()).map((doc:any) => doc._id); toDeleteCursor.close(); if (!full) { await Database.Instance.updateMany({ _id: { $nin: notToDelete } }, { $set: { "deleted": true } }); diff --git a/src/server/IDatabase.ts b/src/server/IDatabase.ts index dd4968579..2274792b3 100644 --- a/src/server/IDatabase.ts +++ b/src/server/IDatabase.ts @@ -3,13 +3,13 @@ import { Transferable } from './Message'; export const DocumentsCollection = 'documents'; export interface IDatabase { - update(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, upsert?: boolean, collectionName?: string): Promise<void>; - updateMany(query: any, update: any, collectionName?: string): Promise<mongodb.WriteOpResult>; + update(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateResult) => void, upsert?: boolean, collectionName?: string): Promise<void>; + updateMany(query: any, update: any, collectionName?: string): Promise<mongodb.UpdateResult>; - replace(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, upsert?: boolean, collectionName?: string): void; + replace(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateResult) => void, upsert?: boolean, collectionName?: string): void; - delete(query: any, collectionName?: string): Promise<mongodb.DeleteWriteOpResultObject>; - delete(id: string, collectionName?: string): Promise<mongodb.DeleteWriteOpResultObject>; + delete(query: any, collectionName?: string): Promise<mongodb.DeleteResult>; + delete(id: string, collectionName?: string): Promise<mongodb.DeleteResult>; dropSchema(...schemaNames: string[]): Promise<any>; @@ -20,5 +20,5 @@ export interface IDatabase { getCollectionNames(): Promise<string[]>; visit(ids: string[], fn: (result: any) => string[] | Promise<string[]>, collectionName?: string): Promise<void>; - query(query: { [key: string]: any }, projection?: { [key: string]: 0 | 1 }, collectionName?: string): Promise<mongodb.Cursor>; + query(query: { [key: string]: any }, projection?: { [key: string]: 0 | 1 }, collectionName?: string): Promise<mongodb.FindCursor>; } diff --git a/src/server/MemoryDatabase.ts b/src/server/MemoryDatabase.ts index d2d8bb3b3..b74332bf5 100644 --- a/src/server/MemoryDatabase.ts +++ b/src/server/MemoryDatabase.ts @@ -19,7 +19,7 @@ export class MemoryDatabase implements IDatabase { return Promise.resolve(Object.keys(this.db)); } - public update(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, _upsert?: boolean, collectionName = DocumentsCollection): Promise<void> { + public update(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateResult) => void, _upsert?: boolean, collectionName = DocumentsCollection): Promise<void> { const collection = this.getCollection(collectionName); const set = "$set"; if (set in value) { @@ -45,17 +45,17 @@ export class MemoryDatabase implements IDatabase { return Promise.resolve(undefined); } - public updateMany(query: any, update: any, collectionName = DocumentsCollection): Promise<mongodb.WriteOpResult> { + public updateMany(query: any, update: any, collectionName = DocumentsCollection): Promise<mongodb.UpdateResult> { throw new Error("Can't updateMany a MemoryDatabase"); } - public replace(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, upsert?: boolean, collectionName = DocumentsCollection): void { + public replace(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateResult) => void, upsert?: boolean, collectionName = DocumentsCollection): void { this.update(id, value, callback, upsert, collectionName); } - public delete(query: any, collectionName?: string): Promise<mongodb.DeleteWriteOpResultObject>; - public delete(id: string, collectionName?: string): Promise<mongodb.DeleteWriteOpResultObject>; - public delete(id: any, collectionName = DocumentsCollection): Promise<mongodb.DeleteWriteOpResultObject> { + public delete(query: any, collectionName?: string): Promise<mongodb.DeleteResult>; + public delete(id: string, collectionName?: string): Promise<mongodb.DeleteResult>; + public delete(id: any, collectionName = DocumentsCollection): Promise<mongodb.DeleteResult> { const i = id.id ?? id; delete this.getCollection(collectionName)[i]; @@ -105,7 +105,7 @@ export class MemoryDatabase implements IDatabase { } } - public query(): Promise<mongodb.Cursor> { + public query(): Promise<mongodb.FindCursor> { throw new Error("Can't query a MemoryDatabase"); } } diff --git a/src/server/ProcessFactory.ts b/src/server/ProcessFactory.ts index 63682368f..f69eda4c3 100644 --- a/src/server/ProcessFactory.ts +++ b/src/server/ProcessFactory.ts @@ -2,7 +2,7 @@ import { ChildProcess, spawn, StdioOptions } from "child_process"; import { existsSync, mkdirSync } from "fs"; import { Stream } from "stream"; import { fileDescriptorFromStream, pathFromRoot } from './ActionUtilities'; -import rimraf = require("rimraf"); +import { rimraf } from "rimraf"; export namespace ProcessFactory { @@ -13,7 +13,7 @@ export namespace ProcessFactory { const log_fd = await Logger.create(command, args); stdio = ["ignore", log_fd, log_fd]; } - const child = spawn(command, args, { detached, stdio }); + const child = spawn(command, args ?? [], { detached, stdio }); child.unref(); return child; } @@ -27,7 +27,7 @@ export namespace Logger { export async function initialize() { if (existsSync(logPath)) { if (!process.env.SPAWNED) { - await new Promise<any>(resolve => rimraf(logPath, resolve)); + await new Promise<any>(resolve => rimraf(logPath).then(resolve)); } } mkdirSync(logPath); diff --git a/src/server/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts index 52d876e95..74d8d2523 100644 --- a/src/server/authentication/AuthenticationManager.ts +++ b/src/server/authentication/AuthenticationManager.ts @@ -3,10 +3,9 @@ import { Request, Response, NextFunction } from 'express'; import * as passport from 'passport'; import { IVerifyOptions } from 'passport-local'; import './Passport'; -import flash = require('express-flash'); import * as async from 'async'; import * as nodemailer from 'nodemailer'; -import c = require('crypto'); +import * as c from 'crypto'; import { emptyFunction, Utils } from '../../Utils'; import { MailOptions } from 'nodemailer/lib/stream-transport'; @@ -62,7 +61,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => { if (existingUser) { return res.redirect('/login'); } - user.save((err: any) => { + user.save().then(undefined, (err: any) => { if (err) { return next(err); } @@ -187,7 +186,7 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio } user.passwordResetToken = token; user.passwordResetExpires = new Date(Date.now() + 3600000); // 1 HOUR - user.save(function (err: any) { + user.save().then(undefined, (err: any) => { done(null, token, user); }); }); @@ -259,7 +258,7 @@ export let postReset = function (req: Request, res: Response) { user.passwordResetToken = undefined; user.passwordResetExpires = undefined; - user.save(function (err) { + user.save().then(undefined, (err:any) => { if (err) { res.redirect('/login'); return; diff --git a/src/server/database.ts b/src/server/database.ts index 725b66836..37bc00a85 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -7,6 +7,7 @@ import { DocumentsCollection, IDatabase } from './IDatabase'; import { MemoryDatabase } from './MemoryDatabase'; import { Transferable } from './Message'; import { Upload } from './SharedMediaTypes'; +import { ObjectId } from 'mongodb'; export namespace Database { @@ -26,7 +27,7 @@ export namespace Database { export async function tryInitializeConnection() { try { const { connection } = mongoose; - disconnect = async () => new Promise<any>(resolve => connection.close(resolve)); + disconnect = async () => new Promise<any>(resolve => connection.close().then(resolve)); if (connection.readyState === ConnectionStates.disconnected) { await new Promise<void>((resolve, reject) => { connection.on('error', reject); @@ -35,8 +36,7 @@ export namespace Database { resolve(); }); mongoose.connect(url, { - useNewUrlParser: true, - useUnifiedTopology: true, + //useNewUrlParser: true, dbName: schema, // reconnectTries: Number.MAX_VALUE, // reconnectInterval: 1000, @@ -60,11 +60,10 @@ export namespace Database { async doConnect() { console.error(`\nConnecting to Mongo with URL : ${url}\n`); return new Promise<void>(resolve => { - this.MongoClient.connect(url, { connectTimeoutMS: 30000, socketTimeoutMS: 30000, useUnifiedTopology: true }, (_err, client) => { + this.MongoClient.connect(url, { connectTimeoutMS: 30000, socketTimeoutMS: 30000, }).then(client => { console.error("mongo connect response\n"); if (!client) { console.error("\nMongo connect failed with the error:\n"); - console.log(_err); process.exit(0); } this.db = client.db(); @@ -74,20 +73,19 @@ export namespace Database { }); } - public async update(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, upsert = true, collectionName = DocumentsCollection) { + public async update(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateResult) => void, upsert = true, collectionName = DocumentsCollection) { if (this.db) { const collection = this.db.collection(collectionName); const prom = this.currentWrites[id]; let newProm: Promise<void>; const run = (): Promise<void> => { return new Promise<void>(resolve => { - collection.updateOne({ _id: id }, value, { upsert } - , (err, res) => { + collection.updateOne({ _id: new ObjectId(id) }, value, { upsert }).then(res => { if (this.currentWrites[id] === newProm) { delete this.currentWrites[id]; } resolve(); - callback(err, res); + callback(undefined as any, res); }); }); }; @@ -99,20 +97,19 @@ export namespace Database { } } - public replace(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateWriteOpResult) => void, upsert = true, collectionName = DocumentsCollection) { + public replace(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateResult<mongodb.Document>) => void, upsert = true, collectionName = DocumentsCollection) { if (this.db) { const collection = this.db.collection(collectionName); const prom = this.currentWrites[id]; let newProm: Promise<void>; const run = (): Promise<void> => { return new Promise<void>(resolve => { - collection.replaceOne({ _id: id }, value, { upsert } - , (err, res) => { + collection.replaceOne({ _id: new ObjectId(id)}, value, { upsert }).then( res => { if (this.currentWrites[id] === newProm) { delete this.currentWrites[id]; } resolve(); - callback(err, res); + callback(undefined as any, res as any); }); }); }; @@ -135,15 +132,15 @@ export namespace Database { return collectionNames; } - public delete(query: any, collectionName?: string): Promise<mongodb.DeleteWriteOpResultObject>; - public delete(id: string, collectionName?: string): Promise<mongodb.DeleteWriteOpResultObject>; + public delete(query: any, collectionName?: string): Promise<mongodb.DeleteResult>; + public delete(id: string, collectionName?: string): Promise<mongodb.DeleteResult>; public delete(id: any, collectionName = DocumentsCollection) { if (typeof id === "string") { - id = { _id: id }; + id = { _id: new ObjectId(id) }; } if (this.db) { const db = this.db; - return new Promise(res => db.collection(collectionName).deleteMany(id, (err, result) => res(result))); + return new Promise(res => db.collection(collectionName).deleteMany(id).then(result => res(result))); } else { return new Promise(res => this.onConnect.push(() => res(this.delete(id, collectionName)))); } @@ -180,7 +177,7 @@ export namespace Database { let newProm: Promise<void>; const run = (): Promise<void> => { return new Promise<void>(resolve => { - collection.insertOne(value, (err, res) => { + collection.insertOne(value).then(res => { if (this.currentWrites[id] === newProm) { delete this.currentWrites[id]; } @@ -198,11 +195,11 @@ export namespace Database { public getDocument(id: string, fn: (result?: Transferable) => void, collectionName = DocumentsCollection) { if (this.db) { - this.db.collection(collectionName).findOne({ _id: id }, (err, result) => { + this.db.collection(collectionName).findOne({ _id: new ObjectId(id) }).then(result => { if (result) { result.id = result._id; - delete result._id; - fn(result); + //delete result._id; + fn(result.id); } else { fn(undefined); } @@ -214,12 +211,12 @@ export namespace Database { public getDocuments(ids: string[], fn: (result: Transferable[]) => void, collectionName = DocumentsCollection) { if (this.db) { - this.db.collection(collectionName).find({ _id: { "$in": ids } }).toArray((err, docs) => { - if (err) { - console.log(err.message); - console.log(err.errmsg); - } - fn(docs.map(doc => { + this.db.collection(collectionName).find({ _id: { "$in": ids.map(id => new ObjectId(id)) } }).map(docs => { + // if (err) { + // console.log(err.message); + // console.log(err.errmsg); + // } + fn(docs.map((doc:any) => { doc.id = doc._id; delete doc._id; return doc; @@ -257,15 +254,15 @@ export namespace Database { } } - public query(query: { [key: string]: any }, projection?: { [key: string]: 0 | 1 }, collectionName = DocumentsCollection): Promise<mongodb.Cursor> { + public query(query: { [key: string]: any }, projection?: { [key: string]: 0 | 1 }, collectionName = DocumentsCollection): Promise<mongodb.FindCursor> { if (this.db) { let cursor = this.db.collection(collectionName).find(query); if (projection) { cursor = cursor.project(projection); } - return Promise.resolve<mongodb.Cursor>(cursor); + return Promise.resolve<mongodb.FindCursor>(cursor); } else { - return new Promise<mongodb.Cursor>(res => { + return new Promise<mongodb.FindCursor>(res => { this.onConnect.push(() => res(this.query(query, projection, collectionName))); }); } @@ -274,9 +271,9 @@ export namespace Database { public updateMany(query: any, update: any, collectionName = DocumentsCollection) { if (this.db) { const db = this.db; - return new Promise<mongodb.WriteOpResult>(res => db.collection(collectionName).update(query, update, (_, result) => res(result))); + return new Promise<mongodb.UpdateResult>(res => db.collection(collectionName).updateMany(query, update).then(result => res(result))); } else { - return new Promise<mongodb.WriteOpResult>(res => { + return new Promise<mongodb.UpdateResult>(res => { this.onConnect.push(() => this.updateMany(query, update, collectionName).then(res)); }); } @@ -322,7 +319,7 @@ export namespace Database { const cursor = await Instance.query(query, undefined, collection); const results = await cursor.toArray(); const slice = results.slice(0, Math.min(cap, results.length)); - return removeId ? slice.map(result => { + return removeId ? slice.map((result:any) => { delete result._id; return result; }) : slice; diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index ccb709453..af8b8dfdd 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -22,12 +22,11 @@ import { Database } from './database'; import RouteManager from './RouteManager'; import RouteSubscriber from './RouteSubscriber'; import { WebSocket } from './websocket'; -import brotli = require('brotli'); -import expressFlash = require('express-flash'); -import flash = require('connect-flash'); -const MongoStore = require('connect-mongo')(session); -const config = require('../../webpack.config'); -const compiler = webpack(config); +import * as brotli from 'brotli'; +import * as expressFlash from 'express-flash'; +import * as flash from 'connect-flash'; +import * as MongoStoreConnect from 'connect-mongo'; +import * as config from '../../webpack.config'; /* RouteSetter is a wrapper around the server that prevents the server from being exposed. */ @@ -40,8 +39,7 @@ export let resolvedServerUrl: string; export default async function InitializeServer(routeSetter: RouteSetter) { const isRelease = determineEnvironment(); const app = buildWithMiddleware(express()); - - const compiler = webpack(config); + const compiler = webpack(config as any); app.use( require('webpack-dev-middleware')(compiler, { @@ -89,13 +87,13 @@ function buildWithMiddleware(server: express.Express) { resave: true, cookie: { maxAge: week }, saveUninitialized: true, - store: process.env.DB === 'MEM' ? new session.MemoryStore() : new MongoStore({ url: Database.url }), + store: process.env.DB === 'MEM' ? new session.MemoryStore() : MongoStoreConnect.create({ mongoUrl: Database.url }), }), flash(), expressFlash(), bodyParser.json({ limit: '10mb' }), bodyParser.urlencoded({ extended: true }), - expressValidator(), + expressValidator.body, passport.initialize(), passport.session(), (req: express.Request, res: express.Response, next: express.NextFunction) => { @@ -110,6 +108,7 @@ function buildWithMiddleware(server: express.Express) { next(); }, ].forEach(next => server.use(next)); + return server; } diff --git a/src/server/websocket.ts b/src/server/websocket.ts index be5cdb202..4453001b0 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -3,7 +3,7 @@ import * as express from 'express'; import { createServer, Server } from 'https'; import { networkInterfaces } from 'os'; import * as sio from 'socket.io'; -import { Socket } from 'socket.io'; +import * as _socket from 'socket.io'; import { Opt } from '../fields/Doc'; import { Utils } from '../Utils'; import { logPort } from './ActionUtilities'; @@ -19,6 +19,7 @@ import { Diff, GestureContent, MessageStore, MobileDocumentUploadContent, Mobile import { Search } from './Search'; import { resolvedPorts } from './server_Initialization'; var _ = require('lodash'); +type Socket = typeof _socket; export namespace WebSocket { export let _socket: Socket; @@ -67,7 +68,7 @@ export namespace WebSocket { socket.on('create or join', function (room) { console.log('Received request to create or join room ' + room); - const clientsInRoom = socket.adapter.rooms[room]; + const clientsInRoom = socket.rooms.has(room); const numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0; console.log('Room ' + room + ' now has ' + numClients + ' client(s)'); @@ -90,7 +91,7 @@ export namespace WebSocket { socket.on('ipaddr', function () { const ifaces = networkInterfaces(); for (const dev in ifaces) { - ifaces[dev].forEach(function (details) { + ifaces[dev]?.forEach(function (details) { if (details.family === 'IPv4' && details.address !== '127.0.0.1') { socket.emit('ipaddr', details.address); } diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts index f5189ddf1..884e55fc7 100644 --- a/src/typings/index.d.ts +++ b/src/typings/index.d.ts @@ -13,6 +13,14 @@ declare module 'reveal'; declare module 'react-reveal'; declare module 'react-reveal/makeCarousel'; declare module 'react-resizable-rotatable-draggable'; +declare module 'socket.io'; +declare module '@hig/flyout'; +declare module 'uuid/v4'; +declare module 'uuid/v5'; +declare module 'express-flash'; +declare module 'connect-flash'; +declare module 'connect-mongo'; +declare module '@mui/material'; declare module 'socket.io-parser' { type Encoder = any; type Decoder = any; |