aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts38
-rw-r--r--src/client/DocServer.ts42
-rw-r--r--src/client/Network.ts2
-rw-r--r--src/client/apis/google_docs/GoogleApiClientUtils.ts4
-rw-r--r--src/client/apis/gpt/GPT.ts22
-rw-r--r--src/client/apis/youtube/YoutubeBox.tsx2
-rw-r--r--src/client/documents/Documents.ts19
-rw-r--r--src/client/util/CurrentUserUtils.ts4
-rw-r--r--src/client/util/DragManager.ts34
-rw-r--r--src/client/util/GroupManager.tsx7
-rw-r--r--src/client/util/History.ts2
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx8
-rw-r--r--src/client/util/Import & Export/ImportMetadataEntry.tsx84
-rw-r--r--src/client/util/InteractionUtils.tsx2
-rw-r--r--src/client/util/Scripting.ts3
-rw-r--r--src/client/util/ScrollBox.tsx2
-rw-r--r--src/client/util/SerializationHelper.ts1
-rw-r--r--src/client/util/SharingManager.tsx2
-rw-r--r--src/client/util/reportManager/ReportManager.tsx8
-rw-r--r--src/client/util/reportManager/ReportManagerComponents.tsx2
-rw-r--r--src/client/views/AntimodeMenu.tsx2
-rw-r--r--src/client/views/AudioWaveform.tsx12
-rw-r--r--src/client/views/ComponentDecorations.tsx12
-rw-r--r--src/client/views/ContextMenu.tsx2
-rw-r--r--src/client/views/ContextMenuItem.tsx2
-rw-r--r--src/client/views/DocumentButtonBar.tsx20
-rw-r--r--src/client/views/DocumentDecorations.tsx6
-rw-r--r--src/client/views/EditableView.tsx4
-rw-r--r--src/client/views/FilterPanel.tsx2
-rw-r--r--src/client/views/GestureOverlay.tsx5
-rw-r--r--src/client/views/InkControlPtHandles.tsx2
-rw-r--r--src/client/views/InkTangentHandles.tsx2
-rw-r--r--src/client/views/InkTranscription.tsx700
-rw-r--r--src/client/views/InkingStroke.tsx6
-rw-r--r--src/client/views/KeyphraseQueryView.tsx22
-rw-r--r--src/client/views/MainView.tsx9
-rw-r--r--src/client/views/MarqueeAnnotator.tsx2
-rw-r--r--src/client/views/MetadataEntryMenu.tsx1
-rw-r--r--src/client/views/PropertiesButtons.tsx2
-rw-r--r--src/client/views/PropertiesSection.tsx2
-rw-r--r--src/client/views/PropertiesView.tsx8
-rw-r--r--src/client/views/SidebarAnnos.tsx2
-rw-r--r--src/client/views/StyleProvider.tsx8
-rw-r--r--src/client/views/TemplateMenu.tsx2
-rw-r--r--src/client/views/TouchScrollableMenu.tsx34
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.tsx6
-rw-r--r--src/client/views/collections/CollectionDockingView.scss15
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx2
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx12
-rw-r--r--src/client/views/collections/CollectionMenu.tsx5
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx4
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewColumn.tsx2
-rw-r--r--src/client/views/collections/CollectionPileView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx2
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx6
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx2
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx2
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx2
-rw-r--r--src/client/views/collections/TabDocView.tsx2
-rw-r--r--src/client/views/collections/TreeView.tsx41
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx41
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx11
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx2
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.scss6
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx2
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx2
-rw-r--r--src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx2
-rw-r--r--src/client/views/collections/collectionSchema/SchemaRowBox.tsx2
-rw-r--r--src/client/views/global/globalScripts.ts6
-rw-r--r--src/client/views/linking/LinkMenu.tsx2
-rw-r--r--src/client/views/linking/LinkMenuGroup.tsx2
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx4
-rw-r--r--src/client/views/linking/LinkPopup.tsx2
-rw-r--r--src/client/views/linking/LinkRelationshipSearch.tsx2
-rw-r--r--src/client/views/newlightbox/components/Recommendation/Recommendation.tsx6
-rw-r--r--src/client/views/nodes/AudioBox.tsx4
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx2
-rw-r--r--src/client/views/nodes/ColorBox.tsx7
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx2
-rw-r--r--src/client/views/nodes/DataVizBox/components/PieChart.tsx3
-rw-r--r--src/client/views/nodes/DataVizBox/components/TableBox.tsx3
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx5
-rw-r--r--src/client/views/nodes/DocumentIcon.tsx2
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx6
-rw-r--r--src/client/views/nodes/DocumentView.tsx2
-rw-r--r--src/client/views/nodes/EquationBox.tsx2
-rw-r--r--src/client/views/nodes/FaceRectangle.tsx19
-rw-r--r--src/client/views/nodes/FaceRectangles.tsx22
-rw-r--r--src/client/views/nodes/FieldView.tsx2
-rw-r--r--src/client/views/nodes/ImageBox.tsx4
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx2
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx4
-rw-r--r--src/client/views/nodes/LabelBox.tsx17
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx12
-rw-r--r--src/client/views/nodes/LinkBox.tsx2
-rw-r--r--src/client/views/nodes/LinkDescriptionPopup.tsx2
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx4
-rw-r--r--src/client/views/nodes/MapBox/MapAnchorMenu.tsx2
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx11
-rw-r--r--src/client/views/nodes/MapBox/MapBox2.tsx1194
-rw-r--r--src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx184
-rw-r--r--src/client/views/nodes/MapBox/MapPushpinBox.tsx2
-rw-r--r--src/client/views/nodes/PDFBox.tsx2
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx6
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx2
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx49
-rw-r--r--src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx2
-rw-r--r--src/client/views/nodes/RadialMenu.tsx82
-rw-r--r--src/client/views/nodes/RadialMenuItem.tsx71
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx2
-rw-r--r--src/client/views/nodes/ScriptingBox.scss15
-rw-r--r--src/client/views/nodes/TaskCompletedBox.tsx35
-rw-r--r--src/client/views/nodes/VideoBox.tsx2
-rw-r--r--src/client/views/nodes/WebBox.tsx2
-rw-r--r--src/client/views/nodes/formattedText/DashDocCommentView.tsx2
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx2
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx4
-rw-r--r--src/client/views/nodes/formattedText/EquationEditor.tsx87
-rw-r--r--src/client/views/nodes/formattedText/EquationView.tsx6
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx35
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx4
-rw-r--r--src/client/views/nodes/formattedText/SummaryView.tsx2
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts2
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts2
-rw-r--r--src/client/views/nodes/generativeFill/GenerativeFill.tsx8
-rw-r--r--src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx2
-rw-r--r--src/client/views/nodes/importBox/ImportElementBox.tsx2
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx4
-rw-r--r--src/client/views/nodes/trails/PresElementBox.tsx4
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx13
-rw-r--r--src/client/views/pdf/Annotation.tsx2
-rw-r--r--src/client/views/pdf/GPTPopup/GPTPopup.tsx27
-rw-r--r--src/client/views/pdf/PDFViewer.tsx2
-rw-r--r--src/client/views/search/IconButton.tsx68
-rw-r--r--src/client/views/search/NaviconButton.scss8
-rw-r--r--src/client/views/search/SearchBox.tsx2
-rw-r--r--src/client/views/selectedDoc/SelectedDocView.tsx2
-rw-r--r--src/client/views/webcam/DashWebRTCVideo.tsx2
-rw-r--r--src/debug/Test.tsx18
-rw-r--r--src/fields/Doc.ts19
-rw-r--r--src/fields/Schema.ts1
-rw-r--r--src/fields/ScriptField.ts6
-rw-r--r--src/mobile/ImageUpload.tsx98
-rw-r--r--src/mobile/MobileInkOverlay.tsx2
-rw-r--r--src/server/ActionUtilities.ts10
-rw-r--r--src/server/ApiManagers/DeleteManager.ts36
-rw-r--r--src/server/ApiManagers/UploadManager.ts34
-rw-r--r--src/server/ApiManagers/UserManager.ts25
-rw-r--r--src/server/DashSession/DashSessionAgent.ts136
-rw-r--r--src/server/DashSession/Session/agents/applied_session_agent.ts9
-rw-r--r--src/server/DashSession/Session/agents/monitor.ts156
-rw-r--r--src/server/DashSession/Session/agents/promisified_ipc_manager.ts96
-rw-r--r--src/server/DashSession/Session/agents/server_worker.ts4
-rw-r--r--src/server/DashUploadUtils.ts70
-rw-r--r--src/server/GarbageCollector.ts4
-rw-r--r--src/server/IDatabase.ts12
-rw-r--r--src/server/MemoryDatabase.ts14
-rw-r--r--src/server/ProcessFactory.ts6
-rw-r--r--src/server/RouteManager.ts3
-rw-r--r--src/server/apis/google/GoogleApiServerUtils.ts71
-rw-r--r--src/server/authentication/AuthenticationManager.ts146
-rw-r--r--src/server/authentication/DashUserModel.ts7
-rw-r--r--src/server/authentication/Passport.ts35
-rw-r--r--src/server/database.ts175
-rw-r--r--src/server/index.ts4
-rw-r--r--src/server/server_Initialization.ts45
-rw-r--r--src/server/websocket.ts38
-rw-r--r--src/typings/index.d.ts8
177 files changed, 2400 insertions, 2349 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 4e4414a93..26514622c 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -1,12 +1,12 @@
-import v4 = require('uuid/v4');
-import v5 = require('uuid/v5');
-import { ColorState } from 'react-color';
+import * as uuid from 'uuid';
+import { ColorResult } from 'react-color';
+//import { Socket } from '../node_modules/socket.io-client';
+import { Socket } from '../node_modules/socket.io/dist/index';
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';
-import Color = require('color');
+import * as Color from 'color';
export namespace Utils {
export let CLICK_TIME = 300;
@@ -48,11 +48,19 @@ export namespace Utils {
}
export function GenerateGuid(): string {
- return v4();
+ return uuid.v4();
}
export function GenerateDeterministicGuid(seed: string): string {
- return v5(seed, v5.URL);
+ return uuid.v5(seed, uuid.v5.URL);
+ }
+
+ export function GenerateMongoId(id: string): string {
+ return id.length !== 36 ? Utils.GenerateDeterministicGuid(id) : id;
+ }
+
+ export function GuestID() {
+ return '__guest__';
}
/**
@@ -139,7 +147,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 +406,14 @@ export namespace Utils {
};
}
- export function Emit<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T) {
+ export function Emit<T>(socket: 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: Socket, message: Message<T>, args: T): Promise<any>;
+ export function EmitCallback<T>(socket: Socket, message: Message<T>, args: T, fn: (args: any) => any): void;
+ export function EmitCallback<T>(socket: 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,7 +422,7 @@ export namespace Utils {
}
}
- export function AddServerHandler<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, handler: (args: T) => any) {
+ export function AddServerHandler<T>(socket: Socket, message: Message<T>, handler: (args: T) => any) {
socket.on(message.Message, loggingCallback('Incoming', handler, message.Name));
}
@@ -425,10 +433,10 @@ export namespace Utils {
});
}
export type RoomHandler = (socket: Socket, room: string) => any;
- export type UsedSockets = Socket | SocketIOClient.Socket;
+ export type UsedSockets = Socket;
export type RoomMessage = 'create or join' | 'created' | 'joined';
export function AddRoomHandler(socket: Socket, message: RoomMessage, handler: RoomHandler) {
- socket.on(message, room => handler(socket, room));
+ socket.on(message, (room: any) => handler(socket, room));
}
}
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 353e11775..df92fe50e 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -7,6 +7,7 @@ import { HandleUpdate, Id, Parent } from '../fields/FieldSymbols';
import { ObjectField } from '../fields/ObjectField';
import { RefField } from '../fields/RefField';
import { DocCast, StrCast } from '../fields/Types';
+import { Socket } from '../../node_modules/socket.io/dist/index';
//import MobileInkOverlay from '../mobile/MobileInkOverlay';
import { emptyFunction, Utils } from '../Utils';
import { GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, YoutubeQueryTypes } from './../server/Message';
@@ -76,7 +77,7 @@ export namespace DocServer {
json: true,
});
}
- export let _socket: SocketIOClient.Socket;
+ export let _socket: Socket;
// this client's distinct GUID created at initialization
let USER_ID: string;
// indicates whether or not a document is currently being udpated, and, if so, its id
@@ -189,20 +190,20 @@ export namespace DocServer {
Utils.AddServerHandler(_socket, MessageStore.DeleteFields, respondToDelete);
Utils.AddServerHandler(_socket, MessageStore.ConnectionTerminated, alertUser);
- // mobile ink overlay socket events to communicate between mobile view and desktop view
- _socket.addEventListener('receiveGesturePoints', (content: GestureContent) => {
- // MobileInkOverlay.Instance.drawStroke(content);
- });
- _socket.addEventListener('receiveOverlayTrigger', (content: MobileInkOverlayContent) => {
- //GestureOverlay.Instance.enableMobileInkOverlay(content);
- // MobileInkOverlay.Instance.initMobileInkOverlay(content);
- });
- _socket.addEventListener('receiveUpdateOverlayPosition', (content: UpdateMobileInkOverlayPositionContent) => {
- // MobileInkOverlay.Instance.updatePosition(content);
- });
- _socket.addEventListener('receiveMobileDocumentUpload', (content: MobileDocumentUploadContent) => {
- // MobileInkOverlay.Instance.uploadDocument(content);
- });
+ // // mobile ink overlay socket events to communicate between mobile view and desktop view
+ // _socket.addEventListener('receiveGesturePoints', (content: GestureContent) => {
+ // // MobileInkOverlay.Instance.drawStroke(content);
+ // });
+ // _socket.addEventListener('receiveOverlayTrigger', (content: MobileInkOverlayContent) => {
+ // //GestureOverlay.Instance.enableMobileInkOverlay(content);
+ // // MobileInkOverlay.Instance.initMobileInkOverlay(content);
+ // });
+ // _socket.addEventListener('receiveUpdateOverlayPosition', (content: UpdateMobileInkOverlayPositionContent) => {
+ // // MobileInkOverlay.Instance.updatePosition(content);
+ // });
+ // _socket.addEventListener('receiveMobileDocumentUpload', (content: MobileDocumentUploadContent) => {
+ // // MobileInkOverlay.Instance.uploadDocument(content);
+ // });
}
function errorFunc(): never {
@@ -470,10 +471,13 @@ export namespace DocServer {
// to their actual RefField | undefined values. This return value either becomes the input
// argument to the caller's promise (i.e. GetRefFields(["_id1_", "_id2_", "_id3_"]).then(map => //do something with map...))
// or it is the direct return result if the promise is awaited (i.e. let fields = await GetRefFields(["_id1_", "_id2_", "_id3_"])).
- return ids.reduce((map, id) => {
- map[id] = _cache[id] as any;
- return map;
- }, {} as { [id: string]: Opt<RefField> });
+ return ids.reduce(
+ (map, id) => {
+ map[id] = _cache[id] as any;
+ return map;
+ },
+ {} as { [id: string]: Opt<RefField> }
+ );
};
let _GetRefFields: (ids: string[]) => Promise<{ [id: string]: Opt<RefField> }> = errorFunc;
diff --git a/src/client/Network.ts b/src/client/Network.ts
index 89b31fdca..cdcd5225a 100644
--- a/src/client/Network.ts
+++ b/src/client/Network.ts
@@ -1,5 +1,5 @@
import { Utils } from '../Utils';
-import requestPromise = require('request-promise');
+import * as requestPromise from 'request-promise';
import { Upload } from '../server/SharedMediaTypes';
/**
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/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
index 2da9927c0..ceb80acbc 100644
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -10,7 +10,7 @@ import { DocumentView } from '../../views/nodes/DocumentView';
import { FieldView, FieldViewProps } from '../../views/nodes/FieldView';
import '../../views/nodes/WebBox.scss';
import './YoutubeBox.scss';
-import React = require('react');
+import * as React from 'react';
interface VideoTemplate {
thumbnailUrl: string;
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 929660ff4..4ec100728 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -30,7 +30,6 @@ import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMu
import { CollectionView } from '../views/collections/CollectionView';
import { ContextMenu } from '../views/ContextMenu';
import { ContextMenuProps } from '../views/ContextMenuItem';
-import { DFLT_IMAGE_NATIVE_DIM } from '../views/global/globalCssVariables.scss';
import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke } from '../views/InkingStroke';
import { AudioBox, media_state } from '../views/nodes/AudioBox';
import { ColorBox } from '../views/nodes/ColorBox';
@@ -61,6 +60,8 @@ import { VideoBox } from '../views/nodes/VideoBox';
import { WebBox } from '../views/nodes/WebBox';
import { SearchBox } from '../views/search/SearchBox';
import { CollectionViewType, DocumentType } from './DocumentTypes';
+//import { DFLT_IMAGE_NATIVE_DIM } from '../views/global/globalCssVariables.scss';
+const DFLT_IMAGE_NATIVE_DIM = '900px';
const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', ''));
class EmptyBox {
@@ -1299,7 +1300,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 +1910,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 +1919,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 +1939,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 +1952,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/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index d3ed4ca7c..8b5f23aa4 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -561,7 +561,7 @@ export class CurrentUserUtils {
toolTip: "Empty recently closed",};
DocUtils.AssignDocField(recentlyClosed, "layout_headerButton", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("this.target")});
- if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script.script.originalScript === clearAll("self"))) {
+ if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script?.script.originalScript === clearAll("self"))) {
recentlyClosed.contextMenuScripts = new List<ScriptField>([ScriptField.MakeScript(clearAll("self"))!])
}
return recentlyClosed;
@@ -946,7 +946,7 @@ export class CurrentUserUtils {
runInAction(() => CurrentUserUtils.ServerVersion = result.version);
Doc.CurrentUserEmail = result.email;
resolvedPorts = result.resolvedPorts as any;
- DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, result.email);
+ DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts?.socket, result.email);
if (result.cacheDocumentIds)
{
const ids = result.cacheDocumentIds.split(";");
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 162a0a11f..427b1c85f 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -7,13 +7,14 @@ import { ScriptField } from '../../fields/ScriptField';
import { ScriptCast, StrCast } from '../../fields/Types';
import { emptyFunction, Utils } from '../../Utils';
import { Docs, DocUtils } from '../documents/Documents';
-import * as globalCssVariables from '../views/global/globalCssVariables.scss';
import { CollectionFreeFormDocumentView } from '../views/nodes/CollectionFreeFormDocumentView';
import { DocumentView } from '../views/nodes/DocumentView';
import { ScriptingGlobals } from './ScriptingGlobals';
import { SelectionManager } from './SelectionManager';
import { SnappingManager } from './SnappingManager';
import { UndoManager } from './UndoManager';
+// import * as globalCssVariables from '../views/global/globalCssVariables.scss';
+const contextMenuZindex = 100002;
export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'inSame' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
@@ -85,7 +86,16 @@ export namespace DragManager {
// event called when the drag operation results in a drop action
export class DropEvent {
- constructor(readonly x: number, readonly y: number, readonly complete: DragCompleteEvent, readonly shiftKey: boolean, readonly altKey: boolean, readonly metaKey: boolean, readonly ctrlKey: boolean, readonly embedKey: boolean) {}
+ constructor(
+ readonly x: number,
+ readonly y: number,
+ readonly complete: DragCompleteEvent,
+ readonly shiftKey: boolean,
+ readonly altKey: boolean,
+ readonly metaKey: boolean,
+ readonly ctrlKey: boolean,
+ readonly embedKey: boolean
+ ) {}
}
// event called when the drag operation has completed (aborted or completed a drop) -- this will be after any drop event has been generated
@@ -209,16 +219,14 @@ export namespace DragManager {
!dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart)
? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result)
: docDragData.dropAction === 'embed'
- ? Doc.BestEmbedding(d)
- : docDragData.dropAction === 'add'
- ? d
- : docDragData.dropAction === 'proto'
- ? Doc.GetProto(d)
- : docDragData.dropAction === 'copy'
- ? (
- await Doc.MakeClone(d)
- ).clone
- : d
+ ? Doc.BestEmbedding(d)
+ : docDragData.dropAction === 'add'
+ ? d
+ : docDragData.dropAction === 'proto'
+ ? Doc.GetProto(d)
+ : docDragData.dropAction === 'copy'
+ ? (await Doc.MakeClone(d)).clone
+ : d
)
)
).filter(d => d);
@@ -428,7 +436,7 @@ export namespace DragManager {
color: 'black',
transition: 'none',
borderRadius: getComputedStyle(ele).borderRadius,
- zIndex: globalCssVariables.contextMenuZindex,
+ zIndex: contextMenuZindex,
transformOrigin: '0 0',
width,
height,
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 8973306bf..c8c93b7d0 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -52,7 +52,7 @@ export class GroupManager extends React.Component<{}> {
* Fetches the list of users stored on the database.
*/
populateUsers = async () => {
- if (Doc.UserDoc()[Id] !== '__guest__') {
+ if (Doc.UserDoc()[Id] !== Utils.GuestID()) {
const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
const raw = JSON.parse(userList) as User[];
raw.map(action(user => !this.users.some(umail => umail === user.email) && this.users.push(user.email)));
@@ -258,10 +258,7 @@ export class GroupManager extends React.Component<{}> {
alert('Please select a unique group name');
return;
}
- this.createGroupDoc(
- value,
- this.selectedUsers?.map(user => user.value)
- );
+ this.createGroupDoc(value, this.selectedUsers?.map(user => user.value));
this.selectedUsers = null;
this.inputRef.current!.value = '';
this.buttonColour = '#979797';
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 18aee6444..2f1a336cc 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -85,7 +85,7 @@ export namespace HistoryUtil {
};
function addParser(type: string, requiredFields: Parser, optionalFields: Parser, customParser?: (pathname: string[], opts: qs.ParsedQuery, current: ParsedUrl) => ParsedUrl | null | undefined) {
- function parse(parser: ParserValue, value: string | string[] | null | undefined) {
+ function parse(parser: ParserValue, value: string | (string | null)[] | null | undefined) {
if (value === undefined || value === null) {
return value;
}
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index c45143f43..30e448797 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -20,8 +20,8 @@ import { FieldView, FieldViewProps } from '../../views/nodes/FieldView';
import { DocumentManager } from '../DocumentManager';
import './DirectoryImportBox.scss';
import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from './ImportMetadataEntry';
-import React = require('react');
-import _ = require('lodash');
+import * as React from 'react';
+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/Import & Export/ImportMetadataEntry.tsx b/src/client/util/Import & Export/ImportMetadataEntry.tsx
index 45d8c0c63..58a09b9c9 100644
--- a/src/client/util/Import & Export/ImportMetadataEntry.tsx
+++ b/src/client/util/Import & Export/ImportMetadataEntry.tsx
@@ -1,10 +1,10 @@
-import React = require("react");
-import { observer } from "mobx-react";
-import { EditableView } from "../../views/EditableView";
-import { action, computed } from "mobx";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Doc } from "../../../fields/Doc";
-import { StrCast, BoolCast } from "../../../fields/Types";
+import * as React from 'react';
+import { observer } from 'mobx-react';
+import { EditableView } from '../../views/EditableView';
+import { action, computed } from 'mobx';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Doc } from '../../../fields/Doc';
+import { StrCast, BoolCast } from '../../../fields/Types';
interface KeyValueProps {
Document: Doc;
@@ -12,19 +12,18 @@ interface KeyValueProps {
next: () => void;
}
-export const keyPlaceholder = "Key";
-export const valuePlaceholder = "Value";
+export const keyPlaceholder = 'Key';
+export const valuePlaceholder = 'Value';
@observer
export default class ImportMetadataEntry extends React.Component<KeyValueProps> {
-
private keyRef = React.createRef<EditableView>();
private valueRef = React.createRef<EditableView>();
private checkRef = React.createRef<HTMLInputElement>();
@computed
public get valid() {
- return (this.key.length > 0 && this.key !== keyPlaceholder) && (this.value.length > 0 && this.value !== valuePlaceholder);
+ return this.key.length > 0 && this.key !== keyPlaceholder && this.value.length > 0 && this.value !== valuePlaceholder;
}
@computed
@@ -66,7 +65,7 @@ export default class ImportMetadataEntry extends React.Component<KeyValueProps>
this.valueRef.current && this.valueRef.current.setIsFocused(true);
this.key.length === 0 && (this.key = keyPlaceholder);
return true;
- }
+ };
@action
updateValue = (newValue: string, shiftDown: boolean) => {
@@ -75,68 +74,45 @@ export default class ImportMetadataEntry extends React.Component<KeyValueProps>
this.value.length > 0 && shiftDown && this.props.next();
this.value.length === 0 && (this.value = valuePlaceholder);
return true;
- }
+ };
render() {
const keyValueStyle: React.CSSProperties = {
paddingLeft: 10,
- width: "50%",
+ width: '50%',
opacity: this.valid ? 1 : 0.5,
};
return (
<div
style={{
- display: "flex",
- flexDirection: "row",
+ display: 'flex',
+ flexDirection: 'row',
paddingBottom: 5,
paddingRight: 5,
- justifyContent: "center",
- 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}
- />
+ justifyContent: 'center',
+ 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} />
</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={true} />
</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,
- transform: "rotate(45deg)"
+ transform: 'rotate(45deg)',
}}
/>
</div>
</div>
);
}
-
-} \ No newline at end of file
+}
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index be885312d..a2f5826fe 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { GestureUtils } from '../../pen-gestures/GestureUtils';
import { Utils } from '../../Utils';
import './InteractionUtils.scss';
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 400b63a1c..f0a5f9ed9 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -120,6 +120,7 @@ class ScriptingCompilerHost {
}
return undefined;
}
+
// getDefaultLibFileName(options: ts.CompilerOptions): string {
getDefaultLibFileName(options: any): string {
return 'node_modules/typescript/lib/lib.d.ts'; // No idea what this means...
@@ -247,7 +248,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
const funcScript = `(function(${paramString})${reqTypes} { ${body} })`;
host.writeFile('file.ts', funcScript);
- if (typecheck) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
+ if (typecheck && false) host.writeFile('node_modules/typescript/lib/lib.d.ts', typescriptlib);
const program = ts.createProgram(['file.ts'], {}, host);
const testResult = program.emit();
const outputText = host.readFile('file.js');
diff --git a/src/client/util/ScrollBox.tsx b/src/client/util/ScrollBox.tsx
index d4620ae3f..785526ab3 100644
--- a/src/client/util/ScrollBox.tsx
+++ b/src/client/util/ScrollBox.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
export class ScrollBox extends React.Component<React.PropsWithChildren<{}>> {
onWheel = (e: React.WheelEvent) => {
diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts
index 76037a7e9..8daa69890 100644
--- a/src/client/util/SerializationHelper.ts
+++ b/src/client/util/SerializationHelper.ts
@@ -1,6 +1,5 @@
import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema } from 'serializr';
import { Field } from '../../fields/Doc';
-import { ClientUtils } from './ClientUtils';
let serializing = 0;
export function afterDocDeserialize(cb: (err: any, val: any) => void, err: any, newValue: any) {
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 34e294a4a..403f4e090 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -133,7 +133,7 @@ export class SharingManager extends React.Component<{}> {
* Populates the list of validated users (this.users) by adding registered users which have a sharingDocument.
*/
populateUsers = async () => {
- if (!this.populating && Doc.UserDoc()[Id] !== '__guest__') {
+ if (!this.populating && Doc.UserDoc()[Id] !== Utils.GuestID()) {
this.populating = true;
const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
const raw = (JSON.parse(userList) as User[]).filter(user => user.email !== 'guest' && user.email !== Doc.CurrentUserEmail);
diff --git a/src/client/util/reportManager/ReportManager.tsx b/src/client/util/reportManager/ReportManager.tsx
index b25d51b41..738902a31 100644
--- a/src/client/util/reportManager/ReportManager.tsx
+++ b/src/client/util/reportManager/ReportManager.tsx
@@ -1,13 +1,11 @@
import * as React from 'react';
-import v4 = require('uuid/v4');
+import * as uuid from 'uuid';
import '.././SettingsManager.scss';
import './ReportManager.scss';
-import Dropzone from 'react-dropzone';
import ReactLoading from 'react-loading';
import { action, observable } from 'mobx';
import { BsX, BsArrowsAngleExpand, BsArrowsAngleContract } from 'react-icons/bs';
import { CgClose } from 'react-icons/cg';
-import { AiOutlineUpload } from 'react-icons/ai';
import { HiOutlineArrowLeft } from 'react-icons/hi';
import { Issue } from './reportManagerSchema';
import { observer } from 'mobx-react';
@@ -156,7 +154,7 @@ export class ReportManager extends React.Component<{}> {
* @param files uploaded files
*/
private onDrop = (files: File[]) => {
- this.setFormData({ ...this.formData, mediaFiles: [...this.formData.mediaFiles, ...files.map(file => ({ _id: v4(), file }))] });
+ this.setFormData({ ...this.formData, mediaFiles: [...this.formData.mediaFiles, ...files.map(file => ({ _id: uuid.v4(), file }))] });
};
/**
@@ -338,7 +336,7 @@ export class ReportManager extends React.Component<{}> {
multiple
onChange={e => {
if (!e.target.files) return;
- this.setFormData({ ...this.formData, mediaFiles: [...this.formData.mediaFiles, ...Array.from(e.target.files).map(file => ({ _id: v4(), file }))] });
+ this.setFormData({ ...this.formData, mediaFiles: [...this.formData.mediaFiles, ...Array.from(e.target.files).map(file => ({ _id: uuid.v4(), file }))] });
}}
/>
{this.formData.mediaFiles.length > 0 && <ul className="file-list">{this.formData.mediaFiles.map(file => this.getMediaPreview(file))}</ul>}
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/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index 16e76694d..412b8ba6e 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { observable, action, runInAction } from 'mobx';
import './AntimodeMenu.scss';
import { StrCast } from '../../fields/Types';
diff --git a/src/client/views/AudioWaveform.tsx b/src/client/views/AudioWaveform.tsx
index c779ce8c4..1fd2fedc3 100644
--- a/src/client/views/AudioWaveform.tsx
+++ b/src/client/views/AudioWaveform.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import axios from 'axios';
import { action, computed, IReactionDisposer, reaction } from 'mobx';
import { observer } from 'mobx-react';
@@ -102,7 +102,15 @@ export class AudioWaveform extends React.Component<AudioWaveformProps> {
render() {
return (
<div className="audioWaveform">
- <Waveform color={Colors.MEDIUM_BLUE_ALT} height={this.waveHeight} barWidth={200 / this.audioBuckets.length} pos={this.props.duration} duration={this.props.duration} peaks={Array.from(this.audioBuckets)} progressColor={Colors.MEDIUM_BLUE_ALT} />
+ <Waveform
+ color={Colors.MEDIUM_BLUE_ALT}
+ height={this.waveHeight}
+ barWidth={200 / this.audioBuckets.length}
+ pos={this.props.duration}
+ duration={this.props.duration}
+ peaks={Array.from(this.audioBuckets)}
+ progressColor={Colors.MEDIUM_BLUE_ALT}
+ />
</div>
);
}
diff --git a/src/client/views/ComponentDecorations.tsx b/src/client/views/ComponentDecorations.tsx
index 66d1bd63d..0a34b283b 100644
--- a/src/client/views/ComponentDecorations.tsx
+++ b/src/client/views/ComponentDecorations.tsx
@@ -1,14 +1,14 @@
-import { observer } from "mobx-react";
-import { SelectionManager } from "../util/SelectionManager";
+import { observer } from 'mobx-react';
+import { SelectionManager } from '../util/SelectionManager';
import './ComponentDecorations.scss';
-import React = require("react");
+import * as React from 'react';
@observer
-export class ComponentDecorations extends React.Component<{ boundsTop: number, boundsLeft: number }, { value: string }> {
+export class ComponentDecorations extends React.Component<{ boundsTop: number; boundsLeft: number }, { value: string }> {
static Instance: ComponentDecorations;
render() {
const seldoc = SelectionManager.Views().lastElement();
- return seldoc?.ComponentView?.componentUI?.(this.props.boundsLeft, this.props.boundsTop) ?? (null);
+ return seldoc?.ComponentView?.componentUI?.(this.props.boundsLeft, this.props.boundsTop) ?? null;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index adefc7e9c..57bc11385 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, IReactionDisposer, observable } from 'mobx';
import { observer } from 'mobx-react';
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index c2cbca3e1..6d97d965e 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { observable, action, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 59d7e75da..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';
@@ -27,10 +27,10 @@ import { DashFieldView } from './nodes/formattedText/DashFieldView';
import { GoogleRef } from './nodes/formattedText/FormattedTextBox';
import { PinProps } from './nodes/trails';
import { TemplateMenu } from './TemplateMenu';
-import React = require('react');
-const higflyout = require('@hig/flyout');
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
+import * as React from 'react';
+// 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
@@ -432,12 +432,12 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
.filter(dv => dv)
.map(dv => dv!.props.Document)}
suggestWithFunction
- /> /* tfs: @bcz This might need to be the data document? */
+ /> // tfs: @bcz This might need to be the data document?
}>
<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 b9fcf5360..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';
@@ -33,8 +33,8 @@ import { LightboxView } from './LightboxView';
import { DocumentView, OpenWhereMod } from './nodes/DocumentView';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import { ImageBox } from './nodes/ImageBox';
-import React = require('react');
-import _ = require('lodash');
+import * as React from 'react';
+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/EditableView.tsx b/src/client/views/EditableView.tsx
index abb7ed7ee..6bc0e5424 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { action, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as Autosuggest from 'react-autosuggest';
@@ -222,12 +222,14 @@ export class EditableView extends React.Component<EditableProps> {
className: 'editableView-input',
onKeyDown: this.onKeyDown,
autoFocus: true,
+ // @ts-ignore
onBlur: e => this.finalizeEdit(e.currentTarget.value, false, true, false),
onPointerDown: this.stopPropagation,
onClick: this.stopPropagation,
onPointerUp: this.stopPropagation,
onKeyPress: this.stopPropagation,
value: this.props.autosuggestProps.value,
+ // @ts-ignore
onChange: this.props.autosuggestProps.onChange,
}}
/>
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 1deca401d..a3c4350d9 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { action, computed, observable, ObservableMap } from 'mobx';
import { observer } from 'mobx-react';
import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider';
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 559f245d9..214da5541 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import * as fitCurve from 'fit-curve';
import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
@@ -14,7 +14,6 @@ import { InteractionUtils } from '../util/InteractionUtils';
import { ScriptingGlobals } from '../util/ScriptingGlobals';
import { Transform } from '../util/Transform';
import './GestureOverlay.scss';
-import { InkTranscription } from './InkTranscription';
import {
ActiveArrowEnd,
ActiveArrowScale,
@@ -178,7 +177,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/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx
index e5141b7f4..b835a3798 100644
--- a/src/client/views/InkControlPtHandles.tsx
+++ b/src/client/views/InkControlPtHandles.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import { Doc } from '../../fields/Doc';
diff --git a/src/client/views/InkTangentHandles.tsx b/src/client/views/InkTangentHandles.tsx
index 65f6a6dfa..a00c26a07 100644
--- a/src/client/views/InkTangentHandles.tsx
+++ b/src/client/views/InkTangentHandles.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { action } from 'mobx';
import { observer } from 'mobx-react';
import { Doc } from '../../fields/Doc';
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 17136a737..b2ac208ca 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -1,350 +1,350 @@
-import * as iink from 'iink-js';
-import { action, observable } from 'mobx';
-import * as React from 'react';
-import { Doc, DocListCast } from '../../fields/Doc';
-import { InkData, InkField, InkTool } from '../../fields/InkField';
-import { Cast, DateCast, NumCast } from '../../fields/Types';
-import { aggregateBounds } from '../../Utils';
-import { DocumentType } from '../documents/DocumentTypes';
-import { DocumentManager } from '../util/DocumentManager';
-import { CollectionFreeFormView } from './collections/collectionFreeForm';
-import { InkingStroke } from './InkingStroke';
-import './InkTranscription.scss';
-
-/**
- * Class component that handles inking in writing mode
- */
-export class InkTranscription extends React.Component {
- static Instance: InkTranscription;
-
- @observable _mathRegister: any;
- @observable _mathRef: any;
- @observable _textRegister: any;
- @observable _textRef: any;
- private lastJiix: any;
- private currGroup?: Doc;
-
- constructor(props: Readonly<{}>) {
- super(props);
-
- InkTranscription.Instance = this;
- }
-
- componentWillUnmount() {
- this._mathRef.removeEventListener('exported', (e: any) => this.exportInk(e, this._mathRef));
- this._textRef.removeEventListener('exported', (e: any) => this.exportInk(e, this._textRef));
- }
-
- @action
- setMathRef = (r: any) => {
- if (!this._mathRegister) {
- this._mathRegister = r
- ? iink.register(r, {
- recognitionParams: {
- type: 'MATH',
- protocol: 'WEBSOCKET',
- server: {
- host: 'cloud.myscript.com',
- applicationKey: process.env.IINKJS_APP,
- hmacKey: process.env.IINKJS_HMAC,
- websocket: {
- pingEnabled: false,
- autoReconnect: true,
- },
- },
- iink: {
- math: {
- mimeTypes: ['application/x-latex', 'application/vnd.myscript.jiix'],
- },
- export: {
- jiix: {
- strokes: true,
- },
- },
- },
- },
- })
- : null;
- }
-
- r?.addEventListener('exported', (e: any) => this.exportInk(e, this._mathRef));
-
- return (this._mathRef = r);
- };
-
- @action
- setTextRef = (r: any) => {
- if (!this._textRegister) {
- this._textRegister = r
- ? iink.register(r, {
- recognitionParams: {
- type: 'TEXT',
- protocol: 'WEBSOCKET',
- server: {
- host: 'cloud.myscript.com',
- applicationKey: '7277ec34-0c2e-4ee1-9757-ccb657e3f89f',
- hmacKey: 'f5cb18f2-1f95-4ddb-96ac-3f7c888dffc1',
- websocket: {
- pingEnabled: false,
- autoReconnect: true,
- },
- },
- iink: {
- text: {
- mimeTypes: ['text/plain'],
- },
- export: {
- jiix: {
- strokes: true,
- },
- },
- },
- },
- })
- : null;
- }
-
- r?.addEventListener('exported', (e: any) => this.exportInk(e, this._textRef));
-
- return (this._textRef = r);
- };
-
- /**
- * Handles processing Dash Doc data for ink transcription.
- *
- * @param groupDoc the group which contains the ink strokes we want to transcribe
- * @param inkDocs the ink docs contained within the selected group
- * @param math boolean whether to do math transcription or not
- */
- transcribeInk = (groupDoc: Doc | undefined, inkDocs: Doc[], math: boolean) => {
- if (!groupDoc) return;
- const validInks = inkDocs.filter(s => s.type === DocumentType.INK);
-
- const strokes: InkData[] = [];
- const times: number[] = [];
- validInks
- .filter(i => Cast(i[Doc.LayoutFieldKey(i)], InkField))
- .forEach(i => {
- const d = Cast(i[Doc.LayoutFieldKey(i)], InkField, null);
- const inkStroke = DocumentManager.Instance.getDocumentView(i)?.ComponentView as InkingStroke;
- strokes.push(d.inkData.map(pd => inkStroke.ptToScreen({ X: pd.X, Y: pd.Y })));
- times.push(DateCast(i.author_date).getDate().getTime());
- });
-
- this.currGroup = groupDoc;
-
- const pointerData = { events: strokes.map((stroke, i) => this.inkJSON(stroke, times[i])) };
- const processGestures = false;
-
- if (math) {
- this._mathRef.editor.pointerEvents(pointerData, processGestures);
- } else {
- this._textRef.editor.pointerEvents(pointerData, processGestures);
- }
- };
-
- /**
- * Converts the Dash Ink Data to JSON.
- *
- * @param stroke The dash ink data
- * @param time the time of the stroke
- * @returns json object representation of ink data
- */
- inkJSON = (stroke: InkData, time: number) => {
- return {
- pointerType: 'PEN',
- pointerId: 1,
- x: stroke.map(point => point.X),
- y: stroke.map(point => point.Y),
- t: new Array(stroke.length).fill(time),
- p: new Array(stroke.length).fill(1.0),
- };
- };
-
- /**
- * Creates subgroups for each word for the whole text transcription
- * @param wordInkDocMap the mapping of words to ink strokes (Ink Docs)
- */
- subgroupsTranscriptions = (wordInkDocMap: Map<string, Doc[]>) => {
- // iterate through the keys of wordInkDocMap
- wordInkDocMap.forEach(async (inkDocs: Doc[], word: string) => {
- const selected = inkDocs.slice();
- if (!selected) {
- return;
- }
- const ctx = await Cast(selected[0].embedContainer, Doc);
- if (!ctx) {
- return;
- }
- const docView: CollectionFreeFormView = DocumentManager.Instance.getDocumentView(ctx)?.ComponentView as CollectionFreeFormView;
-
- if (!docView) return;
- const marqViewRef = docView._marqueeViewRef.current;
- if (!marqViewRef) return;
- this.groupInkDocs(selected, docView, word);
- });
- };
-
- /**
- * Event listener function for when the 'exported' event is heard.
- *
- * @param e the event objects
- * @param ref the ref to the editor
- */
- exportInk = (e: any, ref: any) => {
- const exports = e.detail.exports;
- if (exports) {
- if (exports['application/x-latex']) {
- const latex = exports['application/x-latex'];
- if (this.currGroup) {
- this.currGroup.text = latex;
- this.currGroup.title = latex;
- }
-
- ref.editor.clear();
- } else if (exports['text/plain']) {
- if (exports['application/vnd.myscript.jiix']) {
- this.lastJiix = JSON.parse(exports['application/vnd.myscript.jiix']);
- // map timestamp to strokes
- const timestampWord = new Map<number, string>();
- this.lastJiix.words.map((word: any) => {
- if (word.items) {
- word.items.forEach((i: { id: string; timestamp: string; X: Array<number>; Y: Array<number>; F: Array<number> }) => {
- const ms = Date.parse(i.timestamp);
- timestampWord.set(ms, word.label);
- });
- }
- });
-
- const wordInkDocMap = new Map<string, Doc[]>();
- if (this.currGroup) {
- const docList = DocListCast(this.currGroup.data);
- docList.forEach((inkDoc: Doc) => {
- // just having the times match up and be a unique value (actual timestamp doesn't matter)
- const ms = DateCast(inkDoc.author_date).getDate().getTime() + 14400000;
- const word = timestampWord.get(ms);
- if (!word) {
- return;
- }
- const entry = wordInkDocMap.get(word);
- if (entry) {
- entry.push(inkDoc);
- wordInkDocMap.set(word, entry);
- } else {
- const newEntry = [inkDoc];
- wordInkDocMap.set(word, newEntry);
- }
- });
- if (this.lastJiix.words.length > 1) this.subgroupsTranscriptions(wordInkDocMap);
- }
- }
- const text = exports['text/plain'];
-
- if (this.currGroup) {
- this.currGroup.transcription = text;
- this.currGroup.title = text.split('\n')[0];
- }
-
- ref.editor.clear();
- }
- }
- };
-
- /**
- * Creates the ink grouping once the user leaves the writing mode.
- */
- createInkGroup() {
- // TODO nda - if document being added to is a inkGrouping then we can just add to that group
- if (Doc.ActiveTool === InkTool.Write) {
- CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
- // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
- const selected = ffView.unprocessedDocs;
- const newCollection = this.groupInkDocs(
- selected.filter(doc => doc.embedContainer),
- ffView
- );
- ffView.unprocessedDocs = [];
-
- InkTranscription.Instance.transcribeInk(newCollection, selected, false);
- });
- }
- CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
- }
-
- /**
- * Creates the groupings for a given list of ink docs on a specific doc view
- * @param selected: the list of ink docs to create a grouping of
- * @param docView: the view in which we want the grouping to be created
- * @param word: optional param if the group we are creating is a word (subgrouping individual words)
- * @returns a new collection Doc or undefined if the grouping fails
- */
- groupInkDocs(selected: Doc[], docView: CollectionFreeFormView, word?: string): Doc | undefined {
- const bounds: { x: number; y: number; width?: number; height?: number }[] = [];
-
- // calculate the necessary bounds from the selected ink docs
- selected.map(
- action(d => {
- const x = NumCast(d.x);
- const y = NumCast(d.y);
- const width = NumCast(d._width);
- const height = NumCast(d._height);
- bounds.push({ x, y, width, height });
- })
- );
-
- // calculate the aggregated bounds
- const aggregBounds = aggregateBounds(bounds, 0, 0);
- const marqViewRef = docView._marqueeViewRef.current;
-
- // set the vals for bounds in marqueeView
- if (marqViewRef) {
- marqViewRef._downX = aggregBounds.x;
- marqViewRef._downY = aggregBounds.y;
- marqViewRef._lastX = aggregBounds.r;
- marqViewRef._lastY = aggregBounds.b;
- }
-
- // map through all the selected ink strokes and create the groupings
- selected.map(
- action(d => {
- const dx = NumCast(d.x);
- const dy = NumCast(d.y);
- delete d.x;
- delete d.y;
- delete d.activeFrame;
- delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
- delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
- // calculate pos based on bounds
- if (marqViewRef?.Bounds) {
- d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
- d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
- }
- return d;
- })
- );
- docView.props.removeDocument?.(selected);
- // Gets a collection based on the selected nodes using a marquee view ref
- const newCollection = marqViewRef?.getCollection(selected, undefined, true);
- if (newCollection) {
- newCollection.width = NumCast(newCollection._width);
- newCollection.height = NumCast(newCollection._height);
- // if the grouping we are creating is an individual word
- if (word) {
- newCollection.title = word;
- }
- }
-
- // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
- newCollection && docView.props.addDocument?.(newCollection);
- return newCollection;
- }
-
- render() {
- return (
- <div className="ink-transcription">
- <div className="math-editor" ref={this.setMathRef} touch-action="none"></div>
- <div className="text-editor" ref={this.setTextRef} touch-action="none"></div>
- </div>
- );
- }
-}
+// import * as iink from 'iink-js';
+// import { action, observable } from 'mobx';
+// import * as React from 'react';
+// import { Doc, DocListCast } from '../../fields/Doc';
+// import { InkData, InkField, InkTool } from '../../fields/InkField';
+// import { Cast, DateCast, NumCast } from '../../fields/Types';
+// import { aggregateBounds } from '../../Utils';
+// import { DocumentType } from '../documents/DocumentTypes';
+// import { DocumentManager } from '../util/DocumentManager';
+// import { CollectionFreeFormView } from './collections/collectionFreeForm';
+// import { InkingStroke } from './InkingStroke';
+// import './InkTranscription.scss';
+
+// /**
+// * Class component that handles inking in writing mode
+// */
+// export class InkTranscription extends React.Component {
+// static Instance: InkTranscription;
+
+// @observable _mathRegister: any;
+// @observable _mathRef: any;
+// @observable _textRegister: any;
+// @observable _textRef: any;
+// private lastJiix: any;
+// private currGroup?: Doc;
+
+// constructor(props: Readonly<{}>) {
+// super(props);
+
+// InkTranscription.Instance = this;
+// }
+
+// componentWillUnmount() {
+// this._mathRef.removeEventListener('exported', (e: any) => this.exportInk(e, this._mathRef));
+// this._textRef.removeEventListener('exported', (e: any) => this.exportInk(e, this._textRef));
+// }
+
+// @action
+// setMathRef = (r: any) => {
+// if (!this._mathRegister) {
+// this._mathRegister = r
+// ? iink.register(r, {
+// recognitionParams: {
+// type: 'MATH',
+// protocol: 'WEBSOCKET',
+// server: {
+// host: 'cloud.myscript.com',
+// applicationKey: process.env.IINKJS_APP,
+// hmacKey: process.env.IINKJS_HMAC,
+// websocket: {
+// pingEnabled: false,
+// autoReconnect: true,
+// },
+// },
+// iink: {
+// math: {
+// mimeTypes: ['application/x-latex', 'application/vnd.myscript.jiix'],
+// },
+// export: {
+// jiix: {
+// strokes: true,
+// },
+// },
+// },
+// },
+// })
+// : null;
+// }
+
+// r?.addEventListener('exported', (e: any) => this.exportInk(e, this._mathRef));
+
+// return (this._mathRef = r);
+// };
+
+// @action
+// setTextRef = (r: any) => {
+// if (!this._textRegister) {
+// this._textRegister = r
+// ? iink.register(r, {
+// recognitionParams: {
+// type: 'TEXT',
+// protocol: 'WEBSOCKET',
+// server: {
+// host: 'cloud.myscript.com',
+// applicationKey: '7277ec34-0c2e-4ee1-9757-ccb657e3f89f',
+// hmacKey: 'f5cb18f2-1f95-4ddb-96ac-3f7c888dffc1',
+// websocket: {
+// pingEnabled: false,
+// autoReconnect: true,
+// },
+// },
+// iink: {
+// text: {
+// mimeTypes: ['text/plain'],
+// },
+// export: {
+// jiix: {
+// strokes: true,
+// },
+// },
+// },
+// },
+// })
+// : null;
+// }
+
+// r?.addEventListener('exported', (e: any) => this.exportInk(e, this._textRef));
+
+// return (this._textRef = r);
+// };
+
+// /**
+// * Handles processing Dash Doc data for ink transcription.
+// *
+// * @param groupDoc the group which contains the ink strokes we want to transcribe
+// * @param inkDocs the ink docs contained within the selected group
+// * @param math boolean whether to do math transcription or not
+// */
+// transcribeInk = (groupDoc: Doc | undefined, inkDocs: Doc[], math: boolean) => {
+// if (!groupDoc) return;
+// const validInks = inkDocs.filter(s => s.type === DocumentType.INK);
+
+// const strokes: InkData[] = [];
+// const times: number[] = [];
+// validInks
+// .filter(i => Cast(i[Doc.LayoutFieldKey(i)], InkField))
+// .forEach(i => {
+// const d = Cast(i[Doc.LayoutFieldKey(i)], InkField, null);
+// const inkStroke = DocumentManager.Instance.getDocumentView(i)?.ComponentView as InkingStroke;
+// strokes.push(d.inkData.map(pd => inkStroke.ptToScreen({ X: pd.X, Y: pd.Y })));
+// times.push(DateCast(i.author_date).getDate().getTime());
+// });
+
+// this.currGroup = groupDoc;
+
+// const pointerData = { events: strokes.map((stroke, i) => this.inkJSON(stroke, times[i])) };
+// const processGestures = false;
+
+// if (math) {
+// this._mathRef.editor.pointerEvents(pointerData, processGestures);
+// } else {
+// this._textRef.editor.pointerEvents(pointerData, processGestures);
+// }
+// };
+
+// /**
+// * Converts the Dash Ink Data to JSON.
+// *
+// * @param stroke The dash ink data
+// * @param time the time of the stroke
+// * @returns json object representation of ink data
+// */
+// inkJSON = (stroke: InkData, time: number) => {
+// return {
+// pointerType: 'PEN',
+// pointerId: 1,
+// x: stroke.map(point => point.X),
+// y: stroke.map(point => point.Y),
+// t: new Array(stroke.length).fill(time),
+// p: new Array(stroke.length).fill(1.0),
+// };
+// };
+
+// /**
+// * Creates subgroups for each word for the whole text transcription
+// * @param wordInkDocMap the mapping of words to ink strokes (Ink Docs)
+// */
+// subgroupsTranscriptions = (wordInkDocMap: Map<string, Doc[]>) => {
+// // iterate through the keys of wordInkDocMap
+// wordInkDocMap.forEach(async (inkDocs: Doc[], word: string) => {
+// const selected = inkDocs.slice();
+// if (!selected) {
+// return;
+// }
+// const ctx = await Cast(selected[0].embedContainer, Doc);
+// if (!ctx) {
+// return;
+// }
+// const docView: CollectionFreeFormView = DocumentManager.Instance.getDocumentView(ctx)?.ComponentView as CollectionFreeFormView;
+
+// if (!docView) return;
+// const marqViewRef = docView._marqueeViewRef.current;
+// if (!marqViewRef) return;
+// this.groupInkDocs(selected, docView, word);
+// });
+// };
+
+// /**
+// * Event listener function for when the 'exported' event is heard.
+// *
+// * @param e the event objects
+// * @param ref the ref to the editor
+// */
+// exportInk = (e: any, ref: any) => {
+// const exports = e.detail.exports;
+// if (exports) {
+// if (exports['application/x-latex']) {
+// const latex = exports['application/x-latex'];
+// if (this.currGroup) {
+// this.currGroup.text = latex;
+// this.currGroup.title = latex;
+// }
+
+// ref.editor.clear();
+// } else if (exports['text/plain']) {
+// if (exports['application/vnd.myscript.jiix']) {
+// this.lastJiix = JSON.parse(exports['application/vnd.myscript.jiix']);
+// // map timestamp to strokes
+// const timestampWord = new Map<number, string>();
+// this.lastJiix.words.map((word: any) => {
+// if (word.items) {
+// word.items.forEach((i: { id: string; timestamp: string; X: Array<number>; Y: Array<number>; F: Array<number> }) => {
+// const ms = Date.parse(i.timestamp);
+// timestampWord.set(ms, word.label);
+// });
+// }
+// });
+
+// const wordInkDocMap = new Map<string, Doc[]>();
+// if (this.currGroup) {
+// const docList = DocListCast(this.currGroup.data);
+// docList.forEach((inkDoc: Doc) => {
+// // just having the times match up and be a unique value (actual timestamp doesn't matter)
+// const ms = DateCast(inkDoc.author_date).getDate().getTime() + 14400000;
+// const word = timestampWord.get(ms);
+// if (!word) {
+// return;
+// }
+// const entry = wordInkDocMap.get(word);
+// if (entry) {
+// entry.push(inkDoc);
+// wordInkDocMap.set(word, entry);
+// } else {
+// const newEntry = [inkDoc];
+// wordInkDocMap.set(word, newEntry);
+// }
+// });
+// if (this.lastJiix.words.length > 1) this.subgroupsTranscriptions(wordInkDocMap);
+// }
+// }
+// const text = exports['text/plain'];
+
+// if (this.currGroup) {
+// this.currGroup.transcription = text;
+// this.currGroup.title = text.split('\n')[0];
+// }
+
+// ref.editor.clear();
+// }
+// }
+// };
+
+// /**
+// * Creates the ink grouping once the user leaves the writing mode.
+// */
+// createInkGroup() {
+// // TODO nda - if document being added to is a inkGrouping then we can just add to that group
+// if (Doc.ActiveTool === InkTool.Write) {
+// CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
+// // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
+// const selected = ffView.unprocessedDocs;
+// const newCollection = this.groupInkDocs(
+// selected.filter(doc => doc.embedContainer),
+// ffView
+// );
+// ffView.unprocessedDocs = [];
+
+// InkTranscription.Instance.transcribeInk(newCollection, selected, false);
+// });
+// }
+// CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
+// }
+
+// /**
+// * Creates the groupings for a given list of ink docs on a specific doc view
+// * @param selected: the list of ink docs to create a grouping of
+// * @param docView: the view in which we want the grouping to be created
+// * @param word: optional param if the group we are creating is a word (subgrouping individual words)
+// * @returns a new collection Doc or undefined if the grouping fails
+// */
+// groupInkDocs(selected: Doc[], docView: CollectionFreeFormView, word?: string): Doc | undefined {
+// const bounds: { x: number; y: number; width?: number; height?: number }[] = [];
+
+// // calculate the necessary bounds from the selected ink docs
+// selected.map(
+// action(d => {
+// const x = NumCast(d.x);
+// const y = NumCast(d.y);
+// const width = NumCast(d._width);
+// const height = NumCast(d._height);
+// bounds.push({ x, y, width, height });
+// })
+// );
+
+// // calculate the aggregated bounds
+// const aggregBounds = aggregateBounds(bounds, 0, 0);
+// const marqViewRef = docView._marqueeViewRef.current;
+
+// // set the vals for bounds in marqueeView
+// if (marqViewRef) {
+// marqViewRef._downX = aggregBounds.x;
+// marqViewRef._downY = aggregBounds.y;
+// marqViewRef._lastX = aggregBounds.r;
+// marqViewRef._lastY = aggregBounds.b;
+// }
+
+// // map through all the selected ink strokes and create the groupings
+// selected.map(
+// action(d => {
+// const dx = NumCast(d.x);
+// const dy = NumCast(d.y);
+// delete d.x;
+// delete d.y;
+// delete d.activeFrame;
+// delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+// delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+// // calculate pos based on bounds
+// if (marqViewRef?.Bounds) {
+// d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
+// d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
+// }
+// return d;
+// })
+// );
+// docView.props.removeDocument?.(selected);
+// // Gets a collection based on the selected nodes using a marquee view ref
+// const newCollection = marqViewRef?.getCollection(selected, undefined, true);
+// if (newCollection) {
+// newCollection.width = NumCast(newCollection._width);
+// newCollection.height = NumCast(newCollection._height);
+// // if the grouping we are creating is an individual word
+// if (word) {
+// newCollection.title = word;
+// }
+// }
+
+// // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
+// newCollection && docView.props.addDocument?.(newCollection);
+// return newCollection;
+// }
+
+// render() {
+// return (
+// <div className="ink-transcription">
+// <div className="math-editor" ref={this.setMathRef} touch-action="none"></div>
+// <div className="text-editor" ref={this.setTextRef} touch-action="none"></div>
+// </div>
+// );
+// }
+// }
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index e081961e5..41a2507f9 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -20,7 +20,7 @@
Most of the operations that can be performed on an InkStroke (eg delete a point, rotate, stretch) are implemented in the InkStrokeProperties helper class
*/
-import React = require('react');
+import * as React from 'react';
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc } from '../../fields/Doc';
@@ -36,7 +36,6 @@ import { Transform } from '../util/Transform';
import { UndoManager } from '../util/UndoManager';
import { ContextMenu } from './ContextMenu';
import { ViewBoxBaseComponent } from './DocComponent';
-import { INK_MASK_SIZE } from './global/globalCssVariables.scss';
import { Colors } from './global/globalEnums';
import { InkControlPtHandles, InkEndPtHandles } from './InkControlPtHandles';
import './InkStroke.scss';
@@ -47,7 +46,8 @@ import { FieldView, FieldViewProps } from './nodes/FieldView';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import { PinProps, PresBox } from './nodes/trails';
import { StyleProp } from './StyleProvider';
-import Color = require('color');
+// import { INK_MASK_SIZE } from './global/globalCssVariables.scss';
+const INK_MASK_SIZE = 1000;
@observer
export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
diff --git a/src/client/views/KeyphraseQueryView.tsx b/src/client/views/KeyphraseQueryView.tsx
index 13d52db88..e996fc946 100644
--- a/src/client/views/KeyphraseQueryView.tsx
+++ b/src/client/views/KeyphraseQueryView.tsx
@@ -1,6 +1,6 @@
-import { observer } from "mobx-react";
-import React = require("react");
-import "./KeyphraseQueryView.scss";
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import './KeyphraseQueryView.scss';
// tslint:disable-next-line: class-name
export interface KP_Props {
@@ -8,7 +8,7 @@ export interface KP_Props {
}
@observer
-export class KeyphraseQueryView extends React.Component<KP_Props>{
+export class KeyphraseQueryView extends React.Component<KP_Props> {
constructor(props: KP_Props) {
super(props);
}
@@ -22,13 +22,17 @@ export class KeyphraseQueryView extends React.Component<KP_Props>{
<form>
{keyterms.map((kp: string) => {
//return (<p>{"-" + kp}</p>);
- return (<p><label>
- <input name="query" type="radio" />
- <span>{kp}</span>
- </label></p>);
+ return (
+ <p>
+ <label>
+ <input name="query" type="radio" />
+ <span>{kp}</span>
+ </label>
+ </p>
+ );
})}
</form>
</div>
);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 58c1570b2..50feccce2 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -3,7 +3,7 @@ import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons';
import * as far from '@fortawesome/free-regular-svg-icons';
import * as fa from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import 'browndash-components/dist/styles/global.min.css';
+import '../../../node_modules/browndash-components/dist/styles/global.min.css';
import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import 'normalize.css';
@@ -43,9 +43,7 @@ import { DashboardView } from './DashboardView';
import { DictationOverlay } from './DictationOverlay';
import { DocumentDecorations } from './DocumentDecorations';
import { GestureOverlay } from './GestureOverlay';
-import { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } from './global/globalCssVariables.scss';
import { KeyManager } from './GlobalKeyHandler';
-import { InkTranscription } from './InkTranscription';
import { LightboxView } from './LightboxView';
import { LinkMenu } from './linking/LinkMenu';
import './MainView.scss';
@@ -71,6 +69,9 @@ import { PreviewCursor } from './PreviewCursor';
import { PropertiesView } from './PropertiesView';
import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
import { TopBar } from './topbar/TopBar';
+// import { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } from './global/globalCssVariables.scss';
+const LEFT_MENU_WIDTH = '60px';
+const TOPBAR_HEIGHT = '37px';
const _global = (window /* browser */ || global) /* node */ as any;
@observer
@@ -1063,7 +1064,7 @@ export class MainView extends React.Component {
<MarqueeOptionsMenu />
<TimelineMenu />
<RichTextMenu />
- <InkTranscription />
+ {/* <InkTranscription /> */}
{this.snapLines}
<LightboxView key="lightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
<OverlayView />
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 70a44a08a..a241e3a2b 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -15,7 +15,7 @@ import './MarqueeAnnotator.scss';
import { DocumentView } from './nodes/DocumentView';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import { AnchorMenu } from './pdf/AnchorMenu';
-import React = require('react');
+import * as React from 'react';
const _global = (window /* browser */ || global) /* node */ as any;
export interface MarqueeAnnotatorProps {
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index bcbdd3ccb..5c6912121 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -156,6 +156,7 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps> {
<span>Key:</span>
<div className="metadataEntry-autoSuggester" onClick={e => this.autosuggestRef.current!.input?.focus()}>
<Autosuggest
+ // @ts-ignore
inputProps={{ value: this._currentKey, onChange: this.onKeyChange }}
getSuggestionValue={this.getSuggestionValue}
suggestions={emptyPath}
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 6635aabf9..7824ad1c8 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -27,7 +27,7 @@ import { Colors } from './global/globalEnums';
import { InkingStroke } from './InkingStroke';
import { DocumentView, OpenWhere } from './nodes/DocumentView';
import './PropertiesButtons.scss';
-import React = require('react');
+import * as React from 'react';
enum UtilityButtonState {
Default,
diff --git a/src/client/views/PropertiesSection.tsx b/src/client/views/PropertiesSection.tsx
index bd586b2f9..2b9bfb505 100644
--- a/src/client/views/PropertiesSection.tsx
+++ b/src/client/views/PropertiesSection.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index c5b0528af..2c3cd8eac 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1,13 +1,13 @@
-import React = require('react');
+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/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 049ba4841..2a52d86c2 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -16,7 +16,7 @@ import { FieldViewProps } from './nodes/FieldView';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import './SidebarAnnos.scss';
import { StyleProp } from './StyleProvider';
-import React = require('react');
+import * as React from 'react';
interface ExtraProps {
fieldKey: string;
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 806c9c001..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';
@@ -23,7 +23,7 @@ import { FieldViewProps } from './nodes/FieldView';
import { KeyValueBox } from './nodes/KeyValueBox';
import { PropertiesView } from './PropertiesView';
import './StyleProvider.scss';
-import React = require('react');
+import * as React from 'react';
export enum StyleProp {
TreeViewIcon = 'treeView_Icon',
@@ -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/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 6da228417..48653f30a 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -13,7 +13,7 @@ import { CollectionTreeView } from './collections/CollectionTreeView';
import { DocumentView } from './nodes/DocumentView';
import { DefaultStyleProvider } from './StyleProvider';
import './TemplateMenu.scss';
-import React = require('react');
+import * as React from 'react';
@observer
class TemplateToggle extends React.Component<{ template: string; checked: boolean; toggle: (event: React.ChangeEvent<HTMLInputElement>, template: string) => void }> {
diff --git a/src/client/views/TouchScrollableMenu.tsx b/src/client/views/TouchScrollableMenu.tsx
index 969605be9..530c693a7 100644
--- a/src/client/views/TouchScrollableMenu.tsx
+++ b/src/client/views/TouchScrollableMenu.tsx
@@ -1,6 +1,6 @@
-import React = require("react");
-import { computed } from "mobx";
-import { observer } from "mobx-react";
+import * as React from 'react';
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
export interface TouchScrollableMenuProps {
options: JSX.Element[];
@@ -24,31 +24,35 @@ export interface TouchScrollableMenuItemProps {
@observer
export default class TouchScrollableMenu extends React.Component<TouchScrollableMenuProps> {
-
@computed
- private get possibilities() { return this.props.options; }
+ private get possibilities() {
+ return this.props.options;
+ }
@computed
- private get selectedIndex() { return this.props.selectedIndex; }
+ private get selectedIndex() {
+ return this.props.selectedIndex;
+ }
render() {
return (
- <div className="inkToTextDoc-cont" style={{
- transform: `translate(${this.props.x}px, ${this.props.y}px)`,
- width: 300,
- height: this.possibilities.length * 25
- }}>
+ <div
+ className="inkToTextDoc-cont"
+ style={{
+ transform: `translate(${this.props.x}px, ${this.props.y}px)`,
+ width: 300,
+ height: this.possibilities.length * 25,
+ }}>
<div className="inkToTextDoc-scroller" style={{ transform: `translate(0, ${-this.selectedIndex * 25}px)` }}>
{this.possibilities}
</div>
- <div className="shadow" style={{ height: `calc(100% - 25px - ${this.selectedIndex * 25}px)` }}>
- </div>
+ <div className="shadow" style={{ height: `calc(100% - 25px - ${this.selectedIndex * 25}px)` }}></div>
</div>
);
}
}
-export class TouchScrollableMenuItem extends React.Component<TouchScrollableMenuItemProps>{
+export class TouchScrollableMenuItem extends React.Component<TouchScrollableMenuItemProps> {
render() {
return (
<div className="menuItem-cont" onClick={this.props.onClick}>
@@ -56,4 +60,4 @@ export class TouchScrollableMenuItem extends React.Component<TouchScrollableMenu
</div>
);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index d6368464a..330cb93e4 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -10,11 +10,13 @@ import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
import { SelectionManager } from '../../util/SelectionManager';
import { StyleProp } from '../StyleProvider';
-import { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } from '../global/globalCssVariables.scss';
import { DocFocusOptions, DocumentView } from '../nodes/DocumentView';
import './CollectionCarousel3DView.scss';
import { CollectionSubView } from './CollectionSubView';
-
+// import { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } from '../global/globalCssVariables.scss';
+const CAROUSEL3D_CENTER_SCALE = '1';
+const CAROUSEL3D_SIDE_SCALE = '1';
+const CAROUSEL3D_TOP = '0';
@observer
export class CollectionCarousel3DView extends CollectionSubView() {
@computed get scrollSpeed() {
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 333ba9f32..11543bbe5 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -276,8 +276,6 @@
z-index: 20;
} /*# sourceMappingURL=goldenlayout-base.css.map */
-@import '../../../../node_modules/golden-layout/src/css/goldenlayout-dark-theme.css';
-
.lm_title {
-webkit-appearance: none;
display: inline-block;
@@ -681,11 +679,6 @@ ul.lm_tabs::before {
height: 8px;
}
- .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;
- }
-
.flexlayout__tab_button_overflow {
float: left;
width: 20px;
@@ -696,7 +689,6 @@ 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;
}
.flexlayout__tabset_header {
@@ -751,7 +743,6 @@ ul.lm_tabs::before {
height: 20px;
border: none;
outline-width: 0;
- background: transparent url('../../../../node_modules/flexlayout-react/images/maximize.png') no-repeat center;
}
.flexlayout__tab_toolbar_button-max {
@@ -759,7 +750,6 @@ ul.lm_tabs::before {
height: 20px;
border: none;
outline-width: 0;
- background: transparent url('../../../../node_modules/flexlayout-react/images/restore.png') no-repeat center;
}
.flexlayout__popup_menu_item {
@@ -877,11 +867,6 @@ ul.lm_tabs::before {
height: 8px;
}
- .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;
- }
-
.flexlayout__border_toolbar_left {
position: absolute;
display: flex;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 2e8047309..858090c05 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -30,7 +30,7 @@ import './CollectionDockingView.scss';
import { CollectionFreeFormView } from './collectionFreeForm';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
import { TabDocView } from './TabDocView';
-import React = require('react');
+import * as React from 'react';
import { SettingsManager } from '../../util/SettingsManager';
const _global = (window /* browser */ || global) /* node */ as any;
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index a9b5f401d..61a5738ec 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
@@ -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 8b3531da8..3edf4135f 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -1,6 +1,6 @@
-import React = require('react');
+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';
@@ -541,6 +541,7 @@ export class CollectionNoteTakingViewChrome extends React.Component<CollectionVi
autosuggestProps: {
inputProps: {
value: this._currentKey,
+ // @ts-ignore
onChange: this.onKeyChange,
},
getSuggestionValue: this.getSuggestionValue,
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index ad5431c8e..ba00c4fa0 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -1,5 +1,4 @@
-import React = require('react');
-import { CursorProperty } from 'csstype';
+import * as React from 'react';
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/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 9e5f09479..9be01df18 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, trace } from 'mobx';
import { observer } from 'mobx-react';
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index ad8144466..c5677e2ba 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -12,7 +12,7 @@ import { computePassLayout, computeStarburstLayout } from './collectionFreeForm'
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import './CollectionPileView.scss';
import { CollectionSubView } from './CollectionSubView';
-import React = require('react');
+import * as React from 'react';
@observer
export class CollectionPileView extends CollectionSubView() {
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 7fee1cda6..92b5470ae 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 59e0c60b9..bc428c22f 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,6 +1,6 @@
-import React = require('react');
+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/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 9bd412fb8..3282eb4b4 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index f082ca0da..40b2f9644 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -24,7 +24,7 @@ import { ViewBoxBaseComponent } from '../DocComponent';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { LoadingBox } from '../nodes/LoadingBox';
import { CollectionView, CollectionViewProps } from './CollectionView';
-import React = require('react');
+import * as React from 'react';
export interface SubCollectionViewProps extends CollectionViewProps {
isAnyChildContentActive: () => boolean;
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 8d114761a..e65f24702 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -21,7 +21,7 @@ import { computePivotLayout, computeTimelineLayout, ViewDefBounds } from './coll
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import { CollectionSubView } from './CollectionSubView';
import './CollectionTimeView.scss';
-import React = require('react');
+import * as React from 'react';
@observer
export class CollectionTimeView extends CollectionSubView() {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 1315decb2..5c165fe70 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -26,7 +26,7 @@ import { CollectionFreeFormView } from './collectionFreeForm';
import { CollectionSubView } from './CollectionSubView';
import './CollectionTreeView.scss';
import { TreeView } from './TreeView';
-import React = require('react');
+import * as React from 'react';
const _global = (window /* browser */ || global) /* node */ as any;
export type collectionTreeViewProps = {
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index def4beca3..4a1d702b8 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -33,7 +33,7 @@ import { CollectionDockingView } from './CollectionDockingView';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import { CollectionView } from './CollectionView';
import './TabDocView.scss';
-import React = require('react');
+import * as React from 'react';
import { Docs } from '../../documents/Documents';
import { ComputedField } from '../../../fields/ScriptField';
const _global = (window /* browser */ || global) /* node */ as any;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 0de1068f7..ac79e4fef 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -24,7 +24,6 @@ import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
import { EditableView } from '../EditableView';
-import { TREE_BULLET_WIDTH } from '../global/globalCssVariables.scss';
import { DocumentView, DocumentViewInternal, DocumentViewProps, OpenWhere, StyleProviderFunc } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
@@ -35,7 +34,9 @@ import { CollectionTreeView, TreeViewType } from './CollectionTreeView';
import { CollectionView } from './CollectionView';
import { TreeSort } from './TreeSort';
import './TreeView.scss';
-import React = require('react');
+import * as React from 'react';
+// import { TREE_BULLET_WIDTH } from '../global/globalCssVariables.scss';
+const TREE_BULLET_WIDTH = '10px';
export interface TreeViewProps {
treeView: CollectionTreeView;
@@ -118,16 +119,16 @@ export class TreeView extends React.Component<TreeViewProps> {
return this.doc._type_collection === CollectionViewType.Docking
? this.fieldKey
: this.props.treeView.dashboardMode
- ? this.fieldKey
- : this.props.treeView.fileSysMode
- ? this.doc.isFolder
- ? this.fieldKey
- : 'data' // file system folders display their contents (data). used to be they displayed their embeddings but now its a tree structure and not a flat list
- : this.props.treeView.outlineMode || this.childDocs
- ? this.fieldKey
- : Doc.noviceMode
- ? 'layout'
- : StrCast(this.props.treeView.doc.treeView_ExpandedView, 'fields');
+ ? this.fieldKey
+ : this.props.treeView.fileSysMode
+ ? this.doc.isFolder
+ ? this.fieldKey
+ : 'data' // file system folders display their contents (data). used to be they displayed their embeddings but now its a tree structure and not a flat list
+ : this.props.treeView.outlineMode || this.childDocs
+ ? this.fieldKey
+ : Doc.noviceMode
+ ? 'layout'
+ : StrCast(this.props.treeView.doc.treeView_ExpandedView, 'fields');
}
@computed get doc() {
@@ -832,14 +833,14 @@ export class TreeView extends React.Component<TreeViewProps> {
...(this.doc.isFolder
? folderOp
: Doc.IsSystem(this.doc)
- ? []
- : this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc)
- ? [openEmbedding, makeFolder]
- : this.doc._type_collection === CollectionViewType.Docking
- ? []
- : this.props.treeView.Document === Doc.MyRecentlyClosed
- ? [reopenDoc]
- : [openEmbedding, focusDoc]),
+ ? []
+ : this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc)
+ ? [openEmbedding, makeFolder]
+ : this.doc._type_collection === CollectionViewType.Docking
+ ? []
+ : this.props.treeView.Document === Doc.MyRecentlyClosed
+ ? [reopenDoc]
+ : [openEmbedding, focusDoc]),
];
};
childContextMenuItems = () => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
index 00505dbe3..99ee5ef4e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
@@ -2,7 +2,7 @@ import { observer } from 'mobx-react';
import { Doc } from '../../../../fields/Doc';
import { NumCast } from '../../../../fields/Types';
import './CollectionFreeFormView.scss';
-import React = require('react');
+import * as React from 'react';
export interface CollectionFreeFormViewBackgroundGridProps {
panX: () => number;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
index 3af5a6c4b..3ba7aedef 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
@@ -1,7 +1,7 @@
import { IReactionDisposer, reaction } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import './CollectionFreeFormView.scss';
-import React = require('react');
/**
* An Fsa Arc. The first array element is a test condition function that will be observed.
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
index daf1bd8a7..1265dc2de 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
@@ -4,7 +4,7 @@ import { Doc } from '../../../../fields/Doc';
import { ScriptField } from '../../../../fields/ScriptField';
import { PresBox } from '../../nodes/trails/PresBox';
import './CollectionFreeFormView.scss';
-import React = require('react');
+import * as React from 'react';
import { CollectionFreeFormView } from './CollectionFreeFormView';
import { NumCast } from '../../../../fields/Types';
import { LinkManager } from '../../../util/LinkManager';
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index d6a738084..b8c0967c1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -5,7 +5,7 @@ import { RefField } from '../../../../fields/RefField';
import { listSpec } from '../../../../fields/Schema';
import { Cast, NumCast, StrCast } from '../../../../fields/Types';
import { aggregateBounds } from '../../../../Utils';
-import React = require('react');
+import * as React from 'react';
export interface ViewDefBounds {
type: string;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 0c0d45a30..4995925c8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -13,7 +13,7 @@ import { SnappingManager } from '../../../util/SnappingManager';
import { Colors } from '../../global/globalEnums';
import { DocumentView } from '../../nodes/DocumentView';
import './CollectionFreeFormLinkView.scss';
-import React = require('react');
+import * as React from 'react';
export interface CollectionFreeFormLinkViewProps {
A: DocumentView;
@@ -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/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 420e6a318..779a0bf96 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -5,7 +5,7 @@ import { DocumentManager } from '../../../util/DocumentManager';
import { LightboxView } from '../../LightboxView';
import './CollectionFreeFormLinksView.scss';
import { CollectionFreeFormLinkView } from './CollectionFreeFormLinkView';
-import React = require('react');
+import * as React from 'react';
@observer
export class CollectionFreeFormLinksView extends React.Component {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
index 38aeea152..f54726a00 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
@@ -4,7 +4,7 @@ import { Doc } from '../../../../fields/Doc';
import { ScriptField } from '../../../../fields/ScriptField';
import { PresBox } from '../../nodes/trails/PresBox';
import './CollectionFreeFormView.scss';
-import React = require('react');
+import * as React from 'react';
import { CollectionFreeFormView } from './CollectionFreeFormView';
export interface CollectionFreeFormPannableContentsProps {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 9e8d92d7d..45e24bbb2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -9,8 +9,8 @@ import { listSpec } from '../../../../fields/Schema';
import { Cast } from '../../../../fields/Types';
import { CollectionViewProps } from '../CollectionView';
import './CollectionFreeFormView.scss';
-import React = require('react');
-import v5 = require('uuid/v5');
+import * as React from 'react';
+import * as uuid from 'uuid';
@observer
export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
@@ -42,7 +42,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
if (el) {
const ctx = el.getContext('2d');
if (ctx) {
- ctx.fillStyle = '#' + v5(metadata.id, v5.URL).substring(0, 6).toUpperCase() + '22';
+ ctx.fillStyle = '#' + uuid.v5(metadata.id, uuid.v5.URL).substring(0, 6).toUpperCase() + '22';
ctx.fillRect(0, 0, 20, 20);
ctx.fillStyle = 'black';
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 91617ec44..03d302f39 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -3,6 +3,7 @@ import { Colors } from 'browndash-components';
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
+import * as React from 'react';
import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
import { DocData, Height, Width } from '../../../../fields/DocSymbols';
@@ -45,13 +46,12 @@ import { StyleProp } from '../../StyleProvider';
import { CollectionSubView } from '../CollectionSubView';
import { TreeViewType } from '../CollectionTreeView';
import { CollectionFreeFormBackgroundGrid } from './CollectionFreeFormBackgroundGrid';
+import { CollectionFreeFormInfoUI } from './CollectionFreeFormInfoUI';
import { computePassLayout, computePivotLayout, computeStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines';
import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannableContents';
import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
-import React = require('react');
-import { CollectionFreeFormInfoUI } from './CollectionFreeFormInfoUI';
export type collectionFreeformViewProps = {
NativeWidth?: () => number;
@@ -815,23 +815,26 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
eraserMax.X >= inkViewBounds.left &&
eraserMax.Y >= inkViewBounds.top
)
- .reduce((intersections, { inkStroke, inkView }) => {
- const { inkData } = inkStroke.inkScaledData();
- // Convert from screen space to ink space for the intersection.
- const prevPointInkSpace = inkStroke.ptFromScreen(lastPoint);
- const currPointInkSpace = inkStroke.ptFromScreen(currPoint);
- for (var i = 0; i < inkData.length - 3; i += 4) {
- const rawIntersects = InkField.Segment(inkData, i).intersects({
- // compute all unique intersections
- p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
- p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y },
- });
- const intersects = Array.from(new Set(rawIntersects as (number | string)[])); // convert to more manageable union array type
- // return tuples of the inkingStroke intersected, and the t value of the intersection
- intersections.push(...intersects.map(t => ({ inkView, t: +t + Math.floor(i / 4) }))); // convert string t's to numbers and add start of curve segment to convert from local t value to t value along complete curve
- }
- return intersections;
- }, [] as { t: number; inkView: DocumentView }[]);
+ .reduce(
+ (intersections, { inkStroke, inkView }) => {
+ const { inkData } = inkStroke.inkScaledData();
+ // Convert from screen space to ink space for the intersection.
+ const prevPointInkSpace = inkStroke.ptFromScreen(lastPoint);
+ const currPointInkSpace = inkStroke.ptFromScreen(currPoint);
+ for (var i = 0; i < inkData.length - 3; i += 4) {
+ const rawIntersects = InkField.Segment(inkData, i).intersects({
+ // compute all unique intersections
+ p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
+ p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y },
+ });
+ const intersects = Array.from(new Set(rawIntersects as (number | string)[])); // convert to more manageable union array type
+ // return tuples of the inkingStroke intersected, and the t value of the intersection
+ intersections.push(...intersects.map(t => ({ inkView, t: +t + Math.floor(i / 4) }))); // convert string t's to numbers and add start of curve segment to convert from local t value to t value along complete curve
+ }
+ return intersections;
+ },
+ [] as { t: number; inkView: DocumentView }[]
+ );
};
/**
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 607f9fb95..825bd5f19 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -1,14 +1,11 @@
-import React = require('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/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 18b9b4423..330c13e97 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -24,7 +24,7 @@ import { PreviewCursor } from '../../PreviewCursor';
import { SubCollectionViewProps } from '../CollectionSubView';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './MarqueeView.scss';
-import React = require('react');
+import * as React from 'react';
import { freeformScrollMode } from '../../../util/SettingsManager';
interface MarqueeViewProps {
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.scss b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
index d0c14a21d..3c2f5ccd7 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.scss
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
@@ -18,9 +18,9 @@
user-select: none;
}
- > input:not(:checked) ~ &.true {
- background-color: transparent;
- }
+ // > input:not(:checked) ~ &.true {
+ // background-color: transparent;
+ // }
.collectionLinearView {
display: flex;
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/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 030f9953c..029d39f88 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, ObservableMap, observe } from 'mobx';
import { observer } from 'mobx-react';
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index 65e47f441..0e37198bb 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 03697b40a..53f4a4d51 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { IconButton, Size } from 'browndash-components';
import { computed } from 'mobx';
import { observer } from 'mobx-react';
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 0ad76db35..de29d8602 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -14,7 +14,7 @@ import { undoable, UndoManager } from '../../util/UndoManager';
import { CollectionFreeFormView } from '../collections/collectionFreeForm';
import { GestureOverlay } from '../GestureOverlay';
import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, SetActiveIsInkMask } from '../InkingStroke';
-import { InkTranscription } from '../InkTranscription';
+// import { InkTranscription } from '../InkTranscription';
import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
import { DocumentView } from '../nodes/DocumentView';
import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
@@ -293,14 +293,14 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
// TODO: nda - will probably need to go through and only remove the unprocessed selected docs
ffView.unprocessedDocs = [];
- InkTranscription.Instance.transcribeInk(newCollection, selected, false);
+ // InkTranscription.Instance.transcribeInk(newCollection, selected, false);
});
}
CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
}
function setActiveTool(tool: InkTool | GestureUtils.Gestures, keepPrim: boolean, checkResult?: boolean) {
- InkTranscription.Instance?.createInkGroup();
+ // InkTranscription.Instance?.createInkGroup();
if (checkResult) {
return (Doc.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool
? GestureOverlay.Instance?.KeepPrimitiveMode || ![GestureUtils.Gestures.Circle, GestureUtils.Gestures.Line, GestureUtils.Gestures.Rectangle].includes(tool as GestureUtils.Gestures)
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 3082c632d..3d9cf1893 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -6,7 +6,7 @@ import { DocumentView } from '../nodes/DocumentView';
import { LinkDocPreview } from '../nodes/LinkDocPreview';
import './LinkMenu.scss';
import { LinkMenuGroup } from './LinkMenuGroup';
-import React = require('react');
+import * as React from 'react';
import { SettingsManager } from '../../util/SettingsManager';
interface Props {
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index 5453ed07b..91142b90b 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -7,7 +7,7 @@ import { LinkManager } from '../../util/LinkManager';
import { DocumentView } from '../nodes/DocumentView';
import './LinkMenu.scss';
import { LinkMenuItem } from './LinkMenuItem';
-import React = require('react');
+import * as React from 'react';
import { DocumentType } from '../../documents/DocumentTypes';
interface LinkMenuGroupProps {
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 611771fff..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';
@@ -17,7 +17,7 @@ import { undoBatch } from '../../util/UndoManager';
import { DocumentView, DocumentViewInternal, OpenWhere } from '../nodes/DocumentView';
import { LinkDocPreview } from '../nodes/LinkDocPreview';
import './LinkMenuItem.scss';
-import React = require('react');
+import * as React from 'react';
interface LinkMenuItemProps {
groupType: string;
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index a36c8d778..5460a6daf 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -10,7 +10,7 @@ import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { SearchBox } from '../search/SearchBox';
import { DefaultStyleProvider } from '../StyleProvider';
import './LinkPopup.scss';
-import React = require('react');
+import * as React from 'react';
interface LinkPopupProps {
linkFrom?: () => Doc | undefined;
diff --git a/src/client/views/linking/LinkRelationshipSearch.tsx b/src/client/views/linking/LinkRelationshipSearch.tsx
index 9662b2fea..3e7f5be74 100644
--- a/src/client/views/linking/LinkRelationshipSearch.tsx
+++ b/src/client/views/linking/LinkRelationshipSearch.tsx
@@ -1,6 +1,6 @@
import { observer } from 'mobx-react';
import './LinkEditor.scss';
-import React = require('react');
+import * as React from 'react';
interface link_relationshipSearchProps {
results: string[] | undefined;
diff --git a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
index a085e4a66..23027808f 100644
--- a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
+++ b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx
@@ -25,11 +25,13 @@ export const Recommendation = (props: IRecommendation) => {
switch (type) {
case 'YouTube':
console.log('create ', type, 'document');
- doc = Docs.Create.VideoDocument(data, { title: title, _width: 400, _height: 315, transcript });
+ doc = Docs.Create.VideoDocument(data, { title: title, _width: 400, _height: 315 });
+ doc.transcript = transcript ? JSON.stringify(transcript) : undefined;
break;
case 'Video':
console.log('create ', type, 'document');
- doc = Docs.Create.VideoDocument(data, { title: title, _width: 400, _height: 315, transcript });
+ doc = Docs.Create.VideoDocument(data, { title: title, _width: 400, _height: 315 });
+ doc.transcript = transcript ? JSON.stringify(transcript) : undefined;
break;
case 'Webpage':
console.log('create ', type, 'document');
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index dacdbe986..1fb26c99b 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -1,6 +1,6 @@
-import React = require('react');
+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/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 6195a155d..624f28413 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -14,7 +14,7 @@ import { DocComponent } from '../DocComponent';
import { StyleProp } from '../StyleProvider';
import './CollectionFreeFormDocumentView.scss';
import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView';
-import React = require('react');
+import * as React from 'react';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
export interface CollectionFreeFormDocumentViewWrapperProps extends DocumentViewProps {
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 2a20d935b..b4ba51814 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -1,9 +1,8 @@
-import React = require('react');
+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/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 9fd4fbcaa..72a473114 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -13,7 +13,7 @@ import './ComparisonBox.scss';
import { DocumentView, DocumentViewProps } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { PinProps, PresBox } from './trails';
-import React = require('react');
+import * as React from 'react';
@observer
export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
index 4dbd67485..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;
@@ -277,6 +277,7 @@ export class PieChart extends React.Component<PieChartProps> {
return 'slice';
}
)
+ // @ts-ignore
.attr('d', arc)
.on('click', onPointClick)
.on('mouseover', onHover)
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index 9ac06cf3c..d422a7536 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -10,8 +10,9 @@ import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../../Utils';
import { DragManager } from '../../../../util/DragManager';
import { DocumentView } from '../../DocumentView';
import { DataVizView } from '../DataVizBox';
-import { DATA_VIZ_TABLE_ROW_HEIGHT } from '../../../global/globalCssVariables.scss';
import './Chart.scss';
+//import { DATA_VIZ_TABLE_ROW_HEIGHT } from '../../../global/globalCssVariables.scss';
+const DATA_VIZ_TABLE_ROW_HEIGHT = '30px';
interface TableBoxProps {
Document: Doc;
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 4b58bfe48..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';
@@ -46,7 +45,7 @@ import { ScriptingBox } from './ScriptingBox';
import { PresBox } from './trails/PresBox';
import { VideoBox } from './VideoBox';
import { WebBox } from './WebBox';
-import React = require('react');
+import * as React from 'react';
import XRegExp = require('xregexp');
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
@@ -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 ce3650749..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';
@@ -15,8 +15,8 @@ import { DocumentView } from './DocumentView';
import { LinkDescriptionPopup } from './LinkDescriptionPopup';
import { TaskCompletionBox } from './TaskCompletedBox';
import { PinProps } from './trails';
-import React = require('react');
-import _ = require('lodash');
+import * as React from 'react';
+import * as _ from 'lodash';
interface DocumentLinksButtonProps {
View: DocumentView;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 40ae1459f..f2a910023 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -51,7 +51,7 @@ import { KeyValueBox } from './KeyValueBox';
import { LinkAnchorBox } from './LinkAnchorBox';
import { PresEffect, PresEffectDirection } from './trails';
import { PinProps, PresBox } from './trails/PresBox';
-import React = require('react');
+import * as React from 'react';
const { Howl } = require('howler');
interface Window {
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index cd263f82a..23413b9d1 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -1,4 +1,4 @@
-import EquationEditor from 'equation-editor-react';
+import EquationEditor from './formattedText/EquationEditor';
import { action, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
diff --git a/src/client/views/nodes/FaceRectangle.tsx b/src/client/views/nodes/FaceRectangle.tsx
index 20afa4565..8d03bf57a 100644
--- a/src/client/views/nodes/FaceRectangle.tsx
+++ b/src/client/views/nodes/FaceRectangle.tsx
@@ -1,14 +1,14 @@
-import React = require("react");
-import { observer } from "mobx-react";
-import { observable, runInAction } from "mobx";
-import { RectangleTemplate } from "./FaceRectangles";
+import * as React from 'react';
+import { observer } from 'mobx-react';
+import { observable, runInAction } from 'mobx';
+import { RectangleTemplate } from './FaceRectangles';
@observer
export default class FaceRectangle extends React.Component<{ rectangle: RectangleTemplate }> {
@observable private opacity = 0;
componentDidMount() {
- setTimeout(() => runInAction(() => this.opacity = 1), 500);
+ setTimeout(() => runInAction(() => (this.opacity = 1)), 500);
}
render() {
@@ -18,12 +18,11 @@ export default class FaceRectangle extends React.Component<{ rectangle: Rectangl
style={{
...rectangle.style,
opacity: this.opacity,
- transition: "1s ease opacity",
- position: "absolute",
- borderRadius: 5
+ transition: '1s ease opacity',
+ position: 'absolute',
+ borderRadius: 5,
}}
/>
);
}
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/FaceRectangles.tsx b/src/client/views/nodes/FaceRectangles.tsx
index 0d1e063af..26e720c0d 100644
--- a/src/client/views/nodes/FaceRectangles.tsx
+++ b/src/client/views/nodes/FaceRectangles.tsx
@@ -1,9 +1,9 @@
-import React = require("react");
-import { Doc, DocListCast } from "../../../fields/Doc";
-import { Cast, NumCast } from "../../../fields/Types";
-import { observer } from "mobx-react";
-import { Id } from "../../../fields/FieldSymbols";
-import FaceRectangle from "./FaceRectangle";
+import * as React from 'react';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { Cast, NumCast } from '../../../fields/Types';
+import { observer } from 'mobx-react';
+import { Id } from '../../../fields/FieldSymbols';
+import FaceRectangle from './FaceRectangle';
interface FaceRectanglesProps {
document: Doc;
@@ -18,7 +18,6 @@ export interface RectangleTemplate {
@observer
export class FaceRectangles extends React.Component<FaceRectanglesProps> {
-
render() {
const faces = DocListCast(this.props.document.faces);
const templates: RectangleTemplate[] = faces.map(faceDoc => {
@@ -33,14 +32,15 @@ export class FaceRectangles extends React.Component<FaceRectanglesProps> {
} as React.CSSProperties;
return {
id: rectangle[Id],
- style: style
+ style: style,
};
});
return (
<div>
- {templates.map(rectangle => <FaceRectangle key={rectangle.id} rectangle={rectangle} />)}
+ {templates.map(rectangle => (
+ <FaceRectangle key={rectangle.id} rectangle={rectangle} />
+ ))}
</div>
);
}
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 219e3025a..f4c5167a5 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { computed } from 'mobx';
import { observer } from 'mobx-react';
import { DateField } from '../../../fields/DateField';
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d28d71fe3..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';
@@ -35,7 +35,7 @@ import { FaceRectangles } from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import './ImageBox.scss';
import { PinProps, PresBox } from './trails';
-import React = require('react');
+import * as React from 'react';
export const pageSchema = createSchema({
googlePhotosUrl: 'string',
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 0c6377c3a..d2325a807 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -20,7 +20,7 @@ import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { ImageBox } from './ImageBox';
import './KeyValueBox.scss';
import { KeyValuePair } from './KeyValuePair';
-import React = require('react');
+import * as React from 'react';
export type KVPScript = {
script: CompiledScript;
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 577685636..3cda70648 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -12,9 +12,9 @@ import { FieldView, FieldViewProps } from './FieldView';
import { KeyValueBox } from './KeyValueBox';
import './KeyValueBox.scss';
import './KeyValuePair.scss';
-import React = require('react');
+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/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index cf157c3a9..1b5161e47 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -12,9 +12,9 @@ import { StyleProp } from '../StyleProvider';
import { FieldView, FieldViewProps } from './FieldView';
import './LinkAnchorBox.scss';
import { LinkDocPreview } from './LinkDocPreview';
-import React = require('react');
-import globalCssVariables = require('../global/globalCssVariables.scss');
-
+import * as React from 'react';
+// import globalCssVariables = require('../global/globalCssVariables.scss');
+const MEDIUM_GRAY = 'lightGray';
@observer
export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) {
@@ -77,8 +77,8 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
const selView = SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('link_anchor_1')
? 'link_anchor_1'
: SelectionManager.Views().lastElement()?.props.LayoutTemplateString?.includes('link_anchor_2')
- ? 'link_anchor_2'
- : '';
+ ? 'link_anchor_2'
+ : '';
return (
<div
ref={this._ref}
@@ -97,7 +97,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
onPointerDown={this.onPointerDown}
onContextMenu={this.specificContextMenu}
style={{
- border: selView && this.dataDoc[selView] === this.dataDoc[this.fieldKey] ? `solid ${globalCssVariables.MEDIUM_GRAY} 2px` : undefined,
+ border: selView && this.dataDoc[selView] === this.dataDoc[this.fieldKey] ? `solid ${MEDIUM_GRAY} 2px` : undefined,
background,
left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`,
top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`,
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 743075c89..ed448ecfb 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { Bezier } from 'bezier-js';
import { computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx
index c45045a8a..32300d60a 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.tsx
+++ b/src/client/views/nodes/LinkDescriptionPopup.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import { Doc } from '../../../fields/Doc';
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index ad5324b3e..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';
@@ -18,7 +18,7 @@ import { SettingsManager } from '../../util/SettingsManager';
import { Transform } from '../../util/Transform';
import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView';
import './LinkDocPreview.scss';
-import React = require('react');
+import * as React from 'react';
interface LinkDocPreviewProps {
linkDoc?: Doc;
diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
index f6680aac0..e6c1db0af 100644
--- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { IReactionDisposer, ObservableMap, reaction } from 'mobx';
import { observer } from 'mobx-react';
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 398d1255e..69723b171 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -794,17 +794,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
/>
))}
</div>
- {/* <MapBoxInfoWindow
- key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}
- {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
- place={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})}
- markerMap={this.markerMap}
- PanelWidth={this.infoWidth}
- PanelHeight={this.infoHeight}
- moveDocument={this.moveDocument}
- isAnyChildContentActive={this.isAnyChildContentActive}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- /> */}
</div>
{/* </LoadScript > */}
<div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index a77bfc50a..39ed6a47e 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -1,597 +1,597 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
-import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
-import { NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
-import { Docs } from '../../../documents/Documents';
-import { DragManager } from '../../../util/DragManager';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { UndoManager } from '../../../util/UndoManager';
-import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
-import { Colors } from '../../global/globalEnums';
-import { AnchorMenu } from '../../pdf/AnchorMenu';
-import { Annotation } from '../../pdf/Annotation';
-import { SidebarAnnos } from '../../SidebarAnnos';
-import { FieldView, FieldViewProps } from '../FieldView';
-import { PinProps } from '../trails';
-import './MapBox2.scss';
-import { MapBoxInfoWindow } from './MapBoxInfoWindow';
-
-/**
- * MapBox2 architecture:
- * Main component: MapBox2.tsx
- * Supporting Components: SidebarAnnos, CollectionStackingView
- *
- * MapBox2 is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content.
- * The main body of MapBox2 uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view.
- * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available,
- * sidebarAddDocument function checks if the document contains lat & lng information, if it does, then the document is added to both the sidebar and the infowindow (a pop up corresponding to a map marker--pin on map).
- * The lat and lng field of the document is filled when importing (spec see ConvertDMSToDD method and processFileUpload method in Documents.ts).
- * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
- */
-
-// const _global = (window /* browser */ || global /* node */) as any;
-
-const mapContainerStyle = {
- height: '100%',
-};
-
-const defaultCenter = {
- lat: 42.360081,
- lng: -71.058884,
-};
-
-const mapOptions = {
- fullscreenControl: false,
-};
-
-const apiKey = process.env.GOOGLE_MAPS;
-
-const script = document.createElement('script');
-script.defer = true;
-script.async = true;
-script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
-console.log(script.src);
-document.head.appendChild(script);
-
-/**
- * Consider integrating later: allows for drawing, circling, making shapes on map
- */
-// const drawingManager = new window.google.maps.drawing.DrawingManager({
-// drawingControl: true,
-// drawingControlOptions: {
-// position: google.maps.ControlPosition.TOP_RIGHT,
-// drawingModes: [
-// google.maps.drawing.OverlayType.MARKER,
-// // currently we are not supporting the following drawing mode on map, a thought for future development
-// google.maps.drawing.OverlayType.CIRCLE,
-// google.maps.drawing.OverlayType.POLYLINE,
-// ],
-// },
-// });
-
-// options for searchbox in Google Maps Places Autocomplete API
-const options = {
- fields: ['formatted_address', 'geometry', 'name'], // note: level of details is charged by item per retrieval, not recommended to return all fields
- strictBounds: false,
- types: ['establishment'], // type pf places, subject of change according to user need
-} as google.maps.places.AutocompleteOptions;
-
-@observer
-export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
- private _dropDisposer?: DragManager.DragDropDisposer;
- private _disposers: { [name: string]: IReactionDisposer } = {};
- private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
- @observable private _overlayAnnoInfo: Opt<Doc>;
- showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
- public static LayoutString(fieldKey: string) {
- return FieldView.LayoutString(MapBox2, fieldKey);
- }
- public get SidebarKey() {
- return this.fieldKey + '_sidebar';
- }
- private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
- @computed get inlineTextAnnotations() {
- return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
- }
-
- @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
- @observable private selectedPlace: Doc | undefined;
- @observable private markerMap: { [id: string]: google.maps.Marker } = {};
- @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter;
- @observable private inputRef = React.createRef<HTMLInputElement>();
- @observable private searchMarkers: google.maps.Marker[] = [];
- @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
- @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
- @computed get allSidebarDocs() {
- return DocListCast(this.dataDoc[this.SidebarKey]);
- }
- @computed get allMapMarkers() {
- return DocListCast(this.dataDoc[this.annotationKey]);
- }
- @observable private toggleAddMarker = false;
-
- @observable _showSidebar = false;
- @computed get SidebarShown() {
- return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
- }
-
- static _canAnnotate = true;
- static _hadSelection: boolean = false;
- private _sidebarRef = React.createRef<SidebarAnnos>();
- private _ref: React.RefObject<HTMLDivElement> = React.createRef();
-
- componentDidMount() {
- this.props.setContentView?.(this);
- }
-
- @action
- private setSearchBox = (searchBox: any) => {
- this.searchBox = searchBox;
- };
-
- // iterate allMarkers to size, center, and zoom map to contain all markers
- private fitBounds = (map: google.maps.Map) => {
- const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds();
- const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean);
- !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds()));
- };
-
- /**
- * Custom control for add marker button
- * @param controlDiv
- * @param map
- */
- private CenterControl = () => {
- const controlDiv = document.createElement('div');
- controlDiv.className = 'MapBox2-addMarker';
- // Set CSS for the control border.
- const controlUI = document.createElement('div');
- controlUI.style.backgroundColor = '#fff';
- controlUI.style.borderRadius = '3px';
- controlUI.style.cursor = 'pointer';
- controlUI.style.marginTop = '10px';
- controlUI.style.borderRadius = '4px';
- controlUI.style.marginBottom = '22px';
- controlUI.style.textAlign = 'center';
- controlUI.style.position = 'absolute';
- controlUI.style.width = '32px';
- controlUI.style.height = '32px';
- controlUI.title = 'Click to toggle marker mode. In marker mode, click on map to place a marker.';
-
- const plIcon = document.createElement('img');
- plIcon.src = 'https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png';
- plIcon.style.color = 'rgb(25,25,25)';
- plIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
- plIcon.style.fontSize = '16px';
- plIcon.style.lineHeight = '32px';
- plIcon.style.left = '18';
- plIcon.style.top = '15';
- plIcon.style.position = 'absolute';
- plIcon.width = 14;
- plIcon.height = 14;
- plIcon.innerHTML = 'Add';
- controlUI.appendChild(plIcon);
-
- // Set CSS for the control interior.
- const markerIcon = document.createElement('img');
- markerIcon.src = 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png';
- markerIcon.style.color = 'rgb(25,25,25)';
- markerIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
- markerIcon.style.fontSize = '16px';
- markerIcon.style.lineHeight = '32px';
- markerIcon.style.left = '-2';
- markerIcon.style.top = '1';
- markerIcon.width = 30;
- markerIcon.height = 30;
- markerIcon.style.position = 'absolute';
- markerIcon.innerHTML = 'Add';
- controlUI.appendChild(markerIcon);
-
- // Setup the click event listeners
- controlUI.addEventListener('click', () => {
- if (this.toggleAddMarker === true) {
- this.toggleAddMarker = false;
- console.log('add marker button status:' + this.toggleAddMarker);
- controlUI.style.backgroundColor = '#fff';
- markerIcon.style.color = 'rgb(25,25,25)';
- } else {
- this.toggleAddMarker = true;
- console.log('add marker button status:' + this.toggleAddMarker);
- controlUI.style.backgroundColor = '#4476f7';
- markerIcon.style.color = 'rgb(255,255,255)';
- }
- });
- controlDiv.appendChild(controlUI);
- return controlDiv;
- };
-
- /**
- * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list
- * @param position - the LatLng position where the marker is placed
- * @param map
- */
- @action
- private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
- const marker = new google.maps.Marker({
- position: position,
- map: map,
- });
- map.panTo(position);
- const mapMarker = Docs.Create.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
- this.addDocument(mapMarker, this.annotationKey);
- };
-
- _loadPending = true;
- /**
- * store a reference to google map instance
- * setup the drawing manager on the top right corner of map
- * fit map bounds to contain all markers
- * @param map
- */
- @action
- private loadHandler = (map: google.maps.Map) => {
- this._map = map;
- this._loadPending = true;
- const centerControlDiv = this.CenterControl();
- map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv);
- //drawingManager.setMap(map);
- // if (navigator.geolocation) {
- // navigator.geolocation.getCurrentPosition(
- // (position: Position) => {
- // const pos = {
- // lat: position.coords.latitude,
- // lng: position.coords.longitude,
- // };
- // this._map.setCenter(pos);
- // }
- // );
- // } else {
- // alert("Your geolocation is not supported by browser.")
- // };
- map.setZoom(NumCast(this.dataDoc.map_zoom, 2.5));
- map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng)));
- setTimeout(() => {
- if (this._loadPending && this._map.getBounds()) {
- this._loadPending = false;
- this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
- }
- }, 250);
- // listener to addmarker event
- this._map.addListener('click', (e: MouseEvent) => {
- if (this.toggleAddMarker === true) {
- this.placeMarker((e as any).latLng, map);
- }
- });
- };
-
- @action
- centered = () => {
- if (this._loadPending && this._map.getBounds()) {
- this._loadPending = false;
- this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
- }
- this.dataDoc.mapLat = this._map.getCenter()?.lat();
- this.dataDoc.mapLng = this._map.getCenter()?.lng();
- };
-
- @action
- zoomChanged = () => {
- if (this._loadPending && this._map.getBounds()) {
- this._loadPending = false;
- this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
- }
- this.dataDoc.map_zoom = this._map.getZoom();
- };
-
- /**
- * Load and render all map markers
- * @param marker
- * @param place
- */
- @action
- private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
- place[Id] ? (this.markerMap[place[Id]] = marker) : null;
- };
-
- /**
- * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true
- * @param e
- * @param place
- */
- @action
- private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
- // set which place was clicked
- this.selectedPlace = place;
- place.infoWindowOpen = true;
- };
-
- /**
- * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts
- * @param doc
- * @param sidebarKey
- * @returns
- */
- sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
- console.log('print all sidebar Docs');
- if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
- const docs = doc instanceof Doc ? [doc] : doc;
- docs.forEach(doc => {
- if (doc.lat !== undefined && doc.lng !== undefined) {
- const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng);
- if (existingMarker) {
- Doc.AddDocToList(existingMarker, 'data', doc);
- } else {
- const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
- this.addDocument(marker, this.annotationKey);
- }
- }
- }); //add to annotation list
-
- return this.addDocument(doc, sidebarKey); // add to sidebar list
- };
-
- /**
- * Removing documents from the sidebar
- * @param doc
- * @param sidebarKey
- * @returns
- */
- sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
- if (this.layoutDoc._layout_showSidebar) this.toggleSidebar();
- const docs = doc instanceof Doc ? [doc] : doc;
- return this.removeDocument(doc, sidebarKey);
- };
-
- /**
- * Toggle sidebar onclick the tiny comment button on the top right corner
- * @param e
- */
- sidebarBtnDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(
- this,
- e,
- (e, down, delta) =>
- runInAction(() => {
- const localDelta = this.props
- .ScreenToLocalTransform()
- .scale(this.props.NativeDimScaling?.() || 1)
- .transformDirection(delta[0], delta[1]);
- const fullWidth = NumCast(this.layoutDoc._width);
- const mapWidth = fullWidth - this.sidebarWidth();
- if (this.sidebarWidth() + localDelta[0] > 0) {
- this._showSidebar = true;
- this.layoutDoc._width = fullWidth + localDelta[0];
- this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
- } else {
- this._showSidebar = false;
- this.layoutDoc._width = mapWidth;
- this.layoutDoc._layout_sidebarWidthPercent = '0%';
- }
- return false;
- }),
- emptyFunction,
- () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map')
- );
- };
-
- sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
- @computed get layout_sidebarWidthPercent() {
- return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
- }
- @computed get sidebarColor() {
- return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
- }
-
- /**
- * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted;
- * add a customized temporary marker on the map
- */
- @action
- private handlePlaceChanged = () => {
- const place = this.searchBox.getPlace();
-
- if (!place.geometry || !place.geometry.location) {
- // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed
- window.alert("No details available for input: '" + place.name + "'");
- return;
- }
-
- // zoom in on the location of the search result
- if (place.geometry.viewport) {
- this._map.fitBounds(place.geometry.viewport);
- } else {
- this._map.setCenter(place.geometry.location);
- this._map.setZoom(17);
- }
-
- // customize icon => customized icon for the nature of the location selected
- const icon = {
- url: place.icon as string,
- size: new google.maps.Size(71, 71),
- origin: new google.maps.Point(0, 0),
- anchor: new google.maps.Point(17, 34),
- scaledSize: new google.maps.Size(25, 25),
- };
-
- // put temporary cutomized marker on searched location
- this.searchMarkers.forEach(marker => {
- marker.setMap(null);
- });
- this.searchMarkers = [];
- this.searchMarkers.push(
- new window.google.maps.Marker({
- map: this._map,
- icon,
- title: place.name,
- position: place.geometry.location,
- })
- );
- };
-
- /**
- * Handles toggle of sidebar on click the little comment button
- */
- @computed get sidebarHandle() {
- return (
- <div
- className="MapBox2-overlayButton-sidebar"
- key="sidebar"
- title="Toggle Sidebar"
- style={{
- display: !this.props.isContentActive() ? 'none' : undefined,
- top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5,
- backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
- }}
- onPointerDown={this.sidebarBtnDown}>
- <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
- </div>
- );
- }
-
- // TODO: Adding highlight box layer to Maps
- @action
- toggleSidebar = () => {
- //1.2 * w * ? = .2 * w .2/1.2
- const prevWidth = this.sidebarWidth();
- this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
- this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
- };
-
- sidebarDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
- };
- sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
- const bounds = this._ref.current!.getBoundingClientRect();
- this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
- this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
- e.preventDefault();
- return false;
- };
-
- setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
-
- addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
- return this.addDocument(doc, annotationKey);
- };
-
- pointerEvents = () => {
- return this.props.isContentActive() === false ? 'none' : this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none';
- };
- @computed get annotationLayer() {
- return (
- <div className="MapBox2-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
- {this.inlineTextAnnotations
- .sort((a, b) => NumCast(a.y) - NumCast(b.y))
- .map(anno => (
- <Annotation key={`${anno[Id]}-annotation`} {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />
- ))}
- </div>
- );
- }
-
- getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.Document;
-
- /**
- * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
- * @returns
- */
- private renderMarkers = () => {
- return this.allMapMarkers.map(place => (
- <Marker key={place[Id]} position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }} onLoad={marker => this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} />
- ));
- };
-
- // TODO: auto center on select a document in the sidebar
- private handleMapCenter = (map: google.maps.Map) => {
- // console.log("print the selected views in selectionManager:")
- // if (SelectionManager.Views().lastElement()) {
- // console.log(SelectionManager.Views().lastElement());
- // }
- };
-
- panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
- panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
- scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
- transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
- opaqueFilter = () => [...this.props.childFilters(), Utils.OpaqueBackgroundFilter];
- infoWidth = () => this.props.PanelWidth() / 5;
- infoHeight = () => this.props.PanelHeight() / 5;
- anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
- savedAnnotations = () => this._savedAnnotations;
-
- get MicrosoftMaps() {
- return (window as any).Microsoft.Maps;
- }
- render() {
- const renderAnnotations = (childFilters?: () => string[]) => null;
- return (
- <div className="MapBox2" ref={this._ref}>
- <div
- className="MapBox2-wrapper"
- onWheel={e => e.stopPropagation()}
- onPointerDown={async e => {
- e.button === 0 && !e.ctrlKey && e.stopPropagation();
- }}
- style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
- <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
- {renderAnnotations(this.opaqueFilter)}
- {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
- {this.annotationLayer}
-
- <div>
- <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
- <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
- <input className="MapBox2-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
- </Autocomplete>
-
- {this.renderMarkers()}
- {this.allMapMarkers
- .filter(marker => marker.infoWindowOpen)
- .map(marker => (
- <MapBoxInfoWindow
- key={marker[Id]}
- {...this.props}
- setContentView={emptyFunction}
- place={marker}
- markerMap={this.markerMap}
- PanelWidth={this.infoWidth}
- PanelHeight={this.infoHeight}
- moveDocument={this.moveDocument}
- isAnyChildContentActive={this.isAnyChildContentActive}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- />
- ))}
- {/* {this.handleMapCenter(this._map)} */}
- </GoogleMap>
- </div>
- </div>
- {/* </LoadScript > */}
- <div className="MapBox2-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
- <SidebarAnnos
- ref={this._sidebarRef}
- {...this.props}
- fieldKey={this.fieldKey}
- Document={this.Document}
- layoutDoc={this.layoutDoc}
- dataDoc={this.dataDoc}
- usePanelWidth={true}
- showSidebar={this.SidebarShown}
- nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- PanelWidth={this.sidebarWidth}
- sidebarAddDocument={this.sidebarAddDocument}
- moveDocument={this.moveDocument}
- removeDocument={this.sidebarRemoveDocument}
- />
- </div>
- {this.sidebarHandle}
- </div>
- );
- }
-}
+// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+// import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
+// import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
+// import { observer } from 'mobx-react';
+// import * as React from 'react';
+// import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
+// import { Id } from '../../../../fields/FieldSymbols';
+// import { NumCast, StrCast } from '../../../../fields/Types';
+// import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
+// import { Docs } from '../../../documents/Documents';
+// import { DragManager } from '../../../util/DragManager';
+// import { SnappingManager } from '../../../util/SnappingManager';
+// import { UndoManager } from '../../../util/UndoManager';
+// import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
+// import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
+// import { Colors } from '../../global/globalEnums';
+// import { AnchorMenu } from '../../pdf/AnchorMenu';
+// import { Annotation } from '../../pdf/Annotation';
+// import { SidebarAnnos } from '../../SidebarAnnos';
+// import { FieldView, FieldViewProps } from '../FieldView';
+// import { PinProps } from '../trails';
+// import './MapBox2.scss';
+// import { MapBoxInfoWindow } from './MapBoxInfoWindow';
+
+// /**
+// * MapBox2 architecture:
+// * Main component: MapBox2.tsx
+// * Supporting Components: SidebarAnnos, CollectionStackingView
+// *
+// * MapBox2 is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content.
+// * The main body of MapBox2 uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view.
+// * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available,
+// * sidebarAddDocument function checks if the document contains lat & lng information, if it does, then the document is added to both the sidebar and the infowindow (a pop up corresponding to a map marker--pin on map).
+// * The lat and lng field of the document is filled when importing (spec see ConvertDMSToDD method and processFileUpload method in Documents.ts).
+// * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
+// */
+
+// // const _global = (window /* browser */ || global /* node */) as any;
+
+// const mapContainerStyle = {
+// height: '100%',
+// };
+
+// const defaultCenter = {
+// lat: 42.360081,
+// lng: -71.058884,
+// };
+
+// const mapOptions = {
+// fullscreenControl: false,
+// };
+
+// const apiKey = process.env.GOOGLE_MAPS;
+
+// const script = document.createElement('script');
+// script.defer = true;
+// script.async = true;
+// script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`;
+// console.log(script.src);
+// document.head.appendChild(script);
+
+// /**
+// * Consider integrating later: allows for drawing, circling, making shapes on map
+// */
+// // const drawingManager = new window.google.maps.drawing.DrawingManager({
+// // drawingControl: true,
+// // drawingControlOptions: {
+// // position: google.maps.ControlPosition.TOP_RIGHT,
+// // drawingModes: [
+// // google.maps.drawing.OverlayType.MARKER,
+// // // currently we are not supporting the following drawing mode on map, a thought for future development
+// // google.maps.drawing.OverlayType.CIRCLE,
+// // google.maps.drawing.OverlayType.POLYLINE,
+// // ],
+// // },
+// // });
+
+// // options for searchbox in Google Maps Places Autocomplete API
+// const options = {
+// fields: ['formatted_address', 'geometry', 'name'], // note: level of details is charged by item per retrieval, not recommended to return all fields
+// strictBounds: false,
+// types: ['establishment'], // type pf places, subject of change according to user need
+// } as google.maps.places.AutocompleteOptions;
+
+// @observer
+// export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
+// private _dropDisposer?: DragManager.DragDropDisposer;
+// private _disposers: { [name: string]: IReactionDisposer } = {};
+// private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+// @observable private _overlayAnnoInfo: Opt<Doc>;
+// showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
+// public static LayoutString(fieldKey: string) {
+// return FieldView.LayoutString(MapBox2, fieldKey);
+// }
+// public get SidebarKey() {
+// return this.fieldKey + '_sidebar';
+// }
+// private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
+// @computed get inlineTextAnnotations() {
+// return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
+// }
+
+// @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
+// @observable private selectedPlace: Doc | undefined;
+// @observable private markerMap: { [id: string]: google.maps.Marker } = {};
+// @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter;
+// @observable private inputRef = React.createRef<HTMLInputElement>();
+// @observable private searchMarkers: google.maps.Marker[] = [];
+// @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
+// @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
+// @computed get allSidebarDocs() {
+// return DocListCast(this.dataDoc[this.SidebarKey]);
+// }
+// @computed get allMapMarkers() {
+// return DocListCast(this.dataDoc[this.annotationKey]);
+// }
+// @observable private toggleAddMarker = false;
+
+// @observable _showSidebar = false;
+// @computed get SidebarShown() {
+// return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
+// }
+
+// static _canAnnotate = true;
+// static _hadSelection: boolean = false;
+// private _sidebarRef = React.createRef<SidebarAnnos>();
+// private _ref: React.RefObject<HTMLDivElement> = React.createRef();
+
+// componentDidMount() {
+// this.props.setContentView?.(this);
+// }
+
+// @action
+// private setSearchBox = (searchBox: any) => {
+// this.searchBox = searchBox;
+// };
+
+// // iterate allMarkers to size, center, and zoom map to contain all markers
+// private fitBounds = (map: google.maps.Map) => {
+// const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds();
+// const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean);
+// !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds()));
+// };
+
+// /**
+// * Custom control for add marker button
+// * @param controlDiv
+// * @param map
+// */
+// private CenterControl = () => {
+// const controlDiv = document.createElement('div');
+// controlDiv.className = 'MapBox2-addMarker';
+// // Set CSS for the control border.
+// const controlUI = document.createElement('div');
+// controlUI.style.backgroundColor = '#fff';
+// controlUI.style.borderRadius = '3px';
+// controlUI.style.cursor = 'pointer';
+// controlUI.style.marginTop = '10px';
+// controlUI.style.borderRadius = '4px';
+// controlUI.style.marginBottom = '22px';
+// controlUI.style.textAlign = 'center';
+// controlUI.style.position = 'absolute';
+// controlUI.style.width = '32px';
+// controlUI.style.height = '32px';
+// controlUI.title = 'Click to toggle marker mode. In marker mode, click on map to place a marker.';
+
+// const plIcon = document.createElement('img');
+// plIcon.src = 'https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png';
+// plIcon.style.color = 'rgb(25,25,25)';
+// plIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
+// plIcon.style.fontSize = '16px';
+// plIcon.style.lineHeight = '32px';
+// plIcon.style.left = '18';
+// plIcon.style.top = '15';
+// plIcon.style.position = 'absolute';
+// plIcon.width = 14;
+// plIcon.height = 14;
+// plIcon.innerHTML = 'Add';
+// controlUI.appendChild(plIcon);
+
+// // Set CSS for the control interior.
+// const markerIcon = document.createElement('img');
+// markerIcon.src = 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png';
+// markerIcon.style.color = 'rgb(25,25,25)';
+// markerIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
+// markerIcon.style.fontSize = '16px';
+// markerIcon.style.lineHeight = '32px';
+// markerIcon.style.left = '-2';
+// markerIcon.style.top = '1';
+// markerIcon.width = 30;
+// markerIcon.height = 30;
+// markerIcon.style.position = 'absolute';
+// markerIcon.innerHTML = 'Add';
+// controlUI.appendChild(markerIcon);
+
+// // Setup the click event listeners
+// controlUI.addEventListener('click', () => {
+// if (this.toggleAddMarker === true) {
+// this.toggleAddMarker = false;
+// console.log('add marker button status:' + this.toggleAddMarker);
+// controlUI.style.backgroundColor = '#fff';
+// markerIcon.style.color = 'rgb(25,25,25)';
+// } else {
+// this.toggleAddMarker = true;
+// console.log('add marker button status:' + this.toggleAddMarker);
+// controlUI.style.backgroundColor = '#4476f7';
+// markerIcon.style.color = 'rgb(255,255,255)';
+// }
+// });
+// controlDiv.appendChild(controlUI);
+// return controlDiv;
+// };
+
+// /**
+// * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list
+// * @param position - the LatLng position where the marker is placed
+// * @param map
+// */
+// @action
+// private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
+// const marker = new google.maps.Marker({
+// position: position,
+// map: map,
+// });
+// map.panTo(position);
+// const mapMarker = Docs.Create.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
+// this.addDocument(mapMarker, this.annotationKey);
+// };
+
+// _loadPending = true;
+// /**
+// * store a reference to google map instance
+// * setup the drawing manager on the top right corner of map
+// * fit map bounds to contain all markers
+// * @param map
+// */
+// @action
+// private loadHandler = (map: google.maps.Map) => {
+// this._map = map;
+// this._loadPending = true;
+// const centerControlDiv = this.CenterControl();
+// map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv);
+// //drawingManager.setMap(map);
+// // if (navigator.geolocation) {
+// // navigator.geolocation.getCurrentPosition(
+// // (position: Position) => {
+// // const pos = {
+// // lat: position.coords.latitude,
+// // lng: position.coords.longitude,
+// // };
+// // this._map.setCenter(pos);
+// // }
+// // );
+// // } else {
+// // alert("Your geolocation is not supported by browser.")
+// // };
+// map.setZoom(NumCast(this.dataDoc.map_zoom, 2.5));
+// map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng)));
+// setTimeout(() => {
+// if (this._loadPending && this._map.getBounds()) {
+// this._loadPending = false;
+// this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
+// }
+// }, 250);
+// // listener to addmarker event
+// this._map.addListener('click', (e: MouseEvent) => {
+// if (this.toggleAddMarker === true) {
+// this.placeMarker((e as any).latLng, map);
+// }
+// });
+// };
+
+// @action
+// centered = () => {
+// if (this._loadPending && this._map.getBounds()) {
+// this._loadPending = false;
+// this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
+// }
+// this.dataDoc.mapLat = this._map.getCenter()?.lat();
+// this.dataDoc.mapLng = this._map.getCenter()?.lng();
+// };
+
+// @action
+// zoomChanged = () => {
+// if (this._loadPending && this._map.getBounds()) {
+// this._loadPending = false;
+// this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
+// }
+// this.dataDoc.map_zoom = this._map.getZoom();
+// };
+
+// /**
+// * Load and render all map markers
+// * @param marker
+// * @param place
+// */
+// @action
+// private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
+// place[Id] ? (this.markerMap[place[Id]] = marker) : null;
+// };
+
+// /**
+// * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true
+// * @param e
+// * @param place
+// */
+// @action
+// private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
+// // set which place was clicked
+// this.selectedPlace = place;
+// place.infoWindowOpen = true;
+// };
+
+// /**
+// * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts
+// * @param doc
+// * @param sidebarKey
+// * @returns
+// */
+// sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
+// console.log('print all sidebar Docs');
+// if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
+// const docs = doc instanceof Doc ? [doc] : doc;
+// docs.forEach(doc => {
+// if (doc.lat !== undefined && doc.lng !== undefined) {
+// const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng);
+// if (existingMarker) {
+// Doc.AddDocToList(existingMarker, 'data', doc);
+// } else {
+// const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
+// this.addDocument(marker, this.annotationKey);
+// }
+// }
+// }); //add to annotation list
+
+// return this.addDocument(doc, sidebarKey); // add to sidebar list
+// };
+
+// /**
+// * Removing documents from the sidebar
+// * @param doc
+// * @param sidebarKey
+// * @returns
+// */
+// sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
+// if (this.layoutDoc._layout_showSidebar) this.toggleSidebar();
+// const docs = doc instanceof Doc ? [doc] : doc;
+// return this.removeDocument(doc, sidebarKey);
+// };
+
+// /**
+// * Toggle sidebar onclick the tiny comment button on the top right corner
+// * @param e
+// */
+// sidebarBtnDown = (e: React.PointerEvent) => {
+// setupMoveUpEvents(
+// this,
+// e,
+// (e, down, delta) =>
+// runInAction(() => {
+// const localDelta = this.props
+// .ScreenToLocalTransform()
+// .scale(this.props.NativeDimScaling?.() || 1)
+// .transformDirection(delta[0], delta[1]);
+// const fullWidth = NumCast(this.layoutDoc._width);
+// const mapWidth = fullWidth - this.sidebarWidth();
+// if (this.sidebarWidth() + localDelta[0] > 0) {
+// this._showSidebar = true;
+// this.layoutDoc._width = fullWidth + localDelta[0];
+// this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
+// } else {
+// this._showSidebar = false;
+// this.layoutDoc._width = mapWidth;
+// this.layoutDoc._layout_sidebarWidthPercent = '0%';
+// }
+// return false;
+// }),
+// emptyFunction,
+// () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map')
+// );
+// };
+
+// sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+// @computed get layout_sidebarWidthPercent() {
+// return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
+// }
+// @computed get sidebarColor() {
+// return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
+// }
+
+// /**
+// * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted;
+// * add a customized temporary marker on the map
+// */
+// @action
+// private handlePlaceChanged = () => {
+// const place = this.searchBox.getPlace();
+
+// if (!place.geometry || !place.geometry.location) {
+// // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed
+// window.alert("No details available for input: '" + place.name + "'");
+// return;
+// }
+
+// // zoom in on the location of the search result
+// if (place.geometry.viewport) {
+// this._map.fitBounds(place.geometry.viewport);
+// } else {
+// this._map.setCenter(place.geometry.location);
+// this._map.setZoom(17);
+// }
+
+// // customize icon => customized icon for the nature of the location selected
+// const icon = {
+// url: place.icon as string,
+// size: new google.maps.Size(71, 71),
+// origin: new google.maps.Point(0, 0),
+// anchor: new google.maps.Point(17, 34),
+// scaledSize: new google.maps.Size(25, 25),
+// };
+
+// // put temporary cutomized marker on searched location
+// this.searchMarkers.forEach(marker => {
+// marker.setMap(null);
+// });
+// this.searchMarkers = [];
+// this.searchMarkers.push(
+// new window.google.maps.Marker({
+// map: this._map,
+// icon,
+// title: place.name,
+// position: place.geometry.location,
+// })
+// );
+// };
+
+// /**
+// * Handles toggle of sidebar on click the little comment button
+// */
+// @computed get sidebarHandle() {
+// return (
+// <div
+// className="MapBox2-overlayButton-sidebar"
+// key="sidebar"
+// title="Toggle Sidebar"
+// style={{
+// display: !this.props.isContentActive() ? 'none' : undefined,
+// top: StrCast(this.layoutDoc._layout_showTitle) === 'title' ? 20 : 5,
+// backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
+// }}
+// onPointerDown={this.sidebarBtnDown}>
+// <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
+// </div>
+// );
+// }
+
+// // TODO: Adding highlight box layer to Maps
+// @action
+// toggleSidebar = () => {
+// //1.2 * w * ? = .2 * w .2/1.2
+// const prevWidth = this.sidebarWidth();
+// this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
+// this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+// };
+
+// sidebarDown = (e: React.PointerEvent) => {
+// setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
+// };
+// sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
+// const bounds = this._ref.current!.getBoundingClientRect();
+// this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
+// this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
+// e.preventDefault();
+// return false;
+// };
+
+// setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
+
+// addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
+// return this.addDocument(doc, annotationKey);
+// };
+
+// pointerEvents = () => {
+// return this.props.isContentActive() === false ? 'none' : this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none';
+// };
+// @computed get annotationLayer() {
+// return (
+// <div className="MapBox2-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
+// {this.inlineTextAnnotations
+// .sort((a, b) => NumCast(a.y) - NumCast(b.y))
+// .map(anno => (
+// <Annotation key={`${anno[Id]}-annotation`} {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />
+// ))}
+// </div>
+// );
+// }
+
+// getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.Document;
+
+// /**
+// * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
+// * @returns
+// */
+// private renderMarkers = () => {
+// return this.allMapMarkers.map(place => (
+// <Marker key={place[Id]} position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }} onLoad={marker => this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} />
+// ));
+// };
+
+// // TODO: auto center on select a document in the sidebar
+// private handleMapCenter = (map: google.maps.Map) => {
+// // console.log("print the selected views in selectionManager:")
+// // if (SelectionManager.Views().lastElement()) {
+// // console.log(SelectionManager.Views().lastElement());
+// // }
+// };
+
+// panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
+// panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
+// scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
+// transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
+// opaqueFilter = () => [...this.props.childFilters(), Utils.OpaqueBackgroundFilter];
+// infoWidth = () => this.props.PanelWidth() / 5;
+// infoHeight = () => this.props.PanelHeight() / 5;
+// anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
+// savedAnnotations = () => this._savedAnnotations;
+
+// get MicrosoftMaps() {
+// return (window as any).Microsoft.Maps;
+// }
+// render() {
+// const renderAnnotations = (childFilters?: () => string[]) => null;
+// return (
+// <div className="MapBox2" ref={this._ref}>
+// <div
+// className="MapBox2-wrapper"
+// onWheel={e => e.stopPropagation()}
+// onPointerDown={async e => {
+// e.button === 0 && !e.ctrlKey && e.stopPropagation();
+// }}
+// style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
+// <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
+// {renderAnnotations(this.opaqueFilter)}
+// {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
+// {this.annotationLayer}
+
+// <div>
+// <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
+// <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
+// <input className="MapBox2-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
+// </Autocomplete>
+
+// {this.renderMarkers()}
+// {this.allMapMarkers
+// .filter(marker => marker.infoWindowOpen)
+// .map(marker => (
+// <MapBoxInfoWindow
+// key={marker[Id]}
+// {...this.props}
+// setContentView={emptyFunction}
+// place={marker}
+// markerMap={this.markerMap}
+// PanelWidth={this.infoWidth}
+// PanelHeight={this.infoHeight}
+// moveDocument={this.moveDocument}
+// isAnyChildContentActive={this.isAnyChildContentActive}
+// whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+// />
+// ))}
+// {/* {this.handleMapCenter(this._map)} */}
+// </GoogleMap>
+// </div>
+// </div>
+// {/* </LoadScript > */}
+// <div className="MapBox2-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+// <SidebarAnnos
+// ref={this._sidebarRef}
+// {...this.props}
+// fieldKey={this.fieldKey}
+// Document={this.Document}
+// layoutDoc={this.layoutDoc}
+// dataDoc={this.dataDoc}
+// usePanelWidth={true}
+// showSidebar={this.SidebarShown}
+// nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
+// whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+// PanelWidth={this.sidebarWidth}
+// sidebarAddDocument={this.sidebarAddDocument}
+// moveDocument={this.moveDocument}
+// removeDocument={this.sidebarRemoveDocument}
+// />
+// </div>
+// {this.sidebarHandle}
+// </div>
+// );
+// }
+// }
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index 415a9d776..a9c6ba22c 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -1,95 +1,95 @@
-import { InfoWindow } from '@react-google-maps/api';
-import { action } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
-import { emptyFunction, returnAll, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents } from '../../../../Utils';
-import { Docs } from '../../../documents/Documents';
-import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
-import { CollectionNoteTakingView } from '../../collections/CollectionNoteTakingView';
-import { CollectionStackingView } from '../../collections/CollectionStackingView';
-import { ViewBoxAnnotatableProps } from '../../DocComponent';
-import { FieldViewProps } from '../FieldView';
-import { FormattedTextBox } from '../formattedText/FormattedTextBox';
-import './MapBox.scss';
+// import { InfoWindow } from '@react-google-maps/api';
+// import { action } from 'mobx';
+// import { observer } from 'mobx-react';
+// import * as React from 'react';
+// import { Doc } from '../../../../fields/Doc';
+// import { Id } from '../../../../fields/FieldSymbols';
+// import { emptyFunction, returnAll, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents } from '../../../../Utils';
+// import { Docs } from '../../../documents/Documents';
+// import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
+// import { CollectionNoteTakingView } from '../../collections/CollectionNoteTakingView';
+// import { CollectionStackingView } from '../../collections/CollectionStackingView';
+// import { ViewBoxAnnotatableProps } from '../../DocComponent';
+// import { FieldViewProps } from '../FieldView';
+// import { FormattedTextBox } from '../formattedText/FormattedTextBox';
+// import './MapBox.scss';
-interface MapBoxInfoWindowProps {
- place: Doc;
- renderDepth: number;
- markerMap: { [id: string]: google.maps.Marker };
- isAnyChildContentActive: () => boolean;
-}
-@observer
-export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & ViewBoxAnnotatableProps & FieldViewProps> {
- @action
- private handleInfoWindowClose = () => {
- if (this.props.place.infoWindowOpen) {
- this.props.place.infoWindowOpen = false;
- }
- this.props.place.infoWindowOpen = false;
- };
+// interface MapBoxInfoWindowProps {
+// place: Doc;
+// renderDepth: number;
+// markerMap: { [id: string]: google.maps.Marker };
+// isAnyChildContentActive: () => boolean;
+// }
+// @observer
+// export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & ViewBoxAnnotatableProps & FieldViewProps> {
+// @action
+// private handleInfoWindowClose = () => {
+// if (this.props.place.infoWindowOpen) {
+// this.props.place.infoWindowOpen = false;
+// }
+// this.props.place.infoWindowOpen = false;
+// };
- addNoteClick = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => {
- const newDoc = Docs.Create.TextDocument('Note', { _layout_autoHeight: true });
- FormattedTextBox.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
- Doc.AddDocToList(this.props.place, 'data', newDoc);
- this._stack?.scrollToBottom();
- e.stopPropagation();
- e.preventDefault();
- });
- };
+// addNoteClick = (e: React.PointerEvent) => {
+// setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => {
+// const newDoc = Docs.Create.TextDocument('Note', { _layout_autoHeight: true });
+// FormattedTextBox.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+// Doc.AddDocToList(this.props.place, 'data', newDoc);
+// this._stack?.scrollToBottom();
+// e.stopPropagation();
+// e.preventDefault();
+// });
+// };
- _stack: CollectionStackingView | CollectionNoteTakingView | null | undefined;
- childLayoutFitWidth = (doc: Doc) => doc.type === DocumentType.RTF;
- addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, 'data', d), true as boolean);
- removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean);
- render() {
- return (
- <InfoWindow
- // anchor={this.props.markerMap[this.props.place[Id]]}
- onCloseClick={this.handleInfoWindowClose}>
- <div className="mapbox-infowindow">
- <div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
- <CollectionStackingView
- ref={r => (this._stack = r)}
- {...this.props}
- setContentView={emptyFunction}
- Document={this.props.place}
- TemplateDataDocument={undefined}
- fieldKey="data"
- NativeWidth={returnZero}
- NativeHeight={returnZero}
- childFilters={returnEmptyFilter}
- setHeight={emptyFunction}
- isAnnotationOverlay={false}
- select={emptyFunction}
- NativeDimScaling={returnOne}
- isContentActive={returnTrue}
- chromeHidden={true}
- childHideResizeHandles={true}
- childHideDecorationTitle={true}
- childLayoutFitWidth={this.childLayoutFitWidth}
- // childDocumentsActive={returnFalse}
- removeDocument={this.removeDoc}
- addDocument={this.addDoc}
- renderDepth={this.props.renderDepth + 1}
- type_collection={CollectionViewType.Stacking}
- pointerEvents={returnAll}
- />
- </div>
- <hr />
- <div
- onPointerDown={this.addNoteClick}
- onClick={e => {
- e.stopPropagation();
- e.preventDefault();
- }}>
- Add Note
- </div>
- </div>
- </InfoWindow>
- );
- }
-}
+// _stack: CollectionStackingView | CollectionNoteTakingView | null | undefined;
+// childLayoutFitWidth = (doc: Doc) => doc.type === DocumentType.RTF;
+// addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, 'data', d), true as boolean);
+// removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean);
+// render() {
+// return (
+// <InfoWindow
+// // anchor={this.props.markerMap[this.props.place[Id]]}
+// onCloseClick={this.handleInfoWindowClose}>
+// <div className="mapbox-infowindow">
+// <div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
+// <CollectionStackingView
+// ref={r => (this._stack = r)}
+// {...this.props}
+// setContentView={emptyFunction}
+// Document={this.props.place}
+// TemplateDataDocument={undefined}
+// fieldKey="data"
+// NativeWidth={returnZero}
+// NativeHeight={returnZero}
+// childFilters={returnEmptyFilter}
+// setHeight={emptyFunction}
+// isAnnotationOverlay={false}
+// select={emptyFunction}
+// NativeDimScaling={returnOne}
+// isContentActive={returnTrue}
+// chromeHidden={true}
+// childHideResizeHandles={true}
+// childHideDecorationTitle={true}
+// childLayoutFitWidth={this.childLayoutFitWidth}
+// // childDocumentsActive={returnFalse}
+// removeDocument={this.removeDoc}
+// addDocument={this.addDoc}
+// renderDepth={this.props.renderDepth + 1}
+// type_collection={CollectionViewType.Stacking}
+// pointerEvents={returnAll}
+// />
+// </div>
+// <hr />
+// <div
+// onPointerDown={this.addNoteClick}
+// onClick={e => {
+// e.stopPropagation();
+// e.preventDefault();
+// }}>
+// Add Note
+// </div>
+// </div>
+// </InfoWindow>
+// );
+// }
+// }
diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
index 56f0a49b8..590f8735c 100644
--- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx
+++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx
@@ -2,7 +2,7 @@ import { observer } from 'mobx-react';
// import { SettingsManager } from '../../../util/SettingsManager';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { FieldView, FieldViewProps } from '../FieldView';
-import React = require('react');
+import * as React from 'react';
import { computed } from 'mobx';
import { MapBox } from './MapBox';
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 9787de0ab..8de8498d7 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -31,7 +31,7 @@ import { FieldView, FieldViewProps } from './FieldView';
import { ImageBox } from './ImageBox';
import './PDFBox.scss';
import { PinProps, PresBox } from './trails';
-import React = require('react');
+import * as React from 'react';
@observer
export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
index cd1ff17dd..c498a58d6 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx
@@ -15,11 +15,11 @@ import { ViewBoxAnnotatableComponent } from '../../DocComponent';
import { FieldView, FieldViewProps } from './../FieldView';
import './PhysicsSimulationBox.scss';
import InputField from './PhysicsSimulationInputField';
-import * as questions from './PhysicsSimulationQuestions.json';
-import * as tutorials from './PhysicsSimulationTutorial.json';
+import questions from './PhysicsSimulationQuestions.json';
+import tutorials from './PhysicsSimulationTutorial.json';
import Wall from './PhysicsSimulationWall';
import Weight from './PhysicsSimulationWeight';
-import React = require('react');
+import * as React from 'react';
interface IWallProps {
length: number;
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx
index d595a499e..c704863f5 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationInputField.tsx
@@ -1,6 +1,6 @@
import { TextField, InputAdornment } from '@mui/material';
import { Doc } from '../../../../fields/Doc';
-import React = require('react');
+import * as React from 'react';
import TaskAltIcon from '@mui/icons-material/TaskAlt';
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
import { isNumber } from 'lodash';
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx
index 8cc1d0fbf..696352296 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWall.tsx
@@ -1,34 +1,33 @@
-import React = require('react');
+import * as React from 'react';
export interface Force {
- magnitude: number;
- directionInDegrees: number;
+ magnitude: number;
+ directionInDegrees: number;
}
export interface IWallProps {
- length: number;
- xPos: number;
- yPos: number;
- angleInDegrees: number;
+ length: number;
+ xPos: number;
+ yPos: number;
+ angleInDegrees: number;
}
export default class Wall extends React.Component<IWallProps> {
+ constructor(props: any) {
+ super(props);
+ }
- constructor(props: any) {
- super(props)
- }
+ wallStyle = {
+ width: this.props.angleInDegrees == 0 ? this.props.length + '%' : '5px',
+ height: this.props.angleInDegrees == 0 ? '5px' : this.props.length + '%',
+ position: 'absolute' as 'absolute',
+ left: this.props.xPos + '%',
+ top: this.props.yPos + '%',
+ backgroundColor: '#6c7b8b',
+ margin: 0,
+ padding: 0,
+ };
- wallStyle = {
- width: this.props.angleInDegrees == 0 ? this.props.length + "%" : "5px",
- height: this.props.angleInDegrees == 0 ? "5px" : this.props.length + "%",
- position: "absolute" as "absolute",
- left: this.props.xPos + "%",
- top: this.props.yPos + "%",
- backgroundColor: "#6c7b8b",
- margin: 0,
- padding: 0,
- };
-
- render () {
- return (<div style={this.wallStyle}></div>);
- }
-};
+ render() {
+ return <div style={this.wallStyle}></div>;
+ }
+}
diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
index 2165c8ba9..99333991f 100644
--- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
+++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationWeight.tsx
@@ -1,7 +1,7 @@
import { computed, IReactionDisposer, reaction } from 'mobx';
import { observer } from 'mobx-react';
import './PhysicsSimulationBox.scss';
-import React = require('react');
+import * as React from 'react';
interface IWallProps {
length: number;
diff --git a/src/client/views/nodes/RadialMenu.tsx b/src/client/views/nodes/RadialMenu.tsx
index 7f0956e51..191877cb5 100644
--- a/src/client/views/nodes/RadialMenu.tsx
+++ b/src/client/views/nodes/RadialMenu.tsx
@@ -1,8 +1,8 @@
-import React = require("react");
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import "./RadialMenu.scss";
-import { RadialMenuItem, RadialMenuProps } from "./RadialMenuItem";
+import * as React from 'react';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import './RadialMenu.scss';
+import { RadialMenuItem, RadialMenuProps } from './RadialMenuItem';
@observer
export class RadialMenu extends React.Component {
@@ -23,11 +23,10 @@ export class RadialMenu extends React.Component {
public used: boolean = false;
-
catchTouch = (te: React.TouchEvent) => {
te.stopPropagation();
te.preventDefault();
- }
+ };
@action
onPointerDown = (e: PointerEvent) => {
@@ -35,8 +34,8 @@ export class RadialMenu extends React.Component {
this._mouseX = e.clientX;
this._mouseY = e.clientY;
this.used = false;
- document.addEventListener("pointermove", this.onPointerMove);
- }
+ document.addEventListener('pointermove', this.onPointerMove);
+ };
@observable
private _closest: number = -1;
@@ -60,11 +59,10 @@ export class RadialMenu extends React.Component {
}
}
this._closest = closest;
- }
- else {
+ } else {
this._closest = -1;
}
- }
+ };
@action
onPointerUp = (e: PointerEvent) => {
this.used = true;
@@ -75,43 +73,41 @@ export class RadialMenu extends React.Component {
this._shouldDisplay = false;
}
this._shouldDisplay && (this._display = true);
- document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener('pointermove', this.onPointerMove);
if (this._closest !== -1 && this._items?.length > this._closest) {
this._items[this._closest].event();
}
- }
+ };
componentWillUnmount() {
- document.removeEventListener("pointerdown", this.onPointerDown);
+ document.removeEventListener('pointerdown', this.onPointerDown);
- document.removeEventListener("pointerup", this.onPointerUp);
+ document.removeEventListener('pointerup', this.onPointerUp);
this._reactionDisposer && this._reactionDisposer();
}
@action
componentDidMount = () => {
- document.addEventListener("pointerdown", this.onPointerDown);
- document.addEventListener("pointerup", this.onPointerUp);
+ document.addEventListener('pointerdown', this.onPointerDown);
+ document.addEventListener('pointerup', this.onPointerUp);
this.previewcircle();
this._reactionDisposer = reaction(
() => this._shouldDisplay,
- () => this._shouldDisplay && !this._mouseDown && runInAction(() => this._display = true)
+ () => this._shouldDisplay && !this._mouseDown && runInAction(() => (this._display = true))
);
- }
+ };
componentDidUpdate = () => {
this.previewcircle();
- }
+ };
@observable private _pageX: number = 0;
@observable private _pageY: number = 0;
@observable _display: boolean = false;
@observable private _yRelativeToTop: boolean = true;
-
@observable private _width: number = 0;
@observable private _height: number = 0;
-
getItems() {
return this._items;
}
@@ -133,7 +129,7 @@ export class RadialMenu extends React.Component {
this._mouseX = x;
this._mouseY = y;
this._shouldDisplay = true;
- }
+ };
// @computed
// get pageX() {
// const x = this._pageX;
@@ -168,7 +164,7 @@ export class RadialMenu extends React.Component {
this.clearItems();
this._display = false;
this._shouldDisplay = false;
- }
+ };
@action
openMenu = (x: number, y: number) => {
@@ -176,56 +172,52 @@ export class RadialMenu extends React.Component {
this._pageY = y;
this._shouldDisplay;
this._display = true;
- }
+ };
@action
clearItems() {
this._items = [];
}
-
previewcircle() {
- if (document.getElementById("newCanvas") !== null) {
- const c: any = document.getElementById("newCanvas");
+ if (document.getElementById('newCanvas') !== null) {
+ const c: any = document.getElementById('newCanvas');
if (c.getContext) {
- const ctx = c.getContext("2d");
+ const ctx = c.getContext('2d');
ctx.beginPath();
ctx.arc(150, 150, 50, 0, 2 * Math.PI);
- ctx.fillStyle = "white";
+ ctx.fillStyle = 'white';
ctx.fill();
- ctx.font = "12px Arial";
- ctx.fillStyle = "black";
- ctx.textAlign = "center";
- let description = "";
+ ctx.font = '12px Arial';
+ ctx.fillStyle = 'black';
+ ctx.textAlign = 'center';
+ let description = '';
if (this._closest !== -1) {
description = this._items[this._closest].description;
}
if (description.length > 15) {
description = description.slice(0, 12);
- description += "...";
+ description += '...';
}
ctx.fillText(description, 150, 150, 90);
}
}
}
-
render() {
if (!this._display) {
return null;
}
- const style = this._yRelativeToTop ? { left: this._pageX - 130, top: this._pageY - 130 } :
- { left: this._pageX - 130, top: this._pageY - 130 };
+ const style = this._yRelativeToTop ? { left: this._pageX - 130, top: this._pageY - 130 } : { left: this._pageX - 130, top: this._pageY - 130 };
return (
-
<div className="radialMenu-cont" onTouchStart={this.catchTouch} style={style}>
- <canvas id="newCanvas" style={{ position: "absolute" }} height="300" width="300"> Your browser does not support the HTML5 canvas tag.</canvas>
+ <canvas id="newCanvas" style={{ position: 'absolute' }} height="300" width="300">
+ {' '}
+ Your browser does not support the HTML5 canvas tag.
+ </canvas>
{this.menuItems}
</div>
-
);
}
-
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/RadialMenuItem.tsx b/src/client/views/nodes/RadialMenuItem.tsx
index 8876b4879..c931202f1 100644
--- a/src/client/views/nodes/RadialMenuItem.tsx
+++ b/src/client/views/nodes/RadialMenuItem.tsx
@@ -1,8 +1,8 @@
-import React = require("react");
+import * as React from 'react';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { observer } from "mobx-react";
-import { UndoManager } from "../../util/UndoManager";
+import { observer } from 'mobx-react';
+import { UndoManager } from '../../util/UndoManager';
export interface RadialMenuProps {
description: string;
@@ -15,17 +15,15 @@ export interface RadialMenuProps {
selected: number;
}
-
@observer
export class RadialMenuItem extends React.Component<RadialMenuProps> {
-
componentDidMount = () => {
this.setcircle();
- }
+ };
componentDidUpdate = () => {
this.setcircle();
- }
+ };
handleEvent = async (e: React.PointerEvent) => {
this.props.closeMenu && this.props.closeMenu();
@@ -35,38 +33,36 @@ export class RadialMenuItem extends React.Component<RadialMenuProps> {
}
await this.props.event({ x: e.clientX, y: e.clientY });
batch && batch.end();
- }
-
+ };
setcircle() {
let circlemin = 0;
let circlemax = 1;
- this.props.min ? circlemin = this.props.min : null;
- this.props.max ? circlemax = this.props.max : null;
- if (document.getElementById("myCanvas") !== null) {
- const c: any = document.getElementById("myCanvas");
- let color = "white";
+ this.props.min ? (circlemin = this.props.min) : null;
+ this.props.max ? (circlemax = this.props.max) : null;
+ if (document.getElementById('myCanvas') !== null) {
+ const c: any = document.getElementById('myCanvas');
+ let color = 'white';
switch (circlemin % 3) {
case 1:
- color = "#c2c2c5";
+ color = '#c2c2c5';
break;
case 0:
- color = "#f1efeb";
+ color = '#f1efeb';
break;
case 2:
- color = "lightgray";
+ color = 'lightgray';
break;
}
if (circlemax % 3 === 1 && circlemin === circlemax - 1) {
- color = "#c2c2c5";
+ color = '#c2c2c5';
}
if (this.props.selected === this.props.min) {
- color = "#808080";
-
+ color = '#808080';
}
if (c.getContext) {
- const ctx = c.getContext("2d");
+ const ctx = c.getContext('2d');
ctx.beginPath();
ctx.arc(150, 150, 150, (circlemin / circlemax) * 2 * Math.PI, ((circlemin + 1) / circlemax) * 2 * Math.PI);
ctx.arc(150, 150, 50, ((circlemin + 1) / circlemax) * 2 * Math.PI, (circlemin / circlemax) * 2 * Math.PI, true);
@@ -79,35 +75,36 @@ export class RadialMenuItem extends React.Component<RadialMenuProps> {
calculatorx() {
let circlemin = 0;
let circlemax = 1;
- this.props.min ? circlemin = this.props.min : null;
- this.props.max ? circlemax = this.props.max : null;
- const avg = ((circlemin / circlemax) + ((circlemin + 1) / circlemax)) / 2;
+ this.props.min ? (circlemin = this.props.min) : null;
+ this.props.max ? (circlemax = this.props.max) : null;
+ const avg = (circlemin / circlemax + (circlemin + 1) / circlemax) / 2;
const degrees = 360 * avg;
- const x = 100 * Math.cos(degrees * Math.PI / 180);
- const y = -125 * Math.sin(degrees * Math.PI / 180);
+ const x = 100 * Math.cos((degrees * Math.PI) / 180);
+ const y = -125 * Math.sin((degrees * Math.PI) / 180);
return x;
}
calculatory() {
-
let circlemin = 0;
let circlemax = 1;
- this.props.min ? circlemin = this.props.min : null;
- this.props.max ? circlemax = this.props.max : null;
- const avg = ((circlemin / circlemax) + ((circlemin + 1) / circlemax)) / 2;
+ this.props.min ? (circlemin = this.props.min) : null;
+ this.props.max ? (circlemax = this.props.max) : null;
+ const avg = (circlemin / circlemax + (circlemin + 1) / circlemax) / 2;
const degrees = 360 * avg;
- const x = 125 * Math.cos(degrees * Math.PI / 180);
- const y = -100 * Math.sin(degrees * Math.PI / 180);
+ const x = 125 * Math.cos((degrees * Math.PI) / 180);
+ const y = -100 * Math.sin((degrees * Math.PI) / 180);
return y;
}
-
render() {
return (
- <div className={"radialMenu-item" + (this.props.selected ? " radialMenu-itemSelected" : "")} onPointerUp={this.handleEvent}>
- <canvas id="myCanvas" height="300" width="300"> Your browser does not support the HTML5 canvas tag.</canvas>
- <FontAwesomeIcon icon={this.props.icon} size="3x" style={{ position: "absolute", left: this.calculatorx() + 150 - 19, top: this.calculatory() + 150 - 19 }} />
+ <div className={'radialMenu-item' + (this.props.selected ? ' radialMenu-itemSelected' : '')} onPointerUp={this.handleEvent}>
+ <canvas id="myCanvas" height="300" width="300">
+ {' '}
+ Your browser does not support the HTML5 canvas tag.
+ </canvas>
+ <FontAwesomeIcon icon={this.props.icon} size="3x" style={{ position: 'absolute', left: this.calculatorx() + 150 - 19, top: this.calculatory() + 150 - 19 }} />
</div>
);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index c2e204eab..7c21b1893 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
// import { Canvas } from '@react-three/fiber';
import { computed, observable, runInAction } from 'mobx';
diff --git a/src/client/views/nodes/ScriptingBox.scss b/src/client/views/nodes/ScriptingBox.scss
index 8d76f2b1d..9789da55a 100644
--- a/src/client/views/nodes/ScriptingBox.scss
+++ b/src/client/views/nodes/ScriptingBox.scss
@@ -32,7 +32,7 @@
.scriptingBox-wrapper {
width: 100%;
height: 100%;
- max-height: calc(100%-30px);
+ max-height: calc(100% - 30px);
display: flex;
flex-direction: row;
overflow: auto;
@@ -42,7 +42,8 @@
overflow: hidden;
}
- .scriptingBox-textArea, .scriptingBox-textArea-inputs {
+ .scriptingBox-textArea,
+ .scriptingBox-textArea-inputs {
flex: 70;
height: 100%;
max-width: 95%;
@@ -110,7 +111,7 @@
cursor: pointer;
}
- .rta__entity>* {
+ .rta__entity > * {
padding-left: 4px;
padding-right: 4px;
}
@@ -125,7 +126,7 @@
.scriptingBox-textArea-inputs {
max-width: 100%;
height: 40%;
- width: 100%;
+ width: 100%;
resize: none;
}
.scriptingBox-textArea-script {
@@ -194,8 +195,8 @@
.scriptingBox-errorMessage {
overflow: auto;
- background: "red";
- background-color: "red";
+ background: 'red';
+ background-color: 'red';
height: 45px;
}
@@ -221,4 +222,4 @@
width: 25%;
}
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/TaskCompletedBox.tsx b/src/client/views/nodes/TaskCompletedBox.tsx
index 2a3dd8d2d..9aab8c5a9 100644
--- a/src/client/views/nodes/TaskCompletedBox.tsx
+++ b/src/client/views/nodes/TaskCompletedBox.tsx
@@ -1,13 +1,11 @@
-import React = require("react");
-import { observer } from "mobx-react";
-import "./TaskCompletedBox.scss";
-import { observable, action } from "mobx";
-import { Fade } from "@material-ui/core";
-
+import * as React from 'react';
+import { observer } from 'mobx-react';
+import './TaskCompletedBox.scss';
+import { observable, action } from 'mobx';
+import { Fade } from '@mui/material';
@observer
export class TaskCompletionBox extends React.Component<{}> {
-
@observable public static taskCompleted: boolean = false;
@observable public static popupX: number = 500;
@observable public static popupY: number = 150;
@@ -16,15 +14,20 @@ export class TaskCompletionBox extends React.Component<{}> {
@action
public static toggleTaskCompleted = () => {
TaskCompletionBox.taskCompleted = !TaskCompletionBox.taskCompleted;
- }
+ };
render() {
- return <Fade in={TaskCompletionBox.taskCompleted}>
- <div className="taskCompletedBox-fade"
- style={{
- left: TaskCompletionBox.popupX ? TaskCompletionBox.popupX : 500,
- top: TaskCompletionBox.popupY ? TaskCompletionBox.popupY : 150,
- }}>{TaskCompletionBox.textDisplayed}</div>
- </Fade>;
+ return (
+ <Fade in={TaskCompletionBox.taskCompleted}>
+ <div
+ className="taskCompletedBox-fade"
+ style={{
+ left: TaskCompletionBox.popupX ? TaskCompletionBox.popupX : 500,
+ top: TaskCompletionBox.popupY ? TaskCompletionBox.popupY : 150,
+ }}>
+ {TaskCompletionBox.textDisplayed}
+ </div>
+ </Fade>
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 0f6c6724a..04780155d 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from 'mobx';
import { observer } from 'mobx-react';
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index b3afe6ba0..d02e5f3e8 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -36,7 +36,7 @@ import { FieldView, FieldViewProps } from './FieldView';
import { LinkDocPreview } from './LinkDocPreview';
import { PinProps, PresBox } from './trails';
import './WebBox.scss';
-import React = require('react');
+import * as React from 'react';
const { CreateImage } = require('./WebBoxRenderer');
const _global = (window /* browser */ || global) /* node */ as any;
const htmlToText = require('html-to-text');
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index aa269d8d6..d6b053c8e 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -2,7 +2,7 @@ import { TextSelection } from 'prosemirror-state';
import * as ReactDOM from 'react-dom/client';
import { Doc } from '../../../../fields/Doc';
import { DocServer } from '../../../DocServer';
-import React = require('react');
+import * as React from 'react';
// creates an inline comment in a note when '>>' is typed.
// the comment sits on the right side of the note and vertically aligns with its anchor in the text.
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 95743a036..4384c8958 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -11,7 +11,7 @@ import { Docs, DocUtils } from '../../../documents/Documents';
import { Transform } from '../../../util/Transform';
import { DocFocusOptions, DocumentView } from '../DocumentView';
import { FormattedTextBox } from './FormattedTextBox';
-import React = require('react');
+import * as React from 'react';
export class DashDocView {
dom: HTMLSpanElement; // container for label and value
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index a914084f9..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';
@@ -16,7 +16,7 @@ import { SchemaTableCell } from '../../collections/collectionSchema/SchemaTableC
import { OpenWhere } from '../DocumentView';
import './DashFieldView.scss';
import { FormattedTextBox } from './FormattedTextBox';
-import React = require('react');
+import * as React from 'react';
import { Transform } from '../../../util/Transform';
export class DashFieldView {
diff --git a/src/client/views/nodes/formattedText/EquationEditor.tsx b/src/client/views/nodes/formattedText/EquationEditor.tsx
new file mode 100644
index 000000000..bde6c1315
--- /dev/null
+++ b/src/client/views/nodes/formattedText/EquationEditor.tsx
@@ -0,0 +1,87 @@
+import React, { Component, createRef } from 'react';
+
+// Import JQuery, required for the functioning of the equation editor
+import $ from 'jquery';
+
+// Import the styles from the Mathquill editor
+import 'mathquill/build/mathquill.css';
+
+// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
+// @ts-ignore
+window.jQuery = $;
+
+// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
+// @ts-ignore
+require('mathquill/build/mathquill');
+
+type EquationEditorProps = {
+ onChange(latex: string): void;
+ value: string;
+ spaceBehavesLikeTab?: boolean;
+ autoCommands: string;
+ autoOperatorNames: string;
+ onEnter?(): void;
+};
+
+/**
+ * @typedef {EquationEditorProps} props
+ * @prop {Function} onChange Triggered when content of the equation editor changes
+ * @prop {string} value Content of the equation handler
+ * @prop {boolean}[false] spaceBehavesLikeTab Whether spacebar should simulate tab behavior
+ * @prop {string} autoCommands List of commands for which you only have to type the name of the
+ * command with a \ in front of it. Examples: pi theta rho sum
+ * @prop {string} autoOperatorNames List of operators for which you only have to type the name of the
+ * operator with a \ in front of it. Examples: sin cos tan
+ * @prop {Function} onEnter Triggered when enter is pressed in the equation editor
+ * @extends {Component<EquationEditorProps>}
+ */
+class EquationEditor extends Component<EquationEditorProps> {
+ element: any;
+ mathField: any;
+ ignoreEditEvents: number;
+
+ // Element needs to be in the class format and thus requires a constructor. The steps that are run
+ // in the constructor is to make sure that React can succesfully communicate with the equation
+ // editor.
+ constructor(props: EquationEditorProps) {
+ super(props);
+
+ this.element = createRef();
+ this.mathField = null;
+
+ // MathJax apparently fire 2 edit events on startup.
+ this.ignoreEditEvents = 2;
+ }
+
+ componentDidMount() {
+ const { onChange, value, spaceBehavesLikeTab, autoCommands, autoOperatorNames, onEnter } = this.props;
+
+ const config = {
+ handlers: {
+ edit: () => {
+ if (this.ignoreEditEvents > 0) {
+ this.ignoreEditEvents -= 1;
+ return;
+ }
+ if (this.mathField.latex() !== value) {
+ onChange(this.mathField.latex());
+ }
+ },
+ enter: onEnter,
+ },
+ spaceBehavesLikeTab,
+ autoCommands,
+ autoOperatorNames,
+ };
+
+ // @ts-ignore
+ this.mathField = (MathQuill as any).MathField(this.element.current, config);
+ this.mathField.latex(value || '');
+ }
+
+ render() {
+ return <span ref={this.element} style={{ border: '0px', boxShadow: 'None' }} />;
+ }
+}
+
+export default EquationEditor;
diff --git a/src/client/views/nodes/formattedText/EquationView.tsx b/src/client/views/nodes/formattedText/EquationView.tsx
index 5e62d94c2..5ee5d25c3 100644
--- a/src/client/views/nodes/formattedText/EquationView.tsx
+++ b/src/client/views/nodes/formattedText/EquationView.tsx
@@ -1,4 +1,4 @@
-import EquationEditor from 'equation-editor-react';
+import EquationEditor from './EquationEditor';
import { IReactionDisposer, trace } from 'mobx';
import { observer } from 'mobx-react';
import { TextSelection } from 'prosemirror-state';
@@ -7,7 +7,7 @@ import { Doc } from '../../../../fields/Doc';
import { StrCast } from '../../../../fields/Types';
import './DashFieldView.scss';
import { FormattedTextBox } from './FormattedTextBox';
-import React = require('react');
+import * as React from 'react';
export class EquationView {
dom: HTMLDivElement; // container for label and value
@@ -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 42e8ace6e..4f8e8769a 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 React = require('react');
-// setting up cors-anywhere server address
-const translate = setCORS('http://cors-anywhere.herokuapp.com/');
+// import * as applyDevTools from 'prosemirror-dev-tools';
+import * as React from 'react';
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;
@@ -1954,7 +1937,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
@computed get sidebarCollection() {
const renderComponent = (tag: string) => {
- const ComponentTag = tag === CollectionViewType.Freeform ? CollectionFreeFormView : tag === CollectionViewType.Tree ? CollectionTreeView : tag === 'translation' ? FormattedTextBox : CollectionStackingView;
+ const ComponentTag: any = tag === CollectionViewType.Freeform ? CollectionFreeFormView : tag === CollectionViewType.Tree ? CollectionTreeView : tag === 'translation' ? FormattedTextBox : CollectionStackingView;
return ComponentTag === CollectionStackingView ? (
<SidebarAnnos
ref={this._sidebarRef}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 791a1bfe7..7ce06cf7f 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,6 +1,6 @@
-import React = require('react');
+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/formattedText/SummaryView.tsx b/src/client/views/nodes/formattedText/SummaryView.tsx
index 3355e4529..7ec296ed2 100644
--- a/src/client/views/nodes/formattedText/SummaryView.tsx
+++ b/src/client/views/nodes/formattedText/SummaryView.tsx
@@ -1,7 +1,7 @@
import { TextSelection } from 'prosemirror-state';
import { Fragment, Node, Slice } from 'prosemirror-model';
import * as ReactDOM from 'react-dom/client';
-import React = require('react');
+import * as React from 'react';
// an elidable textblock that collapses when its '<-' is clicked and expands when its '...' anchor is clicked.
// this node actively edits prosemirror (as opposed to just changing how things are rendered) and thus doesn't
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 4faef26e2..a342285b0 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { DOMOutputSpec, Fragment, MarkSpec, Node, NodeSpec, Schema, Slice } from 'prosemirror-model';
import { Doc } from '../../../../fields/Doc';
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 4cd2cee52..d023020e1 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { DOMOutputSpec, Node, NodeSpec } from 'prosemirror-model';
import { listItem, orderedList } from 'prosemirror-schema-list';
import { ParagraphNodeSpec, toParagraphDOM, getParagraphNodeAttrs } from './ParagraphNodeSpec';
diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
index 2b0927148..87e1b69c3 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx
+++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx
@@ -21,7 +21,7 @@ import { activeColor, canvasSize, eraserColor, freeformRenderSize, newCollection
import { CursorData, ImageDimensions, Point } from './generativeFillUtils/generativeFillInterfaces';
import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler';
import { PointerHandler } from './generativeFillUtils/PointerHandler';
-import React = require('react');
+import * as React from 'react';
enum BrushStyle {
ADD,
@@ -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/generativeFill/GenerativeFillButtons.tsx b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
index 10eca358e..185ba2280 100644
--- a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
+++ b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx
@@ -1,5 +1,5 @@
import './GenerativeFillButtons.scss';
-import React = require('react');
+import * as React from 'react';
import ReactLoading from 'react-loading';
import { activeColor } from './generativeFillUtils/generativeFillConstants';
import { Button, IconButton, Type } from 'browndash-components';
diff --git a/src/client/views/nodes/importBox/ImportElementBox.tsx b/src/client/views/nodes/importBox/ImportElementBox.tsx
index c4c89d8b1..9dc0c5180 100644
--- a/src/client/views/nodes/importBox/ImportElementBox.tsx
+++ b/src/client/views/nodes/importBox/ImportElementBox.tsx
@@ -7,7 +7,7 @@ import { ViewBoxBaseComponent } from '../../DocComponent';
import { DefaultStyleProvider } from '../../StyleProvider';
import { DocumentView, DocumentViewInternal } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
-import React = require('react');
+import * as React from 'react';
@observer
export class ImportElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 2303e4126..1317ca814 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1,6 +1,6 @@
-import React = require('react');
+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 f8aebd021..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';
@@ -24,7 +24,7 @@ import { StyleProp } from '../../StyleProvider';
import { PresBox } from './PresBox';
import './PresElementBox.scss';
import { PresMovement } from './PresEnums';
-import React = require('react');
+import * as React from 'react';
/**
* This class models the view a document added to presentation will have in the presentation.
* It involves some functionality for its buttons and options.
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index d68859e88..2d8e9e2a2 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -1,9 +1,9 @@
-import React = require('react');
+import * as React from 'react';
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/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index e1e87763c..cf19ff6bc 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { action, computed } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
index 038c45582..0cda36509 100644
--- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx
+++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx
@@ -1,10 +1,10 @@
-import React = require('react');
+import * as React from 'react';
import './GPTPopup.scss';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import ReactLoading from 'react-loading';
-import Typist from 'react-typist';
+import { TypeAnimation } from 'react-type-animation';
import { Doc } from '../../../../fields/Doc';
import { DocUtils, Docs } from '../../../documents/Documents';
import { Button, IconButton, Type } from 'browndash-components';
@@ -218,17 +218,18 @@ export class GPTPopup extends React.Component<GPTPopupProps> {
<div className="content-wrapper">
{!this.loading &&
(!this.done ? (
- <Typist
- key={this.text}
- avgTypingDelay={15}
- cursor={{ hideWhenDone: true }}
- onTypingDone={() => {
- setTimeout(() => {
- this.setDone(true);
- }, 500);
- }}>
- {this.text}
- </Typist>
+ <TypeAnimation
+ speed={50}
+ sequence={[
+ this.text,
+ () => {
+ setTimeout(() => {
+ this.setDone(true);
+ }, 500);
+ },
+ ]}
+ //cursor={{ hideWhenDone: true }}
+ />
) : (
this.text
))}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 33a336de3..b2373b190 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -23,7 +23,7 @@ import { AnchorMenu } from './AnchorMenu';
import { Annotation } from './Annotation';
import { GPTPopup } from './GPTPopup/GPTPopup';
import './PDFViewer.scss';
-import React = require('react');
+import * as React from 'react';
const PDFJSViewer = require('pdfjs-dist/web/pdf_viewer');
const pdfjsLib = require('pdfjs-dist');
const _global = (window /* browser */ || global) /* node */ as any;
diff --git a/src/client/views/search/IconButton.tsx b/src/client/views/search/IconButton.tsx
index 6cf3a5f72..e12e4511c 100644
--- a/src/client/views/search/IconButton.tsx
+++ b/src/client/views/search/IconButton.tsx
@@ -1,29 +1,27 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import * as _ from "lodash";
+import * as _ from 'lodash';
import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { DocumentType } from "../../documents/DocumentTypes";
+import { DocumentType } from '../../documents/DocumentTypes';
import '../global/globalCssVariables.scss';
import { IconBar } from './IconBar';
-import "./IconButton.scss";
-import "./SearchBox.scss";
-import { Font } from '@react-pdf/renderer';
+import './IconButton.scss';
+import './SearchBox.scss';
-interface IconButtonProps {
+interface iconButtonProps {
type: string;
}
@observer
-export class IconButton extends React.Component<IconButtonProps>{
-
+export class IconButton extends React.Component<iconButtonProps> {
@observable private _isSelected: boolean = IconBar.Instance.getIcons().indexOf(this.props.type) !== -1;
@observable private _hover = false;
private _resetReaction?: IReactionDisposer;
private _selectAllReaction?: IReactionDisposer;
static Instance: IconButton;
- constructor(props: IconButtonProps) {
+ constructor(props: iconButtonProps) {
super(props);
IconButton.Instance = this;
}
@@ -40,7 +38,7 @@ export class IconButton extends React.Component<IconButtonProps>{
IconBar.Instance._resetClicked = false;
}
}
- }),
+ })
);
this._selectAllReaction = reaction(
@@ -54,25 +52,25 @@ export class IconButton extends React.Component<IconButtonProps>{
IconBar.Instance._selectAllClicked = false;
}
}
- }),
+ })
);
- }
+ };
@action.bound
getIcon() {
switch (this.props.type) {
- case (DocumentType.NONE): return "ban";
- case (DocumentType.AUDIO): return "music";
- case (DocumentType.COL): return "object-group";
- case (DocumentType.IMG): return "image";
- case (DocumentType.LINK): return "link";
- case (DocumentType.PDF): return "file-pdf";
- case (DocumentType.RTF): return "sticky-note";
- case (DocumentType.VID): return "video";
- case (DocumentType.WEB): return "globe-asia";
- case (DocumentType.MAP): return "map-marker-alt";
- default: return "caret-down";
- }
+ case DocumentType.NONE: return 'ban';
+ case DocumentType.AUDIO: return 'music';
+ case DocumentType.COL: return 'object-group';
+ case DocumentType.IMG: return 'image';
+ case DocumentType.LINK: return 'link';
+ case DocumentType.PDF: return 'file-pdf';
+ case DocumentType.RTF: return 'sticky-note';
+ case DocumentType.VID: return 'video';
+ case DocumentType.WEB: return 'globe-asia';
+ case DocumentType.MAP: return 'map-marker-alt';
+ default: return 'caret-down';
+ } // prettier-ignore
}
@action.bound
@@ -82,45 +80,39 @@ export class IconButton extends React.Component<IconButtonProps>{
if (!this._isSelected) {
this._isSelected = true;
newList.push(this.props.type);
- }
- else {
+ } else {
this._isSelected = false;
_.pull(newList, this.props.type);
}
IconBar.Instance.updateIcon(newList);
- }
+ };
selected = {
opacity: 1,
- backgroundColor: "#121721",
+ backgroundColor: '#121721',
//backgroundColor: "rgb(128, 128, 128)"
};
notSelected = {
opacity: 0.2,
- backgroundColor: "#121721",
+ backgroundColor: '#121721',
};
hoverStyle = {
opacity: 1,
- backgroundColor: "rgb(128, 128, 128)"
+ backgroundColor: 'rgb(128, 128, 128)',
//backgroundColor: "rgb(178, 206, 248)" //$medium-blue
};
render() {
return (
- <div className="type-outer" id={this.props.type + "-filter"}
- onMouseEnter={() => this._hover = true}
- onMouseLeave={() => this._hover = false}
- onClick={this.onClick}>
- <div className="type-icon" id={this.props.type + "-icon"}
- style={this._hover ? this.hoverStyle : this._isSelected ? this.selected : this.notSelected}
- >
+ <div className="type-outer" id={this.props.type + '-filter'} onMouseEnter={() => (this._hover = true)} onMouseLeave={() => (this._hover = false)} onClick={this.onClick}>
+ <div className="type-icon" id={this.props.type + '-icon'} style={this._hover ? this.hoverStyle : this._isSelected ? this.selected : this.notSelected}>
<FontAwesomeIcon className="fontawesome-icon" icon={this.getIcon()} />
</div>
{/* <div className="filter-description">{this.props.type}</div> */}
</div>
);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/search/NaviconButton.scss b/src/client/views/search/NaviconButton.scss
index 8a70b29de..13d31bbe9 100644
--- a/src/client/views/search/NaviconButton.scss
+++ b/src/client/views/search/NaviconButton.scss
@@ -1,4 +1,4 @@
-@import "../global/globalCssVariables";
+@import '../global/globalCssVariables';
$height-icon: 15px;
$width-line: 30px;
@@ -6,7 +6,7 @@ $height-line: 4px;
$transition-time: 0.4s;
$rotation: 45deg;
-$translateY: ($height-icon / 2);
+$translateY: calc($height-icon / 2);
$translateX: 0;
#hamburger-icon {
@@ -25,7 +25,7 @@ $translateX: 0;
height: $height-line;
position: absolute;
left: 0;
- border-radius: ($height-line / 2);
+ border-radius: calc($height-line / 2);
transition: all $transition-time;
-webkit-transition: all $transition-time;
-moz-transition: all $transition-time;
@@ -66,4 +66,4 @@ $translateX: 0;
transform: scale(1.1);
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1);
-} \ No newline at end of file
+}
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/client/views/selectedDoc/SelectedDocView.tsx b/src/client/views/selectedDoc/SelectedDocView.tsx
index 2139919e0..39e778b76 100644
--- a/src/client/views/selectedDoc/SelectedDocView.tsx
+++ b/src/client/views/selectedDoc/SelectedDocView.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ListBox } from 'browndash-components';
import { computed } from 'mobx';
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
index 524492226..093806127 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -11,7 +11,7 @@ import { DocumentView } from '../nodes/DocumentView';
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import './DashWebRTCVideo.scss';
import { hangup, initialize, refreshVideos } from './WebCamLogic';
-import React = require('react');
+import * as React from 'react';
/**
* This models the component that will be rendered, that can be used as a doc that will reflect the video cams.
diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx
index 17d3db8fd..c906dcc03 100644
--- a/src/debug/Test.tsx
+++ b/src/debug/Test.tsx
@@ -1,13 +1,11 @@
import * as React from 'react';
-import * as ReactDOM from 'react-dom';
-import { DocServer } from '../client/DocServer';
-import { Doc } from '../fields/Doc';
-import * as Pdfjs from "pdfjs-dist";
-import "pdfjs-dist/web/pdf_viewer.css";
-import { Utils } from '../Utils';
-const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
-
-const protoId = "protoDoc";
-const delegateId = "delegateDoc";
+import * as ReactDOM from 'react-dom/client';
+console.log('ENTERED');
class Test extends React.Component {
+ render() {
+ return <div> HELLO WORLD </div>;
+ }
}
+const root = ReactDOM.createRoot(document.getElementById('root')!);
+
+root.render(<Test />);
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index e60f7553f..6fb97d70c 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -30,7 +30,7 @@ import { ComputedField, ScriptField } from './ScriptField';
import { BoolCast, Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Types';
import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField';
import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, setter, SharingPermissions } from './util';
-import JSZip = require('jszip');
+import * as JSZip from 'jszip';
export const LinkedTo = '-linkedTo';
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
@@ -816,6 +816,7 @@ export namespace Doc {
}
export function FindReferences(infield: Doc | List<any>, references: Set<Doc>, system: boolean | undefined) {
+ if (infield instanceof Promise) return;
if (!(infield instanceof Doc)) {
infield.forEach(val => (val instanceof Doc || val instanceof List) && FindReferences(val, references, system));
return;
@@ -1201,14 +1202,14 @@ export namespace Doc {
return !doc
? undefined
: doc.isTemplateDoc
- ? doc
- : Cast(doc.dragFactory, Doc, null)?.isTemplateDoc
- ? doc.dragFactory
- : Cast(Doc.Layout(doc), Doc, null)?.isTemplateDoc
- ? Cast(Doc.Layout(doc), Doc, null).resolvedDataDoc
- ? Doc.Layout(doc).proto
- : Doc.Layout(doc)
- : undefined;
+ ? doc
+ : Cast(doc.dragFactory, Doc, null)?.isTemplateDoc
+ ? doc.dragFactory
+ : Cast(Doc.Layout(doc), Doc, null)?.isTemplateDoc
+ ? Cast(Doc.Layout(doc), Doc, null).resolvedDataDoc
+ ? Doc.Layout(doc).proto
+ : Doc.Layout(doc)
+ : undefined;
}
export function deiconifyView(doc: Doc) {
diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts
index f035eeb0d..f5e64ae1f 100644
--- a/src/fields/Schema.ts
+++ b/src/fields/Schema.ts
@@ -94,6 +94,7 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc)
}
export function createSchema<T extends Interface>(schema: T): T & { proto: ToConstructor<Doc> } {
+ return undefined as any;
(schema as any).proto = Doc;
return schema as any;
}
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index cd07a8885..62690a9fb 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -15,7 +15,7 @@ function optional(propSchema: PropSchema) {
return custom(
value => {
if (value !== undefined) {
- return propSchema.serializer(value);
+ return propSchema.serializer(value, '', undefined); // this function only takes one parameter, but I think its typescript typings are messed up to take 3
}
return SKIP;
},
@@ -97,7 +97,7 @@ export class ScriptField extends ObjectField {
constructor(script: CompiledScript | undefined, setterscript?: CompiledScript, rawscript?: string) {
super();
- const captured = script?.options.capturedVariables;
+ const captured = script?.options?.capturedVariables;
if (captured) {
this.captures = new List<string>(Object.keys(captured).map(key => key + ':' + (captured[key] instanceof Doc ? 'ID->' + (captured[key] as Doc)[Id] : captured[key].toString())));
}
@@ -151,7 +151,7 @@ export class ComputedField extends ScriptField {
_lastComputedResult: any;
//TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc
value = computedFn((doc: Doc) => this._valueOutsideReaction(doc));
- _valueOutsideReaction = (doc: Doc) => (this._lastComputedResult = this.script.run({ this: doc, self: doc, value: '', _last_: this._lastComputedResult, _readOnly_: true }, console.log).result);
+ _valueOutsideReaction = (doc: Doc) => (this._lastComputedResult = this.script.compiled && this.script.run({ this: doc, self: doc, value: '', _last_: this._lastComputedResult, _readOnly_: true }, console.log).result);
[ToValue](doc: Doc) {
return ComputedField.toValue(doc, this);
diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx
index da38fcaee..ee212e4af 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -5,45 +5,45 @@ import * as rp from 'request-promise';
import { DocServer } from '../client/DocServer';
import { Docs } from '../client/documents/Documents';
import { Networking } from '../client/Network';
-import { DFLT_IMAGE_NATIVE_DIM } from '../client/views/global/globalCssVariables.scss';
import { MainViewModal } from '../client/views/MainViewModal';
import { Doc, Opt } from '../fields/Doc';
import { List } from '../fields/List';
import { listSpec } from '../fields/Schema';
import { Cast } from '../fields/Types';
import { Utils } from '../Utils';
-import "./ImageUpload.scss";
+import './ImageUpload.scss';
import { MobileInterface } from './MobileInterface';
-import React = require('react');
-
+import * as React from 'react';
+//import { DFLT_IMAGE_NATIVE_DIM } from '../client/views/global/globalCssVariables.scss';
+const DFLT_IMAGE_NATIVE_DIM = '50px';
export interface ImageUploadProps {
Document: Doc; // Target document for upload (upload location)
}
const inputRef = React.createRef<HTMLInputElement>();
-const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", ""));
+const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', ''));
@observer
export class Uploader extends React.Component<ImageUploadProps> {
- @observable error: string = "";
- @observable nm: string = "Choose files"; // Text of 'Choose Files' button
- @observable process: string = ""; // Current status of upload
+ @observable error: string = '';
+ @observable nm: string = 'Choose files'; // Text of 'Choose Files' button
+ @observable process: string = ''; // Current status of upload
onClick = async () => {
try {
const col = this.props.Document;
await Docs.Prototypes.initialize();
- const imgPrev = document.getElementById("img_preview");
- this.setOpacity(1, "1"); // Slab 1
+ const imgPrev = document.getElementById('img_preview');
+ this.setOpacity(1, '1'); // Slab 1
if (imgPrev) {
const files: FileList | null = inputRef.current!.files;
- this.setOpacity(2, "1"); // Slab 2
+ this.setOpacity(2, '1'); // Slab 2
if (files && files.length !== 0) {
- this.process = "Uploading Files";
+ this.process = 'Uploading Files';
for (let index = 0; index < files.length; ++index) {
const file = files[index];
- const res = await Networking.UploadFilesToServer({file});
- this.setOpacity(3, "1"); // Slab 3
+ const res = await Networking.UploadFilesToServer({ file });
+ this.setOpacity(3, '1'); // Slab 3
// For each item that the user has selected
res.map(async ({ result }) => {
const name = file.name;
@@ -53,19 +53,19 @@ export class Uploader extends React.Component<ImageUploadProps> {
const path = result.accessPaths.agnostic.client;
let doc = null;
// Case 1: File is a video
- if (file.type === "video/mp4") {
+ if (file.type === 'video/mp4') {
doc = Docs.Create.VideoDocument(path, { _nativeWidth: defaultNativeImageDim, _width: 400, title: name });
// Case 2: File is a PDF document
- } else if (file.type === "application/pdf") {
+ } else if (file.type === 'application/pdf') {
doc = Docs.Create.PdfDocument(path, { _nativeWidth: defaultNativeImageDim, _width: 400, title: name });
// Case 3: File is another document type (most likely Image)
} else {
doc = Docs.Create.ImageDocument(path, { _nativeWidth: defaultNativeImageDim, _width: 400, title: name });
}
- this.setOpacity(4, "1"); // Slab 4
- const res = await rp.get(Utils.prepend("/getUserDocumentIds"));
+ this.setOpacity(4, '1'); // Slab 4
+ const res = await rp.get(Utils.prepend('/getUserDocumentIds'));
if (!res) {
- throw new Error("No user id returned");
+ throw new Error('No user id returned');
}
const field = await DocServer.GetRefField(JSON.parse(res).userDocumentId);
let pending: Opt<Doc>;
@@ -76,19 +76,19 @@ export class Uploader extends React.Component<ImageUploadProps> {
const data = await Cast(pending.data, listSpec(Doc));
if (data) data.push(doc);
else pending.data = new List([doc]);
- this.setOpacity(5, "1"); // Slab 5
- this.process = "File " + (index + 1).toString() + " Uploaded";
- this.setOpacity(6, "1"); // Slab 6
+ this.setOpacity(5, '1'); // Slab 5
+ this.process = 'File ' + (index + 1).toString() + ' Uploaded';
+ this.setOpacity(6, '1'); // Slab 6
}
- if ((index + 1) === files.length) {
- this.process = "Uploads Completed";
- this.setOpacity(7, "1"); // Slab 7
+ if (index + 1 === files.length) {
+ this.process = 'Uploads Completed';
+ this.setOpacity(7, '1'); // Slab 7
}
});
}
// Case in which the user pressed upload and no files were selected
} else {
- this.process = "No file selected";
+ this.process = 'No file selected';
}
// Three seconds after upload the menu will reset
setTimeout(this.clearUpload, 3000);
@@ -96,7 +96,7 @@ export class Uploader extends React.Component<ImageUploadProps> {
} catch (error) {
this.error = JSON.stringify(error);
}
- }
+ };
// Updates label after a files is selected (so user knows a file is uploaded)
inputLabel = async () => {
@@ -105,46 +105,48 @@ export class Uploader extends React.Component<ImageUploadProps> {
if (files && files.length === 1) {
this.nm = files[0].name;
} else if (files && files.length > 1) {
- this.nm = files.length.toString() + " files selected";
+ this.nm = files.length.toString() + ' files selected';
}
- }
+ };
// Loops through load icons, and resets buttons
@action
clearUpload = () => {
for (let i = 1; i < 8; i++) {
- this.setOpacity(i, "0.2");
+ this.setOpacity(i, '0.2');
}
- this.nm = "Choose files";
+ this.nm = 'Choose files';
if (inputRef.current) {
- inputRef.current.value = "";
+ inputRef.current.value = '';
}
- this.process = "";
- }
+ this.process = '';
+ };
// Clears the upload and closes the upload menu
closeUpload = () => {
this.clearUpload();
MobileInterface.Instance.toggleUpload();
- }
+ };
// Handles the setting of the loading bar
setOpacity = (index: number, opacity: string) => {
- const slab = document.getElementById("slab" + index);
+ const slab = document.getElementById('slab' + index);
if (slab) slab.style.opacity = opacity;
- }
+ };
// Returns the upload interface for mobile
private get uploadInterface() {
return (
<div className="imgupload_cont">
<div className="closeUpload" onClick={() => this.closeUpload()}>
- <FontAwesomeIcon icon="window-close" size={"lg"} />
+ <FontAwesomeIcon icon="window-close" size={'lg'} />
</div>
- <FontAwesomeIcon icon="upload" size="lg" style={{ fontSize: "130" }} />
- <input type="file" accept="application/pdf, video/*,image/*" className={`inputFile ${this.nm !== "Choose files" ? "active" : ""}`} id="input_image_file" ref={inputRef} onChange={this.inputLabel} multiple></input>
- <label className="file" id="label" htmlFor="input_image_file">{this.nm}</label>
+ <FontAwesomeIcon icon="upload" size="lg" style={{ fontSize: '130' }} />
+ <input type="file" accept="application/pdf, video/*,image/*" className={`inputFile ${this.nm !== 'Choose files' ? 'active' : ''}`} id="input_image_file" ref={inputRef} onChange={this.inputLabel} multiple></input>
+ <label className="file" id="label" htmlFor="input_image_file">
+ {this.nm}
+ </label>
<div className="upload_label" onClick={this.onClick}>
Upload
</div>
@@ -167,16 +169,6 @@ export class Uploader extends React.Component<ImageUploadProps> {
@observable private overlayOpacity = 0.4;
render() {
- return (
- <MainViewModal
- contents={this.uploadInterface}
- isDisplayed={true}
- interactive={true}
- dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity}
- overlayDisplayedOpacity={this.overlayOpacity}
- closeOnExternalClick={this.closeUpload}
- />
- );
+ return <MainViewModal contents={this.uploadInterface} isDisplayed={true} interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} closeOnExternalClick={this.closeUpload} />;
}
-
-} \ No newline at end of file
+}
diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx
index 6415099fd..2e595e4bc 100644
--- a/src/mobile/MobileInkOverlay.tsx
+++ b/src/mobile/MobileInkOverlay.tsx
@@ -1,4 +1,4 @@
-import React = require('react');
+import * as React from 'react';
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import { DocServer } from '../client/DocServer';
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..c6c4ca464 100644
--- a/src/server/ApiManagers/DeleteManager.ts
+++ b/src/server/ApiManagers/DeleteManager.ts
@@ -1,21 +1,19 @@
-import ApiManager, { Registration } from "./ApiManager";
-import { Method, _permission_denied } from "../RouteManager";
-import { WebSocket } from "../websocket";
-import { Database } from "../database";
-import rimraf = require("rimraf");
-import { filesDirectory } from "..";
-import { DashUploadUtils } from "../DashUploadUtils";
-import { mkdirSync } from "fs";
-import RouteSubscriber from "../RouteSubscriber";
+import ApiManager, { Registration } from './ApiManager';
+import { Method, _permission_denied } from '../RouteManager';
+import { WebSocket } from '../websocket';
+import { Database } from '../database';
+import { rimraf } from 'rimraf';
+import { filesDirectory } from '..';
+import { DashUploadUtils } from '../DashUploadUtils';
+import { mkdirSync } from 'fs';
+import RouteSubscriber from '../RouteSubscriber';
export default class DeleteManager extends ApiManager {
-
protected initialize(register: Registration): void {
-
register({
method: Method.GET,
requireAdminInRelease: true,
- subscription: new RouteSubscriber("delete").add("target?"),
+ subscription: new RouteSubscriber('delete').add('target?'),
secureHandler: async ({ req, res }) => {
const { target } = req.params;
@@ -24,12 +22,12 @@ export default class DeleteManager extends ApiManager {
} else {
let all = false;
switch (target) {
- case "all":
+ case 'all':
all = true;
- case "database":
+ case 'database':
await WebSocket.doDelete(false);
if (!all) break;
- case "files":
+ case 'files':
rimraf.sync(filesDirectory);
mkdirSync(filesDirectory);
await DashUploadUtils.buildFileDirectories();
@@ -39,10 +37,8 @@ export default class DeleteManager extends ApiManager {
}
}
- res.redirect("/home");
- }
+ res.redirect('/home');
+ },
});
-
}
-
-} \ No newline at end of file
+}
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 391f67bbb..c3b8643d3 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 uuid from 'uuid';
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,15 +197,14 @@ 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;
const getId = (id: string): string => {
if (!remap || id.endsWith('Proto')) return id;
if (id in ids) return ids[id];
- return (ids[id] = v4());
+ return (ids[id] = uuid.v4());
};
const mapFn = (doc: any) => {
if (doc.id) {
@@ -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..0431b9bcf 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -7,6 +7,8 @@ import { Opt } from '../../fields/Doc';
import { WebSocket } from '../websocket';
import { resolvedPorts } from '../server_Initialization';
import { DashVersion } from '../../fields/DocSymbols';
+import { Utils } from '../../Utils';
+import { check, validationResult } from 'express-validator';
export const timeMap: { [id: string]: number } = {};
interface ActivityUnit {
@@ -32,7 +34,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' }];
}
@@ -49,7 +51,7 @@ export default class UserManager extends ApiManager {
method: Method.GET,
subscription: '/getUserDocumentIds',
secureHandler: ({ res, user }) => res.send({ userDocumentId: user.userDocumentId, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId }),
- publicHandler: ({ res }) => res.send({ userDocumentId: '__guest__', linkDatabaseId: 3, sharingDocumentId: 2 }),
+ publicHandler: ({ res }) => res.send({ userDocumentId: Utils.GuestID(), linkDatabaseId: 3, sharingDocumentId: 2 }),
});
register({
@@ -81,7 +83,7 @@ export default class UserManager extends ApiManager {
resolvedPorts,
})
),
- publicHandler: ({ res }) => res.send(JSON.stringify({ id: '__guest__', email: 'guest' })),
+ publicHandler: ({ res }) => res.send(JSON.stringify({ userDocumentId: Utils.GuestID(), email: 'guest', resolvedPorts })),
});
register({
@@ -107,15 +109,18 @@ export default class UserManager extends ApiManager {
return;
}
- req.assert('new_pass', 'Password must be at least 4 characters long').len({ min: 4 });
- req.assert('new_confirm', 'Passwords do not match').equals(new_pass);
+ check('new_pass', 'Password must be at least 4 characters long')
+ .run(req)
+ .then(chcekcres => console.log(chcekcres)); //.len({ min: 4 });
+ check('new_confirm', 'Passwords do not match')
+ .run(req)
+ .then(theres => console.log(theres)); //.equals(new_pass);
if (curr_pass === new_pass) {
result.error = [{ msg: 'Current and new password are the same' }];
}
- // was there error in validating new passwords?
- if (req.validationErrors()) {
- // was there error?
- result.error = req.validationErrors();
+ if (validationResult(req).array().length) {
+ // was there error in validating new passwords?
+ result.error = validationResult(req);
}
// will only change password if there are no errors.
@@ -125,7 +130,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 1a5934d8f..1ef7a131d 100644
--- a/src/server/DashSession/DashSessionAgent.ts
+++ b/src/server/DashSession/DashSessionAgent.ts
@@ -1,18 +1,18 @@
-import { Email, pathFromRoot } from "../ActionUtilities";
-import { red, yellow, green, cyan } from "colors";
-import { get } from "request-promise";
-import { Utils } from "../../Utils";
-import { WebSocket } from "../websocket";
-import { MessageStore } from "../Message";
-import { launchServer, onWindows } from "..";
-import { readdirSync, statSync, createWriteStream, readFileSync, unlinkSync } from "fs";
-import * as Archiver from "archiver";
-import { resolve } from "path";
-import rimraf = require("rimraf");
-import { AppliedSessionAgent, ExitHandler } from "./Session/agents/applied_session_agent";
-import { ServerWorker } from "./Session/agents/server_worker";
-import { Monitor } from "./Session/agents/monitor";
-import { MessageHandler, ErrorLike } from "./Session/agents/promisified_ipc_manager";
+import { Email, pathFromRoot } from '../ActionUtilities';
+import { red, yellow, green, cyan } from 'colors';
+import { get } from 'request-promise';
+import { Utils } from '../../Utils';
+import { WebSocket } from '../websocket';
+import { MessageStore } from '../Message';
+import { launchServer, onWindows } from '..';
+import { readdirSync, statSync, createWriteStream, readFileSync, unlinkSync } from 'fs';
+import * as Archiver from 'archiver';
+import { resolve } from 'path';
+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';
+import { MessageHandler, ErrorLike } from './Session/agents/promisified_ipc_manager';
/**
* If we're the monitor (master) thread, we should launch the monitor logic for the session.
@@ -20,9 +20,8 @@ import { MessageHandler, ErrorLike } from "./Session/agents/promisified_ipc_mana
* our job should be to run the server.
*/
export class DashSessionAgent extends AppliedSessionAgent {
-
- private readonly signature = "-Dash Server Session Manager";
- private readonly releaseDesktop = pathFromRoot("../../Desktop");
+ private readonly signature = '-Dash Server Session Manager';
+ private readonly releaseDesktop = pathFromRoot('../../Desktop');
/**
* The core method invoked when the single master thread is initialized.
@@ -31,13 +30,13 @@ export class DashSessionAgent extends AppliedSessionAgent {
protected async initializeMonitor(monitor: Monitor): Promise<string> {
const sessionKey = Utils.GenerateGuid();
await this.dispatchSessionPassword(sessionKey);
- monitor.addReplCommand("pull", [], () => monitor.exec("git pull"));
- monitor.addReplCommand("solr", [/start|stop|index/], this.executeSolrCommand);
- monitor.addReplCommand("backup", [], this.backup);
- monitor.addReplCommand("debug", [/\S+\@\S+/], async ([to]) => this.dispatchZippedDebugBackup(to));
- monitor.on("backup", this.backup);
- monitor.on("debug", async ({ to }) => this.dispatchZippedDebugBackup(to));
- monitor.on("delete", WebSocket.doDelete);
+ monitor.addReplCommand('pull', [], () => monitor.exec('git pull'));
+ monitor.addReplCommand('solr', [/start|stop|index/], this.executeSolrCommand);
+ monitor.addReplCommand('backup', [], this.backup);
+ monitor.addReplCommand('debug', [/\S+\@\S+/], async ([to]) => this.dispatchZippedDebugBackup(to));
+ monitor.on('backup', this.backup);
+ monitor.on('debug', async ({ to }) => this.dispatchZippedDebugBackup(to));
+ monitor.on('delete', WebSocket.doDelete);
monitor.coreHooks.onCrashDetected(this.dispatchCrashReport);
return sessionKey;
}
@@ -58,13 +57,13 @@ export class DashSessionAgent extends AppliedSessionAgent {
private _remoteDebugInstructions: string | undefined;
private generateDebugInstructions = (zipName: string, target: string): string => {
if (!this._remoteDebugInstructions) {
- this._remoteDebugInstructions = readFileSync(resolve(__dirname, "./templates/remote_debug_instructions.txt"), { encoding: "utf8" });
+ this._remoteDebugInstructions = readFileSync(resolve(__dirname, './templates/remote_debug_instructions.txt'), { encoding: 'utf8' });
}
return this._remoteDebugInstructions
.replace(/__zipname__/, zipName)
.replace(/__target__/, target)
.replace(/__signature__/, this.signature);
- }
+ };
/**
* Prepares the body of the email with information regarding a crash event.
@@ -72,12 +71,12 @@ export class DashSessionAgent extends AppliedSessionAgent {
private _crashInstructions: string | undefined;
private generateCrashInstructions({ name, message, stack }: ErrorLike): string {
if (!this._crashInstructions) {
- this._crashInstructions = readFileSync(resolve(__dirname, "./templates/crash_instructions.txt"), { encoding: "utf8" });
+ this._crashInstructions = readFileSync(resolve(__dirname, './templates/crash_instructions.txt'), { encoding: 'utf8' });
}
return this._crashInstructions
- .replace(/__name__/, name || "[no error name found]")
- .replace(/__message__/, message || "[no error message found]")
- .replace(/__stack__/, stack || "[no error stack found]")
+ .replace(/__name__/, name || '[no error name found]')
+ .replace(/__message__/, message || '[no error message found]')
+ .replace(/__stack__/, stack || '[no error stack found]')
.replace(/__signature__/, this.signature);
}
@@ -88,23 +87,19 @@ export class DashSessionAgent extends AppliedSessionAgent {
private dispatchSessionPassword = async (sessionKey: string): Promise<void> => {
const { mainLog } = this.sessionMonitor;
const { notificationRecipient } = DashSessionAgent;
- mainLog(green("dispatching session key..."));
+ mainLog(green('dispatching session key...'));
const error = await Email.dispatch({
to: notificationRecipient,
- subject: "Dash Release Session Admin Authentication Key",
- content: [
- `Here's the key for this session (started @ ${new Date().toUTCString()}):`,
- sessionKey,
- this.signature
- ].join("\n\n")
+ subject: 'Dash Release Session Admin Authentication Key',
+ content: [`Here's the key for this session (started @ ${new Date().toUTCString()}):`, sessionKey, this.signature].join('\n\n'),
});
if (error) {
this.sessionMonitor.mainLog(red(`dispatch failure @ ${notificationRecipient} (${yellow(error.message)})`));
- mainLog(red("distribution of session key experienced errors"));
+ mainLog(red('distribution of session key experienced errors'));
} else {
- mainLog(green("successfully distributed session key to recipients"));
+ mainLog(green('successfully distributed session key to recipients'));
}
- }
+ };
/**
* This sends an email with the generated crash report.
@@ -114,37 +109,37 @@ export class DashSessionAgent extends AppliedSessionAgent {
const { notificationRecipient } = DashSessionAgent;
const error = await Email.dispatch({
to: notificationRecipient,
- subject: "Dash Web Server Crash",
- content: this.generateCrashInstructions(crashCause)
+ subject: 'Dash Web Server Crash',
+ content: this.generateCrashInstructions(crashCause),
});
if (error) {
this.sessionMonitor.mainLog(red(`dispatch failure @ ${notificationRecipient} ${yellow(`(${error.message})`)}`));
- mainLog(red("distribution of crash notification experienced errors"));
+ mainLog(red('distribution of crash notification experienced errors'));
} else {
- mainLog(green("successfully distributed crash notification to recipients"));
+ mainLog(green('successfully distributed crash notification to recipients'));
}
- }
+ };
/**
- * Logic for interfacing with Solr. Either starts it,
+ * Logic for interfacing with Solr. Either starts it,
* stops it, or rebuilds its indices.
*/
private executeSolrCommand = async (args: string[]): Promise<void> => {
const { exec, mainLog } = this.sessionMonitor;
const action = args[0];
- if (action === "index") {
- exec("npx ts-node ./updateSearch.ts", { cwd: pathFromRoot("./src/server") });
+ if (action === 'index') {
+ exec('npx ts-node ./updateSearch.ts', { cwd: pathFromRoot('./src/server') });
} else {
- const command = `${onWindows ? "solr.cmd" : "solr"} ${args[0] === "start" ? "start" : "stop -p 8983"}`;
- await exec(command, { cwd: "./solr-8.3.1/bin" });
+ const command = `${onWindows ? 'solr.cmd' : 'solr'} ${args[0] === 'start' ? 'start' : 'stop -p 8983'}`;
+ await exec(command, { cwd: './solr-8.3.1/bin' });
try {
- await get("http://localhost:8983");
- mainLog(green("successfully connected to 8983 after running solr initialization"));
+ await get('http://localhost:8983');
+ mainLog(green('successfully connected to 8983 after running solr initialization'));
} catch {
- mainLog(red("unable to connect at 8983 after running solr initialization"));
+ mainLog(red('unable to connect at 8983 after running solr initialization'));
}
}
- }
+ };
/**
* Broadcast to all clients that their connection
@@ -153,16 +148,16 @@ export class DashSessionAgent extends AppliedSessionAgent {
private notifyClient: ExitHandler = reason => {
const { _socket } = WebSocket;
if (_socket) {
- const message = typeof reason === "boolean" ? (reason ? "exit" : "temporary") : "crash";
+ const message = typeof reason === 'boolean' ? (reason ? 'exit' : 'temporary') : 'crash';
Utils.Emit(_socket, MessageStore.ConnectionTerminated, message);
}
- }
+ };
/**
* Performs a backup of the database, saved to the desktop subdirectory.
* This should work as is only on our specific release server.
*/
- private backup = async (): Promise<void> => this.sessionMonitor.exec("backup.bat", { cwd: this.releaseDesktop });
+ private backup = async (): Promise<void> => this.sessionMonitor.exec('backup.bat', { cwd: this.releaseDesktop });
/**
* Compress either a brand new backup or the most recent backup and send it
@@ -175,15 +170,17 @@ export class DashSessionAgent extends AppliedSessionAgent {
try {
// if desired, complete an immediate backup to send
await this.backup();
- mainLog("backup complete");
+ mainLog('backup complete');
const backupsDirectory = `${this.releaseDesktop}/backups`;
// sort all backups by their modified time, and choose the most recent one
- const target = readdirSync(backupsDirectory).map(filename => ({
- modifiedTime: statSync(`${backupsDirectory}/${filename}`).mtimeMs,
- filename
- })).sort((a, b) => b.modifiedTime - a.modifiedTime)[0].filename;
+ const target = readdirSync(backupsDirectory)
+ .map(filename => ({
+ modifiedTime: statSync(`${backupsDirectory}/${filename}`).mtimeMs,
+ filename,
+ }))
+ .sort((a, b) => b.modifiedTime - a.modifiedTime)[0].filename;
mainLog(`targeting ${target}...`);
// create a zip file and to it, write the contents of the backup directory
@@ -202,28 +199,25 @@ export class DashSessionAgent extends AppliedSessionAgent {
to,
subject: `Remote debug: compressed backup of ${target}...`,
content: this.generateDebugInstructions(zipName, target),
- attachments: [{ filename: zipName, path: zipPath }]
+ attachments: [{ filename: zipName, path: zipPath }],
});
- // since this is intended to be a zero-footprint operation, clean up
+ // since this is intended to be a zero-footprint operation, clean up
// by unlinking both the backup generated earlier in the function and the compressed zip file.
// to generate a persistent backup, just run backup.
unlinkSync(zipPath);
rimraf.sync(targetPath);
// indicate success or failure
- mainLog(`${error === null ? green("successfully dispatched") : red("failed to dispatch")} ${zipName} to ${cyan(to)}`);
+ mainLog(`${error === null ? green('successfully dispatched') : red('failed to dispatch')} ${zipName} to ${cyan(to)}`);
error && mainLog(red(error.message));
} catch (error: any) {
- mainLog(red("unable to dispatch zipped backup..."));
+ mainLog(red('unable to dispatch zipped backup...'));
mainLog(red(error.message));
}
}
-
}
export namespace DashSessionAgent {
-
- export const notificationRecipient = "browndashptc@gmail.com";
-
+ export const notificationRecipient = 'browndashptc@gmail.com';
}
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..a6fde4356 100644
--- a/src/server/DashSession/Session/agents/monitor.ts
+++ b/src/server/DashSession/Session/agents/monitor.ts
@@ -1,15 +1,21 @@
-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 { manage, MessageHandler, ErrorLike } from "./promisified_ipc_manager";
-import { red, cyan, white, yellow, blue } from "colors";
-import { exec, ExecOptions } from "child_process";
-import { validate, ValidationError } from "jsonschema";
-import { Utilities } from "../utilities/utilities";
-import { readFileSync } from "fs";
-import IPCMessageReceiver from "./process_message_router";
-import { ServerWorker } from "./server_worker";
+import { ExitHandler } from './applied_session_agent';
+import { Configuration, configurationSchema, defaultConfig, Identifiers, colorMapping } from '../utilities/session_config';
+import Repl, { ReplAction } from '../utilities/repl';
+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';
+import { validate, ValidationError } from 'jsonschema';
+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
@@ -26,14 +32,14 @@ export class Monitor extends IPCMessageReceiver {
public static Create() {
if (isWorker) {
- ServerWorker.IPCManager.emit("kill", {
- reason: "cannot create a monitor on the worker process.",
+ ServerWorker.IPCManager.emit('kill', {
+ reason: 'cannot create a monitor on the worker process.',
graceful: false,
- errorCode: 1
+ errorCode: 1,
});
process.exit(1);
} else if (++Monitor.count > 1) {
- console.error(red("cannot create more than one monitor."));
+ console.error(red('cannot create more than one monitor.'));
process.exit(1);
} else {
return new Monitor();
@@ -42,7 +48,7 @@ export class Monitor extends IPCMessageReceiver {
private constructor() {
super();
- console.log(this.timestamp(), cyan("initializing session..."));
+ console.log(this.timestamp(), cyan('initializing session...'));
this.configureInternalHandlers();
this.config = this.loadAndValidateConfiguration();
this.initializeClusterFunctions();
@@ -53,8 +59,8 @@ export class Monitor extends IPCMessageReceiver {
// handle exceptions in the master thread - there shouldn't be many of these
// the IPC (inter process communication) channel closed exception can't seem
// to be caught in a try catch, and is inconsequential, so it is ignored
- process.on("uncaughtException", ({ message, stack }): void => {
- if (message !== "Channel closed") {
+ process.on('uncaughtException', ({ message, stack }): void => {
+ if (message !== 'Channel closed') {
this.mainLog(red(message));
if (stack) {
this.mainLog(`uncaught exception\n${red(stack)}`);
@@ -62,36 +68,36 @@ export class Monitor extends IPCMessageReceiver {
}
});
- this.on("kill", ({ reason, graceful, errorCode }) => this.killSession(reason, graceful, errorCode));
- this.on("lifecycle", ({ event }) => console.log(this.timestamp(), `${this.config.identifiers.worker.text} lifecycle phase (${event})`));
- }
+ this.on('kill', ({ reason, graceful, errorCode }) => this.killSession(reason, graceful, errorCode));
+ this.on('lifecycle', ({ event }) => console.log(this.timestamp(), `${this.config.identifiers.worker.text} lifecycle phase (${event})`));
+ };
private initializeClusterFunctions = () => {
// determines whether or not we see the compilation / initialization / runtime output of each child server process
- const output = this.config.showServerOutput ? "inherit" : "ignore";
- setupMaster({ stdio: ["ignore", output, output, "ipc"] });
+ const output = this.config.showServerOutput ? 'inherit' : 'ignore';
+ setupMaster({ stdio: ['ignore', output, output, 'ipc'] });
// a helpful cluster event called on the master thread each time a child process exits
- on("exit", ({ process: { pid } }, code, signal) => {
- const prompt = `server worker with process id ${pid} has exited with code ${code}${signal === null ? "" : `, having encountered signal ${signal}`}.`;
+ on('exit', ({ process: { pid } }: { process: { pid: any } }, code: any, signal: any) => {
+ const prompt = `server worker with process id ${pid} has exited with code ${code}${signal === null ? '' : `, having encountered signal ${signal}`}.`;
this.mainLog(cyan(prompt));
// to make this a robust, continuous session, every time a child process dies, we immediately spawn a new one
this.spawn();
});
- }
+ };
public finalize = (sessionKey: string): void => {
if (this.finalized) {
- throw new Error("Session monitor is already finalized");
+ throw new Error('Session monitor is already finalized');
}
this.finalized = true;
this.key = sessionKey;
this.spawn();
- }
+ };
public readonly coreHooks = Object.freeze({
onCrashDetected: (listener: MessageHandler<{ error: ErrorLike }>) => this.on(Monitor.IntrinsicEvents.CrashDetected, listener),
- onServerRunning: (listener: MessageHandler<{ isFirstTime: boolean }>) => this.on(Monitor.IntrinsicEvents.ServerRunning, listener)
+ onServerRunning: (listener: MessageHandler<{ isFirstTime: boolean }>) => this.on(Monitor.IntrinsicEvents.ServerRunning, listener),
});
/**
@@ -101,12 +107,12 @@ export class Monitor extends IPCMessageReceiver {
* requests to complete) or immediately.
*/
public killSession = async (reason: string, graceful = true, errorCode = 0) => {
- this.mainLog(cyan(`exiting session ${graceful ? "clean" : "immediate"}ly`));
- this.mainLog(`session exit reason: ${(red(reason))}`);
+ this.mainLog(cyan(`exiting session ${graceful ? 'clean' : 'immediate'}ly`));
+ this.mainLog(`session exit reason: ${red(reason)}`);
await this.executeExitHandlers(true);
await this.killActiveWorker(graceful, true);
process.exit(errorCode);
- }
+ };
/**
* Execute the list of functions registered to be called
@@ -120,27 +126,27 @@ export class Monitor extends IPCMessageReceiver {
*/
public addReplCommand = (basename: string, argPatterns: (RegExp | string)[], action: ReplAction) => {
this.repl.registerCommand(basename, argPatterns, action);
- }
+ };
public exec = (command: string, options?: ExecOptions) => {
return new Promise<void>(resolve => {
- exec(command, { ...options, encoding: "utf8" }, (error, stdout, stderr) => {
+ exec(command, { ...options, encoding: 'utf8' }, (error, stdout, stderr) => {
if (error) {
this.execLog(red(`unable to execute ${white(command)}`));
- error.message.split("\n").forEach(line => line.length && this.execLog(red(`(error) ${line}`)));
+ error.message.split('\n').forEach(line => line.length && this.execLog(red(`(error) ${line}`)));
} else {
let outLines: string[], errorLines: string[];
- if ((outLines = stdout.split("\n").filter(line => line.length)).length) {
+ if ((outLines = stdout.split('\n').filter(line => line.length)).length) {
outLines.forEach(line => line.length && this.execLog(cyan(`(stdout) ${line}`)));
}
- if ((errorLines = stderr.split("\n").filter(line => line.length)).length) {
+ if ((errorLines = stderr.split('\n').filter(line => line.length)).length) {
errorLines.forEach(line => line.length && this.execLog(yellow(`(stderr) ${line}`)));
}
}
resolve();
});
});
- }
+ };
/**
* Generates a blue UTC string associated with the time
@@ -153,14 +159,14 @@ export class Monitor extends IPCMessageReceiver {
*/
public mainLog = (...optionalParams: any[]) => {
console.log(this.timestamp(), this.config.identifiers.master.text, ...optionalParams);
- }
+ };
/**
* A formatted, identified and timestamped log in color for non-
*/
private execLog = (...optionalParams: any[]) => {
console.log(this.timestamp(), this.config.identifiers.exec.text, ...optionalParams);
- }
+ };
/**
* Reads in configuration .json file only once, in the master thread
@@ -169,28 +175,28 @@ export class Monitor extends IPCMessageReceiver {
private loadAndValidateConfiguration = (): Configuration => {
let config: Configuration | undefined;
try {
- console.log(this.timestamp(), cyan("validating configuration..."));
+ console.log(this.timestamp(), cyan('validating configuration...'));
config = JSON.parse(readFileSync('./session.config.json', 'utf8'));
const options = {
throwError: true,
- allowUnknownAttributes: false
+ allowUnknownAttributes: false,
};
// ensure all necessary and no excess information is specified by the configuration file
validate(config, configurationSchema, options);
config = Utilities.preciseAssign({}, defaultConfig, config);
} catch (error: any) {
if (error instanceof ValidationError) {
- console.log(red("\nSession configuration failed."));
- console.log("The given session.config.json configuration file is invalid.");
+ console.log(red('\nSession configuration failed.'));
+ console.log('The given session.config.json configuration file is invalid.');
console.log(`${error.instance}: ${error.stack}`);
process.exit(0);
- } else if (error.code === "ENOENT" && error.path === "./session.config.json") {
- console.log(cyan("Loading default session parameters..."));
- console.log("Consider including a session.config.json configuration file in your project root for customization.");
+ } else if (error.code === 'ENOENT' && error.path === './session.config.json') {
+ console.log(cyan('Loading default session parameters...'));
+ console.log('Consider including a session.config.json configuration file in your project root for customization.');
config = Utilities.preciseAssign({}, defaultConfig);
} else {
- console.log(red("\nSession configuration failed."));
- console.log("The following unknown error occurred during configuration.");
+ console.log(red('\nSession configuration failed.'));
+ console.log('The following unknown error occurred during configuration.');
console.log(error.stack);
process.exit(0);
}
@@ -203,7 +209,7 @@ export class Monitor extends IPCMessageReceiver {
});
return config!;
}
- }
+ };
/**
* Builds the repl that allows the following commands to be typed into stdin of the master thread.
@@ -213,24 +219,24 @@ export class Monitor extends IPCMessageReceiver {
const boolean = /true|false/;
const number = /\d+/;
const letters = /[a-zA-Z]+/;
- repl.registerCommand("exit", [/clean|force/], args => this.killSession("manual exit requested by repl", args[0] === "clean", 0));
- repl.registerCommand("restart", [/clean|force/], args => this.killActiveWorker(args[0] === "clean"));
- repl.registerCommand("set", [letters, "port", number, boolean], args => this.setPort(args[0], Number(args[2]), args[3] === "true"));
- repl.registerCommand("set", [/polling/, number, boolean], args => {
+ repl.registerCommand('exit', [/clean|force/], args => this.killSession('manual exit requested by repl', args[0] === 'clean', 0));
+ repl.registerCommand('restart', [/clean|force/], args => this.killActiveWorker(args[0] === 'clean'));
+ repl.registerCommand('set', [letters, 'port', number, boolean], args => this.setPort(args[0], Number(args[2]), args[3] === 'true'));
+ repl.registerCommand('set', [/polling/, number, boolean], args => {
const newPollingIntervalSeconds = Math.floor(Number(args[1]));
if (newPollingIntervalSeconds < 0) {
- this.mainLog(red("the polling interval must be a non-negative integer"));
+ this.mainLog(red('the polling interval must be a non-negative integer'));
} else {
if (newPollingIntervalSeconds !== this.config.polling.intervalSeconds) {
this.config.polling.intervalSeconds = newPollingIntervalSeconds;
- if (args[2] === "true") {
- Monitor.IPCManager.emit("updatePollingInterval", { newPollingIntervalSeconds });
+ if (args[2] === 'true') {
+ Monitor.IPCManager.emit('updatePollingInterval', { newPollingIntervalSeconds });
}
}
}
});
return repl;
- }
+ };
private executeExitHandlers = async (reason: Error | boolean) => Promise.all(this.exitHandlers.map(handler => handler(reason)));
@@ -240,13 +246,13 @@ export class Monitor extends IPCMessageReceiver {
private killActiveWorker = async (graceful = true, isSessionEnd = false): Promise<void> => {
if (this.activeWorker && !this.activeWorker.isDead()) {
if (graceful) {
- Monitor.IPCManager.emit("manualExit", { isSessionEnd });
+ Monitor.IPCManager.emit('manualExit', { isSessionEnd });
} else {
await ServerWorker.IPCManager.destroy();
this.activeWorker.process.kill();
}
}
- }
+ };
/**
* Allows the caller to set the port at which the target (be it the server,
@@ -255,7 +261,7 @@ export class Monitor extends IPCMessageReceiver {
* at the port. Otherwise, the updated port won't be used until / unless the child
* dies on its own and triggers a restart.
*/
- private setPort = (port: "server" | "socket" | string, value: number, immediateRestart: boolean): void => {
+ private setPort = (port: 'server' | 'socket' | string, value: number, immediateRestart: boolean): void => {
if (value > 1023 && value < 65536) {
this.config.ports[port] = value;
if (immediateRestart) {
@@ -264,7 +270,7 @@ export class Monitor extends IPCMessageReceiver {
} else {
this.mainLog(red(`${port} is an invalid port number`));
}
- }
+ };
/**
* Kills the current active worker and proceeds to spawn a new worker,
@@ -272,27 +278,29 @@ export class Monitor extends IPCMessageReceiver {
*/
private spawn = async (): Promise<void> => {
await this.killActiveWorker();
- const { config: { polling, ports }, key } = this;
+ const {
+ config: { polling, ports },
+ key,
+ } = this;
this.activeWorker = fork({
pollingRoute: polling.route,
pollingFailureTolerance: polling.failureTolerance,
serverPort: ports.server,
socketPort: ports.socket,
pollingIntervalSeconds: polling.intervalSeconds,
- session_key: key
+ 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}`));
- }
-
+ };
}
export namespace Monitor {
-
export enum IntrinsicEvents {
- KeyGenerated = "key_generated",
- CrashDetected = "crash_detected",
- ServerRunning = "server_running"
+ KeyGenerated = 'key_generated',
+ CrashDetected = 'crash_detected',
+ ServerRunning = 'server_running',
}
-
-} \ No newline at end of file
+}
diff --git a/src/server/DashSession/Session/agents/promisified_ipc_manager.ts b/src/server/DashSession/Session/agents/promisified_ipc_manager.ts
index f6c8de521..76e218977 100644
--- a/src/server/DashSession/Session/agents/promisified_ipc_manager.ts
+++ b/src/server/DashSession/Session/agents/promisified_ipc_manager.ts
@@ -1,9 +1,9 @@
-import { Utilities } from "../utilities/utilities";
-import { ChildProcess } from "child_process";
+import { Utilities } from '../utilities/utilities';
+import { ChildProcess } from 'child_process';
/**
* Convenience constructor
- * @param target the process / worker to which to attach the specialized listeners
+ * @param target the process / worker to which to attach the specialized listeners
*/
export function manage(target: IPCTarget, handlers?: HandlerMap) {
return new PromisifiedIPCManager(target, handlers);
@@ -18,26 +18,30 @@ export type HandlerMap = { [name: string]: MessageHandler[] };
/**
* This will always literally be a child process. But, though setting
* up a manager in the parent will indeed see the target as the ChildProcess,
- * setting up a manager in the child will just see itself as a regular NodeJS.Process.
+ * setting up a manager in the child will just see itself as a regular NodeJS.Process.
*/
export type IPCTarget = NodeJS.Process | ChildProcess;
/**
- * Specifies a general message format for this API
+ * Specifies a general message format for this API
*/
export type Message<T = any> = {
name: string;
args?: T;
};
-export type MessageHandler<T = any> = (args: T) => (any | Promise<any>);
+export type MessageHandler<T = any> = (args: T) => any | Promise<any>;
/**
* When a message is emitted, it is embedded with private metadata
* to facilitate the resolution of promises, etc.
*/
-interface InternalMessage extends Message { metadata: Metadata; }
-interface Metadata { isResponse: boolean; id: string; }
-type InternalMessageHandler = (message: InternalMessage) => (any | Promise<any>);
+interface InternalMessage extends Message {
+ metadata: Metadata;
+}
+interface Metadata {
+ isResponse: boolean;
+ id: string;
+}
/**
* Allows for the transmission of the error's key features over IPC.
@@ -56,12 +60,12 @@ export interface Response<T = any> {
error?: ErrorLike;
}
-const destroyEvent = "__destroy__";
+const destroyEvent = '__destroy__';
/**
* This is a wrapper utility class that allows the caller process
* to emit an event and return a promise that resolves when it and all
- * other processes listening to its emission of this event have completed.
+ * other processes listening to its emission of this event have completed.
*/
export class PromisifiedIPCManager {
private readonly target: IPCTarget;
@@ -75,7 +79,7 @@ export class PromisifiedIPCManager {
this.target = target;
if (handlers) {
handlers[destroyEvent] = [this.destroyHelper];
- this.target.addListener("message", this.generateInternalHandler(handlers));
+ this.target.addListener('message', this.generateInternalHandler(handlers));
}
}
@@ -86,26 +90,27 @@ export class PromisifiedIPCManager {
*/
public emit = async <T = any>(name: string, args?: any): Promise<Response<T>> => {
if (this.isDestroyed) {
- const error = { name: "FailedDispatch", message: "Cannot use a destroyed IPC manager to emit a message." };
+ const error = { name: 'FailedDispatch', message: 'Cannot use a destroyed IPC manager to emit a message.' };
return { error };
}
return new Promise<Response<T>>(resolve => {
const messageId = Utilities.guid();
+ type InternalMessageHandler = (message: any /* MessageListener*/) => any | Promise<any>;
const responseHandler: InternalMessageHandler = ({ metadata: { id, isResponse }, args }) => {
if (isResponse && id === messageId) {
- this.target.removeListener("message", responseHandler);
+ this.target.removeListener('message', responseHandler);
resolve(args);
}
};
- this.target.addListener("message", responseHandler);
+ this.target.addListener('message', responseHandler);
const message = { name, args, metadata: { id: messageId, isResponse: false } };
if (!(this.target.send && this.target.send(message))) {
- const error: ErrorLike = { name: "FailedDispatch", message: "Either the target's send method was undefined or the act of sending failed." };
+ const error: ErrorLike = { name: 'FailedDispatch', message: "Either the target's send method was undefined or the act of sending failed." };
resolve({ error });
- this.target.removeListener("message", responseHandler);
+ this.target.removeListener('message', responseHandler);
}
});
- }
+ };
/**
* Invoked from either the parent or the child process, this allows
@@ -122,7 +127,7 @@ export class PromisifiedIPCManager {
}
resolve();
});
- }
+ };
/**
* Dispatches the dummy responses and sets the isDestroyed flag to true.
@@ -131,12 +136,12 @@ export class PromisifiedIPCManager {
const { pendingMessages } = this;
this.isDestroyed = true;
Object.keys(pendingMessages).forEach(id => {
- const error: ErrorLike = { name: "ManagerDestroyed", message: "The IPC manager was destroyed before the response could be returned." };
+ const error: ErrorLike = { name: 'ManagerDestroyed', message: 'The IPC manager was destroyed before the response could be returned.' };
const message: InternalMessage = { name: pendingMessages[id], args: { error }, metadata: { id, isResponse: true } };
this.target.send?.(message);
});
this.pendingMessages = {};
- }
+ };
/**
* This routine receives a uniquely identified message. If the message is itself a response,
@@ -145,29 +150,30 @@ export class PromisifiedIPCManager {
* which will ultimately invoke the responseHandler of the original emission and resolve the
* sender's promise.
*/
- private generateInternalHandler = (handlers: HandlerMap): MessageHandler => async (message: InternalMessage) => {
- const { name, args, metadata } = message;
- if (name && metadata && !metadata.isResponse) {
- const { id } = metadata;
- this.pendingMessages[id] = name;
- let error: Error | undefined;
- let results: any[] | undefined;
- try {
- const registered = handlers[name];
- if (registered) {
- results = await Promise.all(registered.map(handler => handler(args)));
+ private generateInternalHandler =
+ (handlers: HandlerMap): MessageHandler =>
+ async (message: InternalMessage) => {
+ const { name, args, metadata } = message;
+ if (name && metadata && !metadata.isResponse) {
+ const { id } = metadata;
+ this.pendingMessages[id] = name;
+ let error: Error | undefined;
+ let results: any[] | undefined;
+ try {
+ const registered = handlers[name];
+ if (registered) {
+ results = await Promise.all(registered.map(handler => handler(args)));
+ }
+ } catch (e: any) {
+ error = e;
+ }
+ if (!this.isDestroyed && this.target.send) {
+ const metadata = { id, isResponse: true };
+ const response: Response = { results, error };
+ const message = { name, args: response, metadata };
+ delete this.pendingMessages[id];
+ this.target.send(message);
}
- } catch (e: any) {
- error = e;
- }
- if (!this.isDestroyed && this.target.send) {
- const metadata = { id, isResponse: true };
- const response: Response = { results, error };
- const message = { name, args: response, metadata };
- delete this.pendingMessages[id];
- this.target.send(message);
}
- }
- }
-
-} \ No newline at end of file
+ };
+}
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 2053ae448..7765349ff 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -14,8 +14,8 @@ import { createIfNotExists } from './ActionUtilities';
import { clientPathToFile, Directory, pathToDirectory, serverPathToFile } from './ApiManagers/UploadManager';
import { resolvedServerUrl } from './server_Initialization';
import { AcceptableMedia, Upload } from './SharedMediaTypes';
-import request = require('request-promise');
-import formidable = require('formidable');
+import * as request from 'request-promise';
+import * as formidable from 'formidable';
import { AzureManager } from './ApiManagers/AzureManager';
import axios from 'axios';
const spawn = require('child_process').spawn;
@@ -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,22 @@ export namespace DashUploadUtils {
res({
source: {
size: 0,
- path,
- name,
- type: '',
- toJSON: () => ({ name, path }),
+ filepath: name,
+ originalFilename: name,
+ newFilename: name,
+ mimetype: 'video',
+ hashAlgorithm: 'md5',
+ toJSON: () => ({ newFilename: name, filepath, mimetype: 'video', mtime: new Date(), size: 0, length: 0, originalFilename: name }),
},
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,18 +196,18 @@ 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.
const category = types[0];
let format = `.${types[1]}`;
- console.log(green(`Processing upload of file (${name}) and format (${format}) with upload type (${type}) in category (${category}).`));
+ console.log(green(`Processing upload of file (${originalFilename}) and format (${format}) with upload type (${type}) in category (${category}).`));
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 +216,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 +282,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 +302,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
@@ -366,7 +368,7 @@ export namespace DashUploadUtils {
}
export interface ImageResizer {
- width?: any; // sharp.Sharp;
+ width: number;
suffix: SizeSuffix;
}
@@ -462,12 +464,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
@@ -622,7 +624,7 @@ export namespace DashUploadUtils {
*/
export function imageResampleSizes(ext: string): DashUploadUtils.ImageResizer[] {
return [
- { suffix: SizeSuffix.Original },
+ { suffix: SizeSuffix.Original, width: 0 },
...[...(AcceptableMedia.imageFormats.includes(ext.toLowerCase()) ? Object.values(DashUploadUtils.Sizes) : [])].map(({ suffix, width }) => ({
width,
suffix,
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/RouteManager.ts b/src/server/RouteManager.ts
index 5683cd539..540bca776 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -1,6 +1,7 @@
import { cyan, green, red } from 'colors';
import { Express, Request, Response } from 'express';
import { AdminPriviliges } from '.';
+import { Utils } from '../Utils';
import { DashUserModel } from './authentication/DashUserModel';
import RouteSubscriber from './RouteSubscriber';
@@ -102,7 +103,7 @@ export default class RouteManager {
let user = req.user as Partial<DashUserModel> | undefined;
const { originalUrl: target } = req;
if (process.env.DB === 'MEM' && !user) {
- user = { id: 'guest', email: 'guest', userDocumentId: '__guest__' };
+ user = { id: 'guest', email: 'guest', userDocumentId: Utils.GuestID() };
}
const core = { req, res, isRelease };
const tryExecute = async (toExecute: (args: any) => any | Promise<any>, args: any) => {
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index 4453b83bf..fc4656bdd 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -1,11 +1,11 @@
-import { google } from "googleapis";
-import { OAuth2Client, Credentials, OAuth2ClientOptions } from "google-auth-library";
-import { Opt } from "../../../fields/Doc";
-import { GaxiosResponse } from "gaxios";
-import request = require('request-promise');
-import * as qs from "query-string";
-import { Database } from "../../database";
-import { GoogleCredentialsLoader } from "./CredentialsLoader";
+import { google } from 'googleapis';
+import { OAuth2Client, Credentials, OAuth2ClientOptions } from 'google-auth-library';
+import { Opt } from '../../../fields/Doc';
+import { GaxiosResponse } from 'gaxios';
+import request from 'request-promise';
+import * as qs from 'query-string';
+import { Database } from '../../database';
+import { GoogleCredentialsLoader } from './CredentialsLoader';
/**
* Scopes give Google users fine granularity of control
@@ -13,33 +13,23 @@ import { GoogleCredentialsLoader } from "./CredentialsLoader";
* This is the somewhat overkill list of what Dash requests
* from the user.
*/
-const scope = [
- 'documents.readonly',
- 'documents',
- 'presentations',
- 'presentations.readonly',
- 'drive',
- 'drive.file',
- 'photoslibrary',
- 'photoslibrary.appendonly',
- 'photoslibrary.sharing',
- 'userinfo.profile'
-].map(relative => `https://www.googleapis.com/auth/${relative}`);
+const scope = ['documents.readonly', 'documents', 'presentations', 'presentations.readonly', 'drive', 'drive.file', 'photoslibrary', 'photoslibrary.appendonly', 'photoslibrary.sharing', 'userinfo.profile'].map(
+ relative => `https://www.googleapis.com/auth/${relative}`
+);
/**
* This namespace manages server side authentication for Google API queries, either
* from the standard v1 APIs or the Google Photos REST API.
*/
export namespace GoogleApiServerUtils {
-
/**
* As we expand out to more Google APIs that are accessible from
* the 'googleapis' module imported above, this enum will record
* the list and provide a unified string representation of each API.
*/
export enum Service {
- Documents = "Documents",
- Slides = "Slides",
+ Documents = 'Documents',
+ Slides = 'Slides',
}
/**
@@ -51,7 +41,7 @@ export namespace GoogleApiServerUtils {
let oAuthOptions: OAuth2ClientOptions;
/**
- * This is a global authorization client that is never
+ * This is a global authorization client that is never
* passed around, and whose credentials are never set.
* Its job is purely to generate new authentication urls
* (users will follow to get to Google's permissions GUI)
@@ -64,7 +54,7 @@ export namespace GoogleApiServerUtils {
* This function is called once before the server is started,
* reading in Dash's project-specific credentials (client secret
* and client id) for later repeated access. It also sets up the
- * global, intentionally unauthenticated worker OAuth2 client instance.
+ * global, intentionally unauthenticated worker OAuth2 client instance.
*/
export function processProjectCredentials(): void {
const { client_secret, client_id, redirect_uris } = GoogleCredentialsLoader.ProjectCredentials;
@@ -72,7 +62,7 @@ export namespace GoogleApiServerUtils {
oAuthOptions = {
clientId: client_id,
clientSecret: client_secret,
- redirectUri: redirect_uris[0]
+ redirectUri: redirect_uris[0],
};
worker = generateClient();
}
@@ -98,7 +88,7 @@ export namespace GoogleApiServerUtils {
* A literal union type indicating the valid actions for these 'googleapis'
* requestions
*/
- export type Action = "create" | "retrieve" | "update";
+ export type Action = 'create' | 'retrieve' | 'update';
/**
* An interface defining any entity on which one can invoke
@@ -135,7 +125,7 @@ export namespace GoogleApiServerUtils {
return resolve();
}
let routed: Opt<Endpoint>;
- const parameters: any = { auth, version: "v1" };
+ const parameters: any = { auth, version: 'v1' };
switch (sector) {
case Service.Documents:
routed = google.docs(parameters).documents;
@@ -165,7 +155,7 @@ export namespace GoogleApiServerUtils {
}
let client = authenticationClients.get(userId);
if (!client) {
- authenticationClients.set(userId, client = generateClient(credentials));
+ authenticationClients.set(userId, (client = generateClient(credentials)));
} else if (refreshed) {
client.setCredentials(credentials);
}
@@ -206,11 +196,11 @@ export namespace GoogleApiServerUtils {
* with a Dash user in the googleAuthentication table of the database.
* @param authenticationCode the Google-provided authentication code that the user copied
* from Google's permissions UI and pasted into the overlay.
- *
+ *
* EXAMPLE CODE: 4/sgF2A5uGg4xASHf7VQDnLtdqo3mUlfQqLSce_HYz5qf1nFtHj9YTeGs
- *
+ *
* @returns the information necessary to authenticate a client side google photos request
- * and display basic user information in the overlay on successful authentication.
+ * and display basic user information in the overlay on successful authentication.
* This can be expanded as needed by adding properties to the interface GoogleAuthenticationResult.
*/
export async function processNewUser(userId: string, authenticationCode: string): Promise<EnrichedCredentials> {
@@ -231,7 +221,7 @@ export namespace GoogleApiServerUtils {
/**
* This type represents the union of the full set of OAuth2 credentials
* and all of a Google user's publically available information. This is the strucure
- * of the JSON object we ultimately store in the googleAuthentication table of the database.
+ * of the JSON object we ultimately store in the googleAuthentication table of the database.
*/
export type EnrichedCredentials = Credentials & { userInfo: UserInfo };
@@ -259,14 +249,14 @@ export namespace GoogleApiServerUtils {
* It's pretty cool: the credentials id_token is split into thirds by periods.
* The middle third contains a base64-encoded JSON string with all the
* user info contained in the interface below. So, we isolate that middle third,
- * base64 decode with atob and parse the JSON.
+ * base64 decode with atob and parse the JSON.
* @param credentials the client credentials returned from OAuth after the user
* has executed the authentication routine
* @returns the full set of credentials in the structure in which they'll be stored
* in the database.
*/
function injectUserInfo(credentials: Credentials): EnrichedCredentials {
- const userInfo: UserInfo = JSON.parse(atob(credentials.id_token!.split(".")[1]));
+ const userInfo: UserInfo = JSON.parse(atob(credentials.id_token!.split('.')[1]));
return { ...credentials, userInfo };
}
@@ -279,7 +269,7 @@ export namespace GoogleApiServerUtils {
* @returns the credentials, or undefined if the user has no stored associated credentials,
* and a flag indicating whether or not they were refreshed during retrieval
*/
- export async function retrieveCredentials(userId: string): Promise<{ credentials: Opt<EnrichedCredentials>, refreshed: boolean }> {
+ export async function retrieveCredentials(userId: string): Promise<{ credentials: Opt<EnrichedCredentials>; refreshed: boolean }> {
let credentials = await Database.Auxiliary.GoogleAccessToken.Fetch(userId);
let refreshed = false;
if (!credentials) {
@@ -299,7 +289,7 @@ export namespace GoogleApiServerUtils {
* the Dash user id passed in. In addition to returning the credentials, it
* writes the diff to the database.
* @param credentials the credentials
- * @param userId the id of the Dash user implicitly requesting that
+ * @param userId the id of the Dash user implicitly requesting that
* his/her credentials be refreshed
* @returns the updated credentials
*/
@@ -310,19 +300,18 @@ export namespace GoogleApiServerUtils {
refreshToken: credentials.refresh_token,
client_id,
client_secret,
- grant_type: "refresh_token"
+ grant_type: 'refresh_token',
})}`;
const { access_token, expires_in } = await new Promise<any>(async resolve => {
const response = await request.post(url, headerParameters);
resolve(JSON.parse(response));
});
// expires_in is in seconds, but we're building the new expiry date in milliseconds
- const expiry_date = new Date().getTime() + (expires_in * 1000);
+ const expiry_date = new Date().getTime() + expires_in * 1000;
await Database.Auxiliary.GoogleAccessToken.Update(userId, access_token, expiry_date);
// update the relevant properties
credentials.access_token = access_token;
credentials.expiry_date = expiry_date;
return credentials;
}
-
-} \ No newline at end of file
+}
diff --git a/src/server/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts
index 52d876e95..b1b84c300 100644
--- a/src/server/authentication/AuthenticationManager.ts
+++ b/src/server/authentication/AuthenticationManager.ts
@@ -3,12 +3,12 @@ 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';
+import { check, validationResult } from 'express-validator';
/**
* GET /signup
@@ -31,14 +31,14 @@ export let getSignup = (req: Request, res: Response) => {
*/
export let postSignup = (req: Request, res: Response, next: NextFunction) => {
const email = req.body.email as String;
- req.assert('email', 'Email is not valid').isEmail();
- req.assert('password', 'Password must be at least 4 characters long').len({ min: 4 });
- req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
- req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });
+ check('email', 'Email is not valid').isEmail().run(req);
+ check('password', 'Password must be at least 4 characters long').isLength({ min: 4 }).run(req);
+ check('confirmPassword', 'Passwords do not match').equals(req.body.password).run(req);
+ check('email').normalizeEmail({ gmail_remove_dots: false }).run(req);
- const errors = req.validationErrors();
+ const errors = validationResult(req).array();
- if (errors) {
+ if (errors.length) {
return res.redirect('/signup');
}
@@ -47,7 +47,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
const model = {
email,
password,
- userDocumentId: email === 'guest' ? '__guest__' : Utils.GenerateGuid(),
+ userDocumentId: email === 'guest' ? Utils.GuestID() : Utils.GenerateGuid(),
sharingDocumentId: email === 'guest' ? 2 : Utils.GenerateGuid(),
linkDatabaseId: email === 'guest' ? 3 : Utils.GenerateGuid(),
cacheDocumentIds: '',
@@ -55,25 +55,21 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
const user = new User(model);
- User.findOne({ email }, (err: any, existingUser: any) => {
- if (err) {
- return next(err);
- }
- if (existingUser) {
- return res.redirect('/login');
- }
- user.save((err: any) => {
- if (err) {
- return next(err);
+ User.findOne({ email })
+ .then(existingUser => {
+ if (existingUser) {
+ return res.redirect('/login');
}
- req.logIn(user, err => {
- if (err) {
- return next(err);
- }
- tryRedirectToTarget(req, res);
- });
- });
- });
+ user.save()
+ .then(() => {
+ req.logIn(user, err => {
+ if (err) return next(err);
+ tryRedirectToTarget(req, res);
+ });
+ })
+ .catch(err => next(err));
+ })
+ .catch(err => next(err));
};
const tryRedirectToTarget = (req: Request, res: Response) => {
@@ -107,16 +103,18 @@ export let getLogin = (req: Request, res: Response) => {
*/
export let postLogin = (req: Request, res: Response, next: NextFunction) => {
if (req.body.email === '') {
- User.findOne({ email: 'guest' }, (err: any, user: DashUserModel) => !user && initializeGuest());
+ User.findOne({ email: 'guest' })
+ .then(user => !user && initializeGuest())
+ .catch(err => err);
req.body.email = 'guest';
req.body.password = 'guest';
} else {
- req.assert('email', 'Email is not valid').isEmail();
- req.assert('password', 'Password cannot be blank').notEmpty();
- req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });
+ check('email', 'Email is not valid').isEmail().run(req);
+ check('password', 'Password cannot be blank').notEmpty().run(req);
+ check('email').normalizeEmail({ gmail_remove_dots: false }).run(req);
}
- if (req.validationErrors()) {
+ if (validationResult(req).array().length) {
req.flash('errors', 'Unable to login at this time. Please try again.');
return res.redirect('/signup');
}
@@ -146,16 +144,10 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => {
* and destroys the user's current session.
*/
export let getLogout = (req: Request, res: Response) => {
- req.logout(emptyFunction);
- const sess = req.session;
- if (sess) {
- sess.destroy(err => {
- if (err) {
- console.log(err);
- }
- });
- }
- res.redirect('/login');
+ req.logout(err => {
+ if (err) console.log(err);
+ else res.redirect('/login');
+ });
};
export let getForgot = function (req: Request, res: Response) {
@@ -179,7 +171,7 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
});
},
function (token: string, done: any) {
- User.findOne({ email }, function (err: any, user: DashUserModel) {
+ User.findOne({ email }).then(user => {
if (!user) {
// NO ACCOUNT WITH SUBMITTED EMAIL
res.redirect('/forgotPassword');
@@ -187,9 +179,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) {
- done(null, token, user);
- });
+ user.save().then(() => done(null, token, user));
});
},
function (token: Uint16Array, user: DashUserModel, done: any) {
@@ -228,50 +218,43 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
};
export let getReset = function (req: Request, res: Response) {
- User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err: any, user: DashUserModel) {
- if (!user || err) {
- return res.redirect('/forgotPassword');
- }
- res.render('reset.pug', {
- title: 'Reset Password',
- user: req.user,
- });
- });
+ User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } })
+ .then(user => {
+ if (!user) return res.redirect('/forgotPassword');
+ res.render('reset.pug', {
+ title: 'Reset Password',
+ user: req.user,
+ });
+ })
+ .catch(err => res.redirect('/forgotPassword'));
};
export let postReset = function (req: Request, res: Response) {
async.waterfall(
[
function (done: any) {
- User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err: any, user: DashUserModel) {
- if (!user || err) {
- return res.redirect('back');
- }
+ User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } })
+ .then(user => {
+ if (!user) return res.redirect('back');
- req.assert('password', 'Password must be at least 4 characters long').len({ min: 4 });
- req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
+ check('password', 'Password must be at least 4 characters long').isLength({ min: 4 }).run(req);
+ check('confirmPassword', 'Passwords do not match').equals(req.body.password).run(req);
- if (req.validationErrors()) {
- return res.redirect('back');
- }
+ if (validationResult(req).array().length) return res.redirect('back');
- user.password = req.body.password;
- user.passwordResetToken = undefined;
- user.passwordResetExpires = undefined;
+ user.password = req.body.password;
+ user.passwordResetToken = undefined;
+ user.passwordResetExpires = undefined;
- user.save(function (err) {
- if (err) {
- res.redirect('/login');
- return;
- }
- req.logIn(user, function (err) {
- if (err) {
- return;
- }
- });
+ user.save()
+ .then(
+ () => (req as any).logIn(user),
+ (err: any) => err
+ )
+ .catch(err => res.redirect('/login'));
done(null, user);
- });
- });
+ })
+ .catch(err => res.redirect('back'));
},
function (user: DashUserModel, done: any) {
const smtpTransport = nodemailer.createTransport({
@@ -287,9 +270,8 @@ export let postReset = function (req: Request, res: Response) {
subject: 'Your password has been changed',
text: 'Hello,\n\n' + 'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n',
} as MailOptions;
- smtpTransport.sendMail(mailOptions, function (err) {
- done(null, err);
- });
+
+ smtpTransport.sendMail(mailOptions, err => done(null, err));
},
],
function (err) {
diff --git a/src/server/authentication/DashUserModel.ts b/src/server/authentication/DashUserModel.ts
index a1883beab..dbb7a79ed 100644
--- a/src/server/authentication/DashUserModel.ts
+++ b/src/server/authentication/DashUserModel.ts
@@ -2,6 +2,7 @@
import * as bcrypt from 'bcrypt-nodejs';
//@ts-ignore
import * as mongoose from 'mongoose';
+import { Utils } from '../../Utils';
export type DashUserModel = mongoose.Document & {
email: String;
@@ -25,7 +26,7 @@ export type DashUserModel = mongoose.Document & {
comparePassword: comparePasswordFunction;
};
-type comparePasswordFunction = (candidatePassword: string, cb: (err: any, isMatch: any) => {}) => void;
+type comparePasswordFunction = (candidatePassword: string, cb: (err: any, isMatch: any) => void) => void;
export type AuthToken = {
accessToken: string;
@@ -63,7 +64,7 @@ const userSchema = new mongoose.Schema(
* Password hash middleware.
*/
userSchema.pre('save', function save(next) {
- const user = this as DashUserModel;
+ const user = this as any as DashUserModel;
if (!user.isModified('password')) {
return next();
}
@@ -101,7 +102,7 @@ export function initializeGuest() {
new User({
email: 'guest',
password: 'guest',
- userDocumentId: '__guest__',
+ userDocumentId: Utils.GuestID(),
sharingDocumentId: '2',
linkDatabaseId: '3',
cacheDocumentIds: '',
diff --git a/src/server/authentication/Passport.ts b/src/server/authentication/Passport.ts
index d7f891c34..a9cf6698b 100644
--- a/src/server/authentication/Passport.ts
+++ b/src/server/authentication/Passport.ts
@@ -1,6 +1,6 @@
import * as passport from 'passport';
import * as passportLocal from 'passport-local';
-import { default as User } from './DashUserModel';
+import { DashUserModel, default as User } from './DashUserModel';
const LocalStrategy = passportLocal.Strategy;
@@ -9,21 +9,24 @@ passport.serializeUser<any, any>((req, user, done) => {
});
passport.deserializeUser<any, any>((id, done) => {
- User.findById(id, (err: any, user: any) => {
- done(err, user);
- });
+ User.findById(id)
+ .exec()
+ .then(user => done(undefined, user));
});
// AUTHENTICATE JUST WITH EMAIL AND PASSWORD
-passport.use(new LocalStrategy({ usernameField: 'email', passReqToCallback: true }, (req, email, password, done) => {
- User.findOne({ email: email.toLowerCase() }, (error: any, user: any) => {
- if (error) return done(error);
- if (!user) return done(undefined, false, { message: "Invalid email or password" }); // invalid email
- user.comparePassword(password, (error: Error, isMatch: boolean) => {
- if (error) return done(error);
- if (!isMatch) return done(undefined, false, { message: "Invalid email or password" }); // invalid password
- // valid authentication HERE
- return done(undefined, user);
- });
- });
-})); \ No newline at end of file
+passport.use(
+ new LocalStrategy({ usernameField: 'email', passReqToCallback: true }, (req, email, password, done) => {
+ User.findOne({ email: email.toLowerCase() })
+ .then(user => {
+ if (!user) return done(undefined, false, { message: 'Invalid email or password' }); // invalid email
+ (user as any as DashUserModel).comparePassword(password, (error: Error, isMatch: boolean) => {
+ if (error) return done(error);
+ if (!isMatch) return done(undefined, false, { message: 'Invalid email or password' }); // invalid password
+ // valid authentication HERE
+ return done(undefined, user);
+ });
+ })
+ .catch(error => done(error));
+ })
+);
diff --git a/src/server/database.ts b/src/server/database.ts
index 725b66836..3a087ce38 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -9,8 +9,12 @@ import { Transferable } from './Message';
import { Upload } from './SharedMediaTypes';
export namespace Database {
-
export let disconnect: Function;
+
+ class DocSchema implements mongodb.BSON.Document {
+ _id!: string;
+ id!: string;
+ }
const schema = 'Dash';
const port = 27017;
export const url = `mongodb://localhost:${port}/${schema}`;
@@ -26,7 +30,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,12 +39,11 @@ export namespace Database {
resolve();
});
mongoose.connect(url, {
- useNewUrlParser: true,
- useUnifiedTopology: true,
+ //useNewUrlParser: true,
dbName: schema,
// reconnectTries: Number.MAX_VALUE,
// reconnectInterval: 1000,
- });
+ });
});
}
} catch (e) {
@@ -60,11 +63,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) => {
- console.error("mongo connect response\n");
+ 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);
+ console.error('\nMongo connect failed with the error:\n');
process.exit(0);
}
this.db = client.db();
@@ -74,20 +76,24 @@ 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 collection = this.db.collection<DocSchema>(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: id }, value, { upsert })
+ .then(res => {
if (this.currentWrites[id] === newProm) {
delete this.currentWrites[id];
}
resolve();
- callback(err, res);
+ callback(undefined as any, res);
+ })
+ .catch(error => {
+ console.log('MOngo UPDATE ONE ERROR:', error);
});
});
};
@@ -99,21 +105,20 @@ 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 collection = this.db.collection<DocSchema>(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) => {
- if (this.currentWrites[id] === newProm) {
- delete this.currentWrites[id];
- }
- resolve();
- callback(err, res);
- });
+ collection.replaceOne({ _id: id }, value, { upsert }).then(res => {
+ if (this.currentWrites[id] === newProm) {
+ delete this.currentWrites[id];
+ }
+ resolve();
+ callback(undefined as any, res as any);
+ });
});
};
newProm = prom ? prom.then(run) : run();
@@ -135,15 +140,20 @@ 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") {
+ if (typeof id === 'string') {
id = { _id: 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))));
}
@@ -170,22 +180,27 @@ export namespace Database {
public async insert(value: any, collectionName = DocumentsCollection) {
if (this.db && value !== null) {
- if ("id" in value) {
+ if ('id' in value) {
value._id = value.id;
delete value.id;
}
const id = value._id;
- const collection = this.db.collection(collectionName);
+ const collection = this.db.collection<DocSchema>(collectionName);
const prom = this.currentWrites[id];
let newProm: Promise<void>;
const run = (): Promise<void> => {
return new Promise<void>(resolve => {
- collection.insertOne(value, (err, res) => {
- if (this.currentWrites[id] === newProm) {
- delete this.currentWrites[id];
- }
- resolve();
- });
+ collection
+ .insertOne(value)
+ .then(res => {
+ if (this.currentWrites[id] === newProm) {
+ delete this.currentWrites[id];
+ }
+ resolve();
+ })
+ .catch(err => {
+ console.log('Mongo INSERT ERROR: ', err);
+ });
});
};
newProm = prom ? prom.then(run) : run();
@@ -198,11 +213,12 @@ 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) => {
+ const collection = this.db.collection<DocSchema>(collectionName);
+ collection.findOne({ _id: id }).then(result => {
if (result) {
result.id = result._id;
- delete result._id;
- fn(result);
+ //delete result._id;
+ fn(result as any);
} else {
fn(undefined);
}
@@ -212,19 +228,19 @@ export namespace Database {
}
}
- public getDocuments(ids: string[], fn: (result: Transferable[]) => void, collectionName = DocumentsCollection) {
+ public async 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 => {
+ const found = await this.db
+ .collection<DocSchema>(collectionName)
+ .find({ _id: { $in: ids } })
+ .toArray();
+ fn(
+ found.map((doc: any) => {
doc.id = doc._id;
delete doc._id;
return doc;
- }));
- });
+ })
+ );
} else {
this.onConnect.push(() => this.getDocuments(ids, fn, collectionName));
}
@@ -257,15 +273,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);
+ let cursor = this.db.collection<DocSchema>(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,22 +290,36 @@ 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))
+ .catch(error => {
+ console.log('Mongo INSERT MANY ERROR:', error);
+ })
+ );
} else {
- return new Promise<mongodb.WriteOpResult>(res => {
- this.onConnect.push(() => this.updateMany(query, update, collectionName).then(res));
+ return new Promise<mongodb.UpdateResult>(res => {
+ this.onConnect.push(() =>
+ this.updateMany(query, update, collectionName)
+ .then(res)
+ .catch(error => {
+ console.log('Mongo UPDATAE MANY ERROR: ', error);
+ })
+ );
});
}
}
public print() {
- console.log("db says hi!");
+ console.log('db says hi!');
}
}
function getDatabase() {
switch (process.env.DB) {
- case "MEM":
+ case 'MEM':
return new MemoryDatabase();
default:
return new Database();
@@ -304,13 +334,12 @@ export namespace Database {
* or Dash-internal user data.
*/
export namespace Auxiliary {
-
/**
* All the auxiliary MongoDB collections (schemas)
*/
export enum AuxiliaryCollections {
- GooglePhotosUploadHistory = "uploadedFromGooglePhotos",
- GoogleAccess = "googleAuthentication",
+ GooglePhotosUploadHistory = 'uploadedFromGooglePhotos',
+ GoogleAccess = 'googleAuthentication',
}
/**
@@ -322,16 +351,18 @@ 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 => {
- delete result._id;
- return result;
- }) : slice;
+ return removeId
+ ? slice.map((result: any) => {
+ delete result._id;
+ return result;
+ })
+ : slice;
};
/**
* Searches for the @param query in the specified @param collection,
* and returns at most the first result. If @param removeId is true,
- * as it is by default, each object will be stripped of its database id.
+ * as it is by default, each object will be stripped of its database id.
* Worth the special case since it converts the Array return type to a single
* object of the specified type.
*/
@@ -341,7 +372,7 @@ export namespace Database {
};
/**
- * Checks to see if an image with the given @param contentSize
+ * Checks to see if an image with the given @param contentSize
* already exists in the aux database, i.e. has already been downloaded from Google Photos.
*/
export const QueryUploadHistory = async (contentSize: number) => {
@@ -355,7 +386,7 @@ export namespace Database {
export const LogUpload = async (information: Upload.ImageInformation) => {
const bundle = {
_id: Utils.GenerateDeterministicGuid(String(information.contentSize)),
- ...information
+ ...information,
};
return Instance.insert(bundle, AuxiliaryCollections.GooglePhotosUploadHistory);
};
@@ -365,7 +396,6 @@ export namespace Database {
* facilitates interactions with all their APIs for a given account.
*/
export namespace GoogleAccessToken {
-
/**
* Format stored in database.
*/
@@ -373,7 +403,7 @@ export namespace Database {
/**
* Retrieves the credentials associaed with @param userId
- * and optionally removes their database id according to @param removeId.
+ * and optionally removes their database id according to @param removeId.
*/
export const Fetch = async (userId: string, removeId = true): Promise<Opt<StoredCredentials>> => {
return SanitizedSingletonQuery<StoredCredentials>({ userId }, AuxiliaryCollections.GoogleAccess, removeId);
@@ -381,7 +411,7 @@ export namespace Database {
/**
* Writes the @param enrichedCredentials to the database, associated
- * with @param userId for later retrieval and updating.
+ * with @param userId for later retrieval and updating.
*/
export const Write = async (userId: string, enrichedCredentials: GoogleApiServerUtils.EnrichedCredentials) => {
return Instance.insert({ userId, canAccess: [], ...enrichedCredentials }, AuxiliaryCollections.GoogleAccess);
@@ -400,7 +430,7 @@ export namespace Database {
};
/**
- * Revokes the credentials associated with @param userId.
+ * Revokes the credentials associated with @param userId.
*/
export const Revoke = async (userId: string) => {
const entry = await Fetch(userId, false);
@@ -408,9 +438,6 @@ export namespace Database {
Instance.delete({ _id: entry._id }, AuxiliaryCollections.GoogleAccess);
}
};
-
}
-
}
-
}
diff --git a/src/server/index.ts b/src/server/index.ts
index 8b2e18847..745653a19 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -1,4 +1,4 @@
-require('dotenv').config();
+import * as dotenv from 'dotenv';
import { yellow } from 'colors';
import * as mobileDetect from 'mobile-detect';
import * as path from 'path';
@@ -26,7 +26,7 @@ import { Logger } from './ProcessFactory';
import RouteManager, { Method, PublicHandler } from './RouteManager';
import RouteSubscriber from './RouteSubscriber';
import initializeServer, { resolvedPorts } from './server_Initialization';
-
+dotenv.config();
export const AdminPriviliges: Map<string, boolean> = new Map();
export const onWindows = process.platform === 'win32';
export let sessionAgent: AppliedSessionAgent;
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index ccb709453..afc6231e5 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -1,13 +1,9 @@
import * as bodyParser from 'body-parser';
import { blue, yellow } from 'colors';
-import * as cookieParser from 'cookie-parser';
import * as cors from 'cors';
import * as express from 'express';
import * as session from 'express-session';
-import * as expressValidator from 'express-validator';
-import * as fs from 'fs';
-import { Server as HttpServer } from 'http';
-import { createServer, Server as HttpsServer } from 'https';
+import { createServer } from 'https';
import * as passport from 'passport';
import * as request from 'request';
import * as webpack from 'webpack';
@@ -22,12 +18,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 expressFlash from 'express-flash';
+import * as flash from 'connect-flash';
+import * as brotli from 'brotli';
+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,32 +35,23 @@ export let resolvedServerUrl: string;
export default async function InitializeServer(routeSetter: RouteSetter) {
const isRelease = determineEnvironment();
const app = buildWithMiddleware(express());
-
- const compiler = webpack(config);
-
- app.use(
- require('webpack-dev-middleware')(compiler, {
- publicPath: config.output.publicPath,
- })
- );
-
- app.use(require('webpack-hot-middleware')(compiler));
+ const compiler = webpack(config as any);
// route table managed by express. routes are tested sequentially against each of these map rules. when a match is found, the handler is called to process the request
+ app.use(wdm(compiler, { publicPath: config.output.publicPath }));
+ app.use(whm(compiler));
app.get(new RegExp(/^\/+$/), (req, res) => res.redirect(req.user ? '/home' : '/login')); // target urls that consist of one or more '/'s with nothing in between
app.use(express.static(publicDirectory, { setHeaders: res => res.setHeader('Access-Control-Allow-Origin', '*') })); //all urls that start with dash's public directory: /files/ (e.g., /files/images, /files/audio, etc)
app.use(cors({ origin: (_origin: any, callback: any) => callback(null, true) }));
- app.use(wdm(compiler, { publicPath: config.output.publicPath }));
- app.use(whm(compiler));
registerAuthenticationRoutes(app); // this adds routes to authenticate a user (login, etc)
registerCorsProxy(app); // this adds a /corsProxy/ route to allow clients to get to urls that would otherwise be blocked by cors policies
isRelease && !SSL.Loaded && SSL.exit();
routeSetter(new RouteManager(app, isRelease)); // this sets up all the regular supervised routes (things like /home, download/upload api's, pdf, search, session, etc)
registerEmbeddedBrowseRelativePathHandler(app); // this allows renered web pages which internally have relative paths to find their content
- let server: HttpServer | HttpsServer;
isRelease && process.env.serverPort && (resolvedPorts.server = Number(process.env.serverPort));
- await new Promise<void>(resolve => (server = isRelease ? createServer(SSL.Credentials, app).listen(resolvedPorts.server, resolve) : app.listen(resolvedPorts.server, resolve)));
+ const server = isRelease ? createServer(SSL.Credentials, app) : app;
+ await new Promise<void>(resolve => server.listen(resolvedPorts.server, resolve));
logPort('server', resolvedPorts.server);
resolvedServerUrl = `${isRelease && process.env.serverName ? `https://${process.env.serverName}.com` : 'http://localhost'}:${resolvedPorts.server}`;
@@ -80,26 +66,26 @@ export default async function InitializeServer(routeSetter: RouteSetter) {
const week = 7 * 24 * 60 * 60 * 1000;
const secret = '64d6866242d3b5a5503c675b32c9605e4e90478e9b77bcf2bc';
+const store = process.env.DB === 'MEM' ? new session.MemoryStore() : MongoStoreConnect.create({ mongoUrl: Database.url });
function buildWithMiddleware(server: express.Express) {
[
- cookieParser(),
session({
secret,
- resave: true,
+ resave: false,
cookie: { maxAge: week },
saveUninitialized: true,
- store: process.env.DB === 'MEM' ? new session.MemoryStore() : new MongoStore({ url: Database.url }),
+ store,
}),
flash(),
expressFlash(),
bodyParser.json({ limit: '10mb' }),
bodyParser.urlencoded({ extended: true }),
- expressValidator(),
passport.initialize(),
passport.session(),
(req: express.Request, res: express.Response, next: express.NextFunction) => {
res.locals.user = req.user;
+ // console.log('HEADER:' + req.originalUrl + ' path = ' + req.path);
if ((req.originalUrl.endsWith('.png') || req.originalUrl.endsWith('.jpg') || (process.env.RELEASE === 'true' && req.originalUrl.endsWith('.js'))) && req.method === 'GET') {
const period = 30000;
res.set('Cache-control', `public, max-age=${period}`);
@@ -110,6 +96,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..a26b81bdf 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -1,10 +1,8 @@
import { blue } from 'colors';
import * as express from 'express';
-import { createServer, Server } from 'https';
+import { createServer } from 'https';
+import { Server, Socket } from '../../node_modules/socket.io/dist/index';
import { networkInterfaces } from 'os';
-import * as sio from 'socket.io';
-import { Socket } from 'socket.io';
-import { Opt } from '../fields/Doc';
import { Utils } from '../Utils';
import { logPort } from './ActionUtilities';
import { timeMap } from './ApiManagers/UserManager';
@@ -18,31 +16,31 @@ import { DocumentsCollection } from './IDatabase';
import { Diff, GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, Transferable, Types, UpdateMobileInkOverlayPositionContent, YoutubeQueryInput, YoutubeQueryTypes } from './Message';
import { Search } from './Search';
import { resolvedPorts } from './server_Initialization';
-var _ = require('lodash');
+import * as _ from 'lodash';
export namespace WebSocket {
export let _socket: Socket;
export const clients: { [key: string]: Client } = {};
- export const socketMap = new Map<SocketIO.Socket, string>();
+ export const socketMap = new Map<Socket, string>();
export const userOperations = new Map<string, number>();
export let disconnect: Function;
export async function initialize(isRelease: boolean, app: express.Express) {
- let io: sio.Server;
+ let io: Server;
if (isRelease) {
const { socketPort } = process.env;
if (socketPort) {
resolvedPorts.socket = Number(socketPort);
}
- let socketEndpoint: Opt<Server>;
- await new Promise<void>(resolve => (socketEndpoint = createServer(SSL.Credentials, app).listen(resolvedPorts.socket, resolve)));
- io = sio(socketEndpoint!, SSL.Credentials as any);
+ io = new Server(createServer(SSL.Credentials, app), SSL.Credentials as any);
+ io.listen(resolvedPorts.socket);
} else {
- io = sio().listen(resolvedPorts.socket);
+ io = new Server();
+ io.listen(resolvedPorts.socket);
}
logPort('websocket', resolvedPorts.socket);
- io.on('connection', function (socket: Socket) {
+ io.on('connection', socket => {
_socket = socket;
socket.use((_packet, next) => {
const userEmail = socketMap.get(socket);
@@ -67,8 +65,8 @@ 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 numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;
+ const clientsInRoom = socket.rooms.has(room);
+ const numClients = clientsInRoom ? Object.keys(room.sockets).length : 0;
console.log('Room ' + room + ' now has ' + numClients + ' client(s)');
if (numClients === 0) {
@@ -90,7 +88,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);
}
@@ -191,7 +189,7 @@ export namespace WebSocket {
initializeGuest();
}
- function barReceived(socket: SocketIO.Socket, userEmail: string) {
+ function barReceived(socket: Socket, userEmail: string) {
clients[userEmail] = new Client(userEmail.toString());
const currentdate = new Date();
const datetime = currentdate.getDate() + '/' + (currentdate.getMonth() + 1) + '/' + currentdate.getFullYear() + ' @ ' + currentdate.getHours() + ':' + currentdate.getMinutes() + ':' + currentdate.getSeconds();
@@ -308,9 +306,9 @@ export namespace WebSocket {
if (sendBack) {
console.log('Warning: list modified during update. Composite list is being returned.');
const id = socket.id;
- socket.id = '';
+ (socket as any).id = '';
socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
- socket.id = id;
+ (socket as any).id = id;
} else socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
dispatchNextOp(diff.id);
},
@@ -401,9 +399,9 @@ export namespace WebSocket {
// the two copies are different, so the server sends its copy.
console.log('SEND BACK');
const id = socket.id;
- socket.id = '';
+ (socket as any).id = '';
socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
- socket.id = id;
+ (socket as any).id = id;
} else socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
dispatchNextOp(diff.id);
},
diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts
index 23e4680fd..284aae338 100644
--- a/src/typings/index.d.ts
+++ b/src/typings/index.d.ts
@@ -12,7 +12,13 @@ declare module 'iink-js';
declare module 'reveal';
declare module 'react-reveal';
declare module 'react-reveal/makeCarousel';
-declare module 'react-resizable-rotatable-draggable';
+declare module '@hig/flyout';
+declare module 'express-flash';
+declare module 'connect-flash' {
+ interface flash {}
+}
+declare module 'connect-mongo';
+declare module '@mui/material';
declare module '@react-pdf/renderer' {
import * as React from 'react';