aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2023-12-05 20:29:53 -0500
committerbobzel <zzzman@gmail.com>2023-12-05 20:29:53 -0500
commitb80d27912cd6d8bc4fe039e52d16582bfbe72c74 (patch)
treee088386dc4e4d974fbb52d74054ec5aa937a8ef0 /src
parent2bd239e39264a362d1fbb013ce2613d03247d78e (diff)
mostly working version with latest libraries
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts28
-rw-r--r--src/client/DocServer.ts42
-rw-r--r--src/client/documents/Documents.ts7
-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/Scripting.ts31
-rw-r--r--src/client/util/SharingManager.tsx2
-rw-r--r--src/client/views/InkTranscription.tsx700
-rw-r--r--src/client/views/InkingStroke.tsx4
-rw-r--r--src/client/views/MainView.tsx6
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.tsx6
-rw-r--r--src/client/views/collections/TreeView.tsx39
-rw-r--r--src/client/views/nodes/DataVizBox/components/TableBox.tsx3
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx10
-rw-r--r--src/debug/Test.tsx18
-rw-r--r--src/fields/Doc.ts20
-rw-r--r--src/fields/ScriptField.ts8
-rw-r--r--src/mobile/ImageUpload.tsx4
-rw-r--r--src/server/ApiManagers/DeleteManager.ts36
-rw-r--r--src/server/ApiManagers/UserManager.ts5
-rw-r--r--src/server/DashUploadUtils.ts2
-rw-r--r--src/server/RouteManager.ts3
-rw-r--r--src/server/authentication/AuthenticationManager.ts117
-rw-r--r--src/server/authentication/DashUserModel.ts7
-rw-r--r--src/server/authentication/Passport.ts35
-rw-r--r--src/server/database.ts131
-rw-r--r--src/server/server_Initialization.ts34
-rw-r--r--src/server/websocket.ts35
-rw-r--r--src/typings/index.d.ts1
30 files changed, 706 insertions, 673 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 38a59d524..08ddfa817 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -1,6 +1,8 @@
import * as v4 from 'uuid/v4';
import * as v5 from 'uuid/v5';
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 { DocumentType } from './client/documents/DocumentTypes';
import { Colors } from './client/views/global/globalEnums';
@@ -54,6 +56,14 @@ export namespace Utils {
return v5(seed, v5.URL);
}
+ export function GenerateMongoId(id: string): string {
+ return id.length !== 36 ? Utils.GenerateDeterministicGuid(id) : id;
+ }
+
+ export function GuestID() {
+ return '__guest__';
+ }
+
/**
* Uploads an image buffer to the server and stores with specified filename. by default the image
* is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large)
@@ -397,14 +407,14 @@ export namespace Utils {
};
}
- export function Emit<T>(socket: SocketIOClient.Socket, message: Message<T>, args: T) {
+ export function Emit<T>(socket: { emit: (msg: string, args: T) => void }, message: Message<T>, args: T) {
log('Emit', message.Name, args, false);
socket.emit(message.Message, args);
}
- export function EmitCallback<T>(socket: SocketIOClient.Socket, message: Message<T>, args: T): Promise<any>;
- export function EmitCallback<T>(socket: SocketIOClient.Socket, message: Message<T>, args: T, fn: (args: any) => any): void;
- export function EmitCallback<T>(socket: SocketIOClient.Socket, message: Message<T>, args: T, fn?: (args: any) => any): void | Promise<any> {
+ 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));
@@ -413,20 +423,20 @@ export namespace Utils {
}
}
- export function AddServerHandler<T>(socket: SocketIOClient.Socket, message: Message<T>, handler: (args: T) => any) {
+ export function AddServerHandler<T>(socket: { on: (event: string, cb: (args: any) => void) => void }, message: Message<T>, handler: (args: T) => any) {
socket.on(message.Message, loggingCallback('Incoming', handler, message.Name));
}
- export function AddServerHandlerCallback<T>(socket: SocketIOClient.Socket, message: Message<T>, handler: (args: [T, (res: any) => any]) => any) {
+ export function AddServerHandlerCallback<T>(socket: { on: (event: string, cb: (arg: T, fn: (res: any) => any) => void) => void }, message: Message<T>, handler: (args: [T, (res: any) => any]) => any) {
socket.on(message.Message, (arg: T, fn: (res: any) => any) => {
log('S receiving', message.Name, arg, true);
handler([arg, loggingCallback('S sending', fn, message.Name)]);
});
}
- export type RoomHandler = (socket: SocketIOClient.Socket, room: string) => any;
- export type UsedSockets = SocketIOClient.Socket;
+ export type RoomHandler = (socket: Socket, room: string) => any;
+ export type UsedSockets = Socket;
export type RoomMessage = 'create or join' | 'created' | 'joined';
- export function AddRoomHandler(socket: SocketIOClient.Socket, message: RoomMessage, handler: RoomHandler) {
+ export function AddRoomHandler(socket: Socket, message: RoomMessage, handler: RoomHandler) {
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/documents/Documents.ts b/src/client/documents/Documents.ts
index df81e12f0..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];
};
@@ -1918,7 +1919,7 @@ export namespace DocUtils {
overwriteDoc.loadingError = (result as any).message;
LoadingBox.removeCurrentlyLoading(overwriteDoc);
}
- } else newFilename && processFileupload(generatedDocuments, newFilename, mimetype??"", result, options, overwriteDoc);
+ } else newFilename && 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/Scripting.ts b/src/client/util/Scripting.ts
index 400b63a1c..b3c694024 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -120,6 +120,35 @@ class ScriptingCompilerHost {
}
return undefined;
}
+ resolveModuleNames(moduleNames: string[], containingFile: string): any {
+ const resolvedModules: ts.ResolvedModule[] = [];
+ for (const moduleName of moduleNames) {
+ // try to use standard resolution
+ let result = ts.resolveModuleName(
+ moduleName,
+ containingFile,
+ {},
+ {
+ fileExists: (fileName: string) => true as any,
+ readFile: (fileName: string) => 'true',
+ }
+ );
+ if (result.resolvedModule) {
+ resolvedModules.push(result.resolvedModule);
+ } else {
+ // check fallback locations, for simplicity assume that module at location
+ // should be represented by '.d.ts' file
+ // for (const location of moduleSearchLocations) {
+ // const modulePath = path.join(location, moduleName + ".d.ts");
+ // if (fileExists(modulePath)) {
+ // resolvedModules.push({ resolvedFileName: modulePath });
+ // }
+ // }
+ }
+ }
+ return resolvedModules;
+ }
+
// getDefaultLibFileName(options: ts.CompilerOptions): string {
getDefaultLibFileName(options: any): string {
return 'node_modules/typescript/lib/lib.d.ts'; // No idea what this means...
@@ -247,7 +276,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/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/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 13df352e3..41a2507f9 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -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/MainView.tsx b/src/client/views/MainView.tsx
index 53182497c..1408e3124 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -43,7 +43,6 @@ 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';
@@ -71,6 +70,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 +1065,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/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/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index b5666b917..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';
@@ -36,6 +35,8 @@ import { CollectionView } from './CollectionView';
import { TreeSort } from './TreeSort';
import './TreeView.scss';
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/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/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index d10cbac5e..1b5161e47 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -13,8 +13,8 @@ import { FieldView, FieldViewProps } from './FieldView';
import './LinkAnchorBox.scss';
import { LinkDocPreview } from './LinkDocPreview';
import * as React from 'react';
-import globalCssVariables = require('../global/globalCssVariables.scss');
-
+// 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/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 525ff7ec0..d441dafec 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,7 +816,7 @@ export namespace Doc {
}
export function FindReferences(infield: Doc | List<any>, references: Set<Doc>, system: boolean | undefined) {
- if (infield instanceof List<any>) {
+ if (!(infield instanceof Doc)) {
infield.forEach(val => (val instanceof Doc || val instanceof List) && FindReferences(val, references, system));
return;
}
@@ -1201,14 +1201,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/ScriptField.ts b/src/fields/ScriptField.ts
index cd07a8885..03dc71c48 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -45,12 +45,12 @@ const scriptSchema = createSimpleSchema({
function finalizeScript(script: ScriptField) {
const comp = CompileScript(script.script.originalScript, script.script.options);
if (!comp.compiled) {
- throw new Error("Couldn't compile loaded script");
+ // throw new Error("Couldn't compile loaded script");
}
if (script.setterscript) {
const compset = CompileScript(script.setterscript?.originalScript, script.setterscript.options);
if (!compset.compiled) {
- throw new Error("Couldn't compile setter script");
+ // throw new Error("Couldn't compile setter script");
}
(script as any).setterscript = compset;
}
@@ -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 fd119e0bf..ee212e4af 100644
--- a/src/mobile/ImageUpload.tsx
+++ b/src/mobile/ImageUpload.tsx
@@ -5,7 +5,6 @@ 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';
@@ -15,7 +14,8 @@ import { Utils } from '../Utils';
import './ImageUpload.scss';
import { MobileInterface } from './MobileInterface';
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)
}
diff --git a/src/server/ApiManagers/DeleteManager.ts b/src/server/ApiManagers/DeleteManager.ts
index 56d8aff60..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 } from "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/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index 9252202b0..0d36ee957 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -7,6 +7,7 @@ import { Opt } from '../../fields/Doc';
import { WebSocket } from '../websocket';
import { resolvedPorts } from '../server_Initialization';
import { DashVersion } from '../../fields/DocSymbols';
+import { Utils } from '../../Utils';
export const timeMap: { [id: string]: number } = {};
interface ActivityUnit {
@@ -49,7 +50,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 +82,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({
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index 643626ae9..2bea15915 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -200,7 +200,7 @@ export namespace DashUploadUtils {
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':
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/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts
index 74d8d2523..5bc6e96b4 100644
--- a/src/server/authentication/AuthenticationManager.ts
+++ b/src/server/authentication/AuthenticationManager.ts
@@ -46,7 +46,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: '',
@@ -54,25 +54,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().then(undefined, (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) => {
@@ -106,7 +102,9 @@ 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 {
@@ -146,14 +144,7 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => {
*/
export let getLogout = (req: Request, res: Response) => {
req.logout(emptyFunction);
- const sess = req.session;
- if (sess) {
- sess.destroy(err => {
- if (err) {
- console.log(err);
- }
- });
- }
+ req.session?.destroy(err => err && console.log(err));
res.redirect('/login');
};
@@ -178,7 +169,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');
@@ -186,9 +177,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().then(undefined, (err: any) => {
- done(null, token, user);
- });
+ user.save().then(() => done(null, token, user));
});
},
function (token: Uint16Array, user: DashUserModel, done: any) {
@@ -227,50 +216,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);
+ 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);
- if (req.validationErrors()) {
- return res.redirect('back');
- }
+ if (req.validationErrors()) 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().then(undefined, (err:any) => {
- 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({
@@ -286,9 +268,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 37bc00a85..0893bfd35 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -7,11 +7,14 @@ import { DocumentsCollection, IDatabase } from './IDatabase';
import { MemoryDatabase } from './MemoryDatabase';
import { Transferable } from './Message';
import { Upload } from './SharedMediaTypes';
-import { ObjectId } from 'mongodb';
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}`;
@@ -36,11 +39,11 @@ export namespace Database {
resolve();
});
mongoose.connect(url, {
- //useNewUrlParser: true,
+ //useNewUrlParser: true,
dbName: schema,
// reconnectTries: Number.MAX_VALUE,
// reconnectInterval: 1000,
- });
+ });
});
}
} catch (e) {
@@ -60,10 +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, }).then(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.error('\nMongo connect failed with the error:\n');
process.exit(0);
}
this.db = client.db();
@@ -75,18 +78,18 @@ export namespace Database {
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: new ObjectId(id) }, value, { upsert }).then(res => {
- if (this.currentWrites[id] === newProm) {
- delete this.currentWrites[id];
- }
- resolve();
- callback(undefined as any, res);
- });
+ collection.updateOne({ _id: id }, value, { upsert }).then(res => {
+ if (this.currentWrites[id] === newProm) {
+ delete this.currentWrites[id];
+ }
+ resolve();
+ callback(undefined as any, res);
+ });
});
};
newProm = prom ? prom.then(run) : run();
@@ -99,18 +102,18 @@ export namespace Database {
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: new ObjectId(id)}, value, { upsert }).then( res => {
- if (this.currentWrites[id] === newProm) {
- delete this.currentWrites[id];
- }
- resolve();
- callback(undefined as any, res as any);
- });
+ 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,12 +138,17 @@ export namespace Database {
public delete(query: any, collectionName?: string): Promise<mongodb.DeleteResult>;
public delete(id: string, collectionName?: string): Promise<mongodb.DeleteResult>;
public delete(id: any, collectionName = DocumentsCollection) {
- if (typeof id === "string") {
- id = { _id: new ObjectId(id) };
+ if (typeof id === 'string') {
+ id = { _id: id };
}
if (this.db) {
const db = this.db;
- return new Promise(res => db.collection(collectionName).deleteMany(id).then(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))));
}
@@ -167,12 +175,12 @@ 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> => {
@@ -195,11 +203,12 @@ export namespace Database {
public getDocument(id: string, fn: (result?: Transferable) => void, collectionName = DocumentsCollection) {
if (this.db) {
- this.db.collection(collectionName).findOne({ _id: new ObjectId(id) }).then(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.id);
+ fn(result as any);
} else {
fn(undefined);
}
@@ -209,19 +218,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.map(id => new ObjectId(id)) } }).map(docs => {
- // if (err) {
- // console.log(err.message);
- // console.log(err.errmsg);
- // }
- fn(docs.map((doc:any) => {
+ 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));
}
@@ -256,7 +265,7 @@ export namespace Database {
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);
}
@@ -271,7 +280,12 @@ export namespace Database {
public updateMany(query: any, update: any, collectionName = DocumentsCollection) {
if (this.db) {
const db = this.db;
- return new Promise<mongodb.UpdateResult>(res => db.collection(collectionName).updateMany(query, update).then(result => res(result)));
+ return new Promise<mongodb.UpdateResult>(res =>
+ db
+ .collection(collectionName)
+ .updateMany(query, update)
+ .then(result => res(result))
+ );
} else {
return new Promise<mongodb.UpdateResult>(res => {
this.onConnect.push(() => this.updateMany(query, update, collectionName).then(res));
@@ -280,13 +294,13 @@ export namespace Database {
}
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();
@@ -301,13 +315,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',
}
/**
@@ -319,16 +332,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:any) => {
- 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.
*/
@@ -338,7 +353,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) => {
@@ -352,7 +367,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);
};
@@ -362,7 +377,6 @@ export namespace Database {
* facilitates interactions with all their APIs for a given account.
*/
export namespace GoogleAccessToken {
-
/**
* Format stored in database.
*/
@@ -370,7 +384,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);
@@ -378,7 +392,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);
@@ -397,7 +411,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);
@@ -405,9 +419,6 @@ export namespace Database {
Instance.delete({ _id: entry._id }, AuxiliaryCollections.GoogleAccess);
}
};
-
}
-
}
-
}
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index af8b8dfdd..a4deaa744 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -1,13 +1,10 @@
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,10 +19,10 @@ import { Database } from './database';
import RouteManager from './RouteManager';
import RouteSubscriber from './RouteSubscriber';
import { WebSocket } from './websocket';
-import * as brotli from 'brotli';
import * as expressFlash from 'express-flash';
import * as flash from 'connect-flash';
-import * as MongoStoreConnect from 'connect-mongo';
+import * as 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
@@ -41,29 +38,21 @@ export default async function InitializeServer(routeSetter: RouteSetter) {
const app = buildWithMiddleware(express());
const compiler = webpack(config as any);
- app.use(
- require('webpack-dev-middleware')(compiler, {
- publicPath: config.output.publicPath,
- })
- );
-
- app.use(require('webpack-hot-middleware')(compiler));
-
// 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}`;
@@ -78,26 +67,27 @@ export default async function InitializeServer(routeSetter: RouteSetter) {
const week = 7 * 24 * 60 * 60 * 1000;
const secret = '64d6866242d3b5a5503c675b32c9605e4e90478e9b77bcf2bc';
+const store = process.env.DB === 'MEM' || true ? new session.MemoryStore() : MongoStoreConnect.create({ mongoUrl: Database.url });
function buildWithMiddleware(server: express.Express) {
[
- cookieParser(),
session({
secret,
resave: true,
cookie: { maxAge: week },
saveUninitialized: true,
- store: process.env.DB === 'MEM' ? new session.MemoryStore() : MongoStoreConnect.create({ mongoUrl: Database.url }),
+ store,
}),
flash(),
expressFlash(),
bodyParser.json({ limit: '10mb' }),
bodyParser.urlencoded({ extended: true }),
- expressValidator.body,
+ expressValidator(), // adds functions (e.g., assert()) to 'req' that help validate the request in the route handling methods
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}`);
@@ -108,7 +98,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 4453001b0..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 * as _socket from 'socket.io';
-import { Opt } from '../fields/Doc';
import { Utils } from '../Utils';
import { logPort } from './ActionUtilities';
import { timeMap } from './ApiManagers/UserManager';
@@ -18,32 +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');
-type Socket = typeof _socket;
+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);
@@ -69,7 +66,7 @@ export namespace WebSocket {
console.log('Received request to create or join room ' + room);
const clientsInRoom = socket.rooms.has(room);
- const numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;
+ const numClients = clientsInRoom ? Object.keys(room.sockets).length : 0;
console.log('Room ' + room + ' now has ' + numClients + ' client(s)');
if (numClients === 0) {
@@ -192,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();
@@ -309,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);
},
@@ -402,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 884e55fc7..6a1afbb03 100644
--- a/src/typings/index.d.ts
+++ b/src/typings/index.d.ts
@@ -13,7 +13,6 @@ declare module 'reveal';
declare module 'react-reveal';
declare module 'react-reveal/makeCarousel';
declare module 'react-resizable-rotatable-draggable';
-declare module 'socket.io';
declare module '@hig/flyout';
declare module 'uuid/v4';
declare module 'uuid/v5';