aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/Main.tsx24
-rw-r--r--src/client/views/collections/CollectionFreeFormView.tsx38
-rw-r--r--src/client/views/collections/CollectionViewBase.tsx44
-rw-r--r--src/fields/TupleField.ts2
-rw-r--r--src/server/ServerUtil.ts2
-rw-r--r--src/server/authentication/models/user_utils.ts22
-rw-r--r--src/server/index.ts179
7 files changed, 179 insertions, 132 deletions
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 268f70de1..fd756972b 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -39,6 +39,8 @@ import { faFilm } from '@fortawesome/free-solid-svg-icons';
import { faMusic } from '@fortawesome/free-solid-svg-icons';
import Measure from 'react-measure';
import { DashUserModel } from '../../server/authentication/models/user_model';
+import { ServerUtils } from '../../server/ServerUtil';
+import { UserUtils } from '../../server/authentication/models/user_utils';
@observer
export class Main extends React.Component {
@@ -61,6 +63,8 @@ export class Main extends React.Component {
this.mainDocId = pathname[pathname.length - 1];
}
+ UserUtils.loadCurrentUserId();
+
library.add(faFont);
library.add(faImage);
library.add(faFilePdf);
@@ -77,14 +81,6 @@ export class Main extends React.Component {
Documents.initProtos(() => {
this.initAuthenticationRouters();
});
-
- request.get(this.prepend(RouteStore.getCurrUser), (error, response, body) => {
- if (body) {
- this.currentUser = body as DashUserModel;
- } else {
- throw new Error("There should be a user! Why does Dash think there isn't one?")
- }
- })
}
initEventListeners = () => {
@@ -101,7 +97,7 @@ export class Main extends React.Component {
initAuthenticationRouters = () => {
// Load the user's active workspace, or create a new one if initial session after signup
- request.get(this.prepend(RouteStore.getActiveWorkspace), (error, response, body) => {
+ request.get(ServerUtils.prepend(RouteStore.getActiveWorkspace), (error, response, body) => {
if (this.mainDocId || body) {
Server.GetField(this.mainDocId || body, field => {
if (field instanceof Document) {
@@ -122,7 +118,7 @@ export class Main extends React.Component {
createNewWorkspace = (init: boolean): void => {
let mainDoc = Documents.DockDocument(JSON.stringify({ content: [{ type: 'row', content: [] }] }), { title: `Main Container ${this.userWorkspaces.length + 1}` });
let newId = mainDoc.Id;
- request.post(this.prepend(RouteStore.addWorkspace), {
+ request.post(ServerUtils.prepend(RouteStore.addWorkspace), {
body: { target: newId },
json: true
}, () => { if (init) this.populateWorkspaces(); });
@@ -141,7 +137,7 @@ export class Main extends React.Component {
@action
populateWorkspaces = () => {
// retrieve all workspace documents from the server
- request.get(this.prepend(RouteStore.getAllWorkspaces), (error, res, body) => {
+ request.get(ServerUtils.prepend(RouteStore.getAllWorkspaces), (error, res, body) => {
let ids = JSON.parse(body) as string[];
Server.GetFields(ids, action((fields: { [id: string]: Field }) => this.userWorkspaces = ids.map(id => fields[id] as Document)));
});
@@ -149,7 +145,7 @@ export class Main extends React.Component {
@action
openWorkspace = (doc: Document): void => {
- request.post(this.prepend(RouteStore.setActiveWorkspace), {
+ request.post(ServerUtils.prepend(RouteStore.setActiveWorkspace), {
body: { target: doc.Id },
json: true
});
@@ -163,8 +159,6 @@ export class Main extends React.Component {
}
}
- prepend = (extension: string) => window.location.origin + extension;
-
render() {
let imgRef = React.createRef<HTMLDivElement>();
let pdfRef = React.createRef<HTMLDivElement>();
@@ -233,7 +227,7 @@ export class Main extends React.Component {
<div className="main-buttonDiv" style={{ top: '34px', left: '2px', position: 'absolute' }} ref={workspacesRef}>
<button onClick={this.toggleWorkspaces}>Workspaces</button></div>
<div className="main-buttonDiv" style={{ top: '34px', right: '1px', position: 'absolute' }} ref={logoutRef}>
- <button onClick={() => request.get(this.prepend(RouteStore.logout), () => { })}>Log Out</button></div>
+ <button onClick={() => request.get(ServerUtils.prepend(RouteStore.logout), () => { })}>Log Out</button></div>
<WorkspacesMenu active={this.mainContainer} open={this.openWorkspace} new={this.createNewWorkspace} allWorkspaces={this.userWorkspaces} />
{/* for the expandable add nodes menu. Not included with the above because once it expands it expands the whole div with it, making canvas interactions limited. */}
diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx
index a3a596c37..89edd1397 100644
--- a/src/client/views/collections/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/CollectionFreeFormView.tsx
@@ -322,7 +322,7 @@ export class CollectionFreeFormView extends CollectionViewBase {
return (
<div className={`collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`}
onPointerDown={this.onPointerDown}
- onPointerMove={(e) => super.setCursorPosition(this.props.ScreenToLocalTransform().transformPoint(e.screenX, e.screenY))}
+ onPointerMove={(e) => super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY))}
onWheel={this.onPointerWheel}
onDrop={this.onDrop.bind(this)}
onDragOver={this.onDragOver}
@@ -330,20 +330,6 @@ export class CollectionFreeFormView extends CollectionViewBase {
style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px`, }}
tabIndex={0}
ref={this.createDropTarget}>
- {super.getCursors().map(entry => {
- let point = entry.Data[1]
- return (
- <div
- style={{
- position: 'absolute',
- left: point[0],
- top: point[1],
- borderRadius: "50%",
- background: "pink"
- }}
- />
- );
- })}
<div className="collectionfreeformview"
style={{ transformOrigin: "left top", transform: `translate(${dx}px, ${dy}px) scale(${this.zoomScaling}, ${this.zoomScaling}) translate(${panx}px, ${pany}px)` }}
ref={this._canvasRef}>
@@ -351,11 +337,33 @@ export class CollectionFreeFormView extends CollectionViewBase {
<InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} />
<PreviewCursor container={this} addLiveTextDocument={this.addLiveTextBox} getTransform={this.getTransform} />
{this.views}
+ {super.getCursors().map(entry => {
+ if (entry.Data.length > 0) {
+ let point = entry.Data[1]
+ return (
+ <div
+ key={entry.Data[0]}
+ style={{
+ position: 'absolute',
+ transform: `translate(${point[0] - 10}px, ${point[1] - 10}px)`,
+ zIndex: 10000,
+ transformOrigin: 'center center',
+ width: "20px",
+ height: "20px",
+ borderRadius: "50%",
+ background: "pink",
+ border: "2px solid black"
+ }}
+ />
+ );
+ }
+ })}
</div>
<MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments}
addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
getMarqueeTransform={this.getMarqueeTransform} getTransform={this.getTransform} />
{this.overlayView}
+
</div>
);
}
diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx
index 81d7f4077..02ee49a38 100644
--- a/src/client/views/collections/CollectionViewBase.tsx
+++ b/src/client/views/collections/CollectionViewBase.tsx
@@ -13,8 +13,8 @@ import { Transform } from "../../util/Transform";
import { CollectionView } from "./CollectionView";
import { RouteStore } from "../../../server/RouteStore";
import { TupleField } from "../../../fields/TupleField";
-import { Server } from "mongodb";
import { DashUserModel } from "../../../server/authentication/models/user_model";
+import { UserUtils } from "../../../server/authentication/models/user_utils";
export interface CollectionViewProps {
fieldKey: Key;
@@ -34,10 +34,9 @@ export interface SubCollectionViewProps extends CollectionViewProps {
addDocument: (doc: Document) => void;
removeDocument: (doc: Document) => boolean;
CollectionView: CollectionView;
- currentUser?: DashUserModel;
}
-export type CursorEntry = TupleField<DashUserModel, [number, number]>;
+export type CursorEntry = TupleField<string, [number, number]>;
export class CollectionViewBase extends React.Component<SubCollectionViewProps> {
private dropDisposer?: DragManager.DragDropDisposer;
@@ -50,31 +49,34 @@ export class CollectionViewBase extends React.Component<SubCollectionViewProps>
}
}
+ @action
protected setCursorPosition(position: [number, number]) {
- let user = this.props.currentUser;
- if (user && user.id) {
- let ind;
- let doc = this.props.Document;
- let cursors = doc.GetOrCreate<ListField<CursorEntry>>(KeyStore.Cursors, ListField, false).Data;
- let entry = new TupleField<DashUserModel, [number, number]>([user.id, position]);
- // if ((ind = cursors.findIndex(entry => entry.Data[0] === user.id)) > -1) {
- // cursors[ind] = entry;
- // } else {
- // cursors.push(entry);
- // }
+ let ind;
+ let doc = this.props.Document;
+ let id = UserUtils.currentUserId;
+ if (id) {
+ doc.GetOrCreateAsync<ListField<CursorEntry>>(KeyStore.Cursors, ListField, field => {
+ let cursors = field.Data;
+ if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.Data[0] === id)) > -1) {
+ cursors[ind].Data[1] = position;
+ } else {
+ let entry = new TupleField<string, [number, number]>([id, position]);
+ cursors.push(entry);
+ }
+ })
+
+
}
}
protected getCursors(): CursorEntry[] {
- let user = this.props.currentUser;
- if (user && user.id) {
- let doc = this.props.Document;
- // return doc.GetList<CursorEntry>(KeyStore.Cursors, []).filter(entry => entry.Data[0] !== user.id);
- }
- return [];
+ let doc = this.props.Document;
+ let id = UserUtils.currentUserId;
+ let cursors = doc.GetList<CursorEntry>(KeyStore.Cursors, []);
+ let notMe = cursors.filter(entry => entry.Data[0] !== id);
+ return id ? notMe : [];
}
-
@undoBatch
@action
protected drop(e: Event, de: DragManager.DropEvent) {
diff --git a/src/fields/TupleField.ts b/src/fields/TupleField.ts
index 778bd5d2f..e2162c751 100644
--- a/src/fields/TupleField.ts
+++ b/src/fields/TupleField.ts
@@ -51,7 +51,7 @@ export class TupleField<T, U> extends BasicField<[T, U]> {
ToJson(): { type: Types, data: [T, U], _id: string } {
return {
- type: Types.List,
+ type: Types.Tuple,
data: this.Data,
_id: this.Id
}
diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts
index dce4bada5..f10f82deb 100644
--- a/src/server/ServerUtil.ts
+++ b/src/server/ServerUtil.ts
@@ -22,6 +22,8 @@ import { TupleField } from '../fields/TupleField';
export class ServerUtils {
+ public static prepend(extension: string): string { return window.location.origin + extension; }
+
public static FromJson(json: any): Field {
let obj = json
let data: any = obj.data
diff --git a/src/server/authentication/models/user_utils.ts b/src/server/authentication/models/user_utils.ts
new file mode 100644
index 000000000..1497a4ba4
--- /dev/null
+++ b/src/server/authentication/models/user_utils.ts
@@ -0,0 +1,22 @@
+import { DashUserModel } from "./user_model";
+import * as request from 'request'
+import { RouteStore } from "../../RouteStore";
+import { ServerUtils } from "../../ServerUtil";
+
+export class UserUtils {
+ private static current: string;
+
+ public static get currentUserId() {
+ return UserUtils.current;
+ }
+
+ public static loadCurrentUserId() {
+ request.get(ServerUtils.prepend(RouteStore.getCurrUser), (error, response, body) => {
+ if (body) {
+ UserUtils.current = JSON.parse(body) as string;
+ } else {
+ throw new Error("There should be a user! Why does Dash think there isn't one?")
+ }
+ });
+ }
+} \ No newline at end of file
diff --git a/src/server/index.ts b/src/server/index.ts
index 17aba99ee..0512ebf72 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -6,19 +6,14 @@ import * as whm from 'webpack-hot-middleware';
import * as path from 'path'
import * as formidable from 'formidable'
import * as passport from 'passport';
-import { MessageStore, Message, SetFieldArgs, GetFieldArgs, Transferable } from "./Message";
+import { MessageStore, Transferable } from "./Message";
import { Client } from './Client';
import { Socket } from 'socket.io';
import { Utils } from '../Utils';
import { ObservableMap } from 'mobx';
import { FieldId, Field } from '../fields/Field';
import { Database } from './database';
-import { ServerUtils } from './ServerUtil';
-import { ObjectID } from 'mongodb';
-import * as bcrypt from "bcrypt-nodejs";
-import { Document } from '../fields/Document';
import * as io from 'socket.io'
-import * as passportConfig from './authentication/config/passport';
import { getLogin, postLogin, getSignup, postSignup, getLogout, postReset, getForgot, postForgot, getReset } from './authentication/controllers/user_controller';
const config = require('../../webpack.config');
const compiler = webpack(config);
@@ -29,17 +24,14 @@ import expressFlash = require('express-flash');
import flash = require('connect-flash');
import * as bodyParser from 'body-parser';
import * as session from 'express-session';
-// import cookieSession = require('cookie-session');
import * as cookieParser from 'cookie-parser';
import c = require("crypto");
const MongoStore = require('connect-mongo')(session);
const mongoose = require('mongoose');
-import { performance } from 'perf_hooks'
-import User, { DashUserModel } from './authentication/models/user_model';
+import { DashUserModel } from './authentication/models/user_model';
import * as fs from 'fs';
import * as request from 'request'
import { RouteStore } from './RouteStore';
-import * as MobileDetect from 'mobile-detect';
const download = (url: string, dest: fs.PathLike) => {
request.get(url).pipe(fs.createWriteStream(dest));
@@ -56,7 +48,7 @@ mongoose.connection.on('connected', function () {
app.use(cookieParser());
app.use(session({
- secret: `our secret`,
+ secret: "64d6866242d3b5a5503c675b32c9605e4e90478e9b77bcf2bc",
resave: true,
cookie: { maxAge: 7 * 24 * 60 * 60 },
saveUninitialized: true,
@@ -91,30 +83,30 @@ enum Method {
* It ensures that any requests leading to or containing user-sensitive information
* does not execute unless Passport authentication detects a user logged in.
* @param method whether or not the request is a GET or a POST
- * @param route the forward slash prepended path name (reference and add to RouteStore.ts)
* @param handler the action to invoke, recieving a DashUserModel and, as expected, the Express.Request and Express.Response
* @param onRejection an optional callback invoked on return if no user is found to be logged in
+ * @param subscribers the forward slash prepended path names (reference and add to RouteStore.ts) that will all invoke the given @param handler
*/
function addSecureRoute(method: Method,
- route: string,
handler: (user: DashUserModel, res: express.Response, req: express.Request) => void,
- onRejection: (res: express.Response) => any = (res) => res.redirect(RouteStore.logout)) {
- switch (method) {
- case Method.GET:
- app.get(route, (req, res) => {
- const dashUser: DashUserModel = req.user;
- if (!dashUser) return onRejection(res);
- handler(dashUser, res, req);
- });
- break;
- case Method.POST:
- app.post(route, (req, res) => {
- const dashUser: DashUserModel = req.user;
- if (!dashUser) return onRejection(res);
- handler(dashUser, res, req);
- });
- break;
+ onRejection: (res: express.Response) => any = (res) => res.redirect(RouteStore.logout),
+ ...subscribers: string[]
+) {
+ let abstracted = (req: express.Request, res: express.Response) => {
+ const dashUser: DashUserModel = req.user;
+ if (!dashUser) return onRejection(res);
+ handler(dashUser, res, req);
}
+ subscribers.forEach(route => {
+ switch (method) {
+ case Method.GET:
+ app.get(route, abstracted);
+ break;
+ case Method.POST:
+ app.post(route, abstracted);
+ break;
+ }
+ });
}
// STATIC FILE SERVING
@@ -128,60 +120,87 @@ app.use(RouteStore.images, express.static(__dirname + RouteStore.public))
// anyone attempting to navigate to localhost at this port will
// first have to login
-addSecureRoute(Method.GET, RouteStore.root, (user, res) => {
- res.redirect(RouteStore.home);
-});
-
-addSecureRoute(Method.GET, RouteStore.home, (user, res) => {
- res.sendFile(path.join(__dirname, '../../deploy/index.html'));
-});
-
-addSecureRoute(Method.GET, RouteStore.openDocumentWithId, (user, res) => {
- res.sendFile(path.join(__dirname, '../../deploy/index.html'));
-});
-
-addSecureRoute(Method.GET, RouteStore.getActiveWorkspace, (user, res) => {
- res.send(user.activeWorkspaceId || "");
-});
-
-addSecureRoute(Method.GET, RouteStore.getAllWorkspaces, (user, res) => {
- res.send(JSON.stringify(user.allWorkspaceIds));
-});
-
-addSecureRoute(Method.GET, RouteStore.getCurrUser, (user, res) => {
- res.send(JSON.stringify(user));
-});
+addSecureRoute(
+ Method.GET,
+ (user, res) => res.redirect(RouteStore.home),
+ undefined,
+ RouteStore.root
+);
+
+addSecureRoute(
+ Method.GET,
+ (user, res) => res.sendFile(path.join(__dirname, '../../deploy/index.html')),
+ undefined,
+ RouteStore.home,
+ RouteStore.openDocumentWithId
+);
+
+addSecureRoute(
+ Method.GET,
+ (user, res) => res.send(user.activeWorkspaceId || ""),
+ undefined,
+ RouteStore.getActiveWorkspace,
+);
+
+addSecureRoute(
+ Method.GET,
+ (user, res) => res.send(JSON.stringify(user.allWorkspaceIds)),
+ undefined,
+ RouteStore.getAllWorkspaces
+);
+
+addSecureRoute(
+ Method.GET,
+ (user, res) => res.send(JSON.stringify(user.id)),
+ undefined,
+ RouteStore.getCurrUser
+);
// SETTERS
-addSecureRoute(Method.POST, RouteStore.setActiveWorkspace, (user, res, req) => {
- user.update({ $set: { activeWorkspaceId: req.body.target } }, (err, raw) => {
- res.sendStatus(err ? 500 : 200);
- });
-});
-
-addSecureRoute(Method.POST, RouteStore.addWorkspace, (user, res, req) => {
- user.update({ $push: { allWorkspaceIds: req.body.target } }, (err, raw) => {
- res.sendStatus(err ? 500 : 200);
- });
-});
-
-addSecureRoute(Method.POST, RouteStore.upload, (user, res, req) => {
- let form = new formidable.IncomingForm()
- form.uploadDir = __dirname + "/public/files/"
- form.keepExtensions = true
- // let path = req.body.path;
- console.log("upload")
- form.parse(req, (err, fields, files) => {
- console.log("parsing")
- let names: any[] = [];
- for (const name in files) {
- let file = files[name];
- names.push(`/files/` + path.basename(file.path));
- }
- res.send(names);
- });
-});
+addSecureRoute(
+ Method.POST,
+ (user, res, req) => {
+ user.update({ $set: { activeWorkspaceId: req.body.target } }, (err, raw) => {
+ res.sendStatus(err ? 500 : 200);
+ });
+ },
+ undefined,
+ RouteStore.setActiveWorkspace
+);
+
+addSecureRoute(
+ Method.POST,
+ (user, res, req) => {
+ user.update({ $push: { allWorkspaceIds: req.body.target } }, (err, raw) => {
+ res.sendStatus(err ? 500 : 200);
+ });
+ },
+ undefined,
+ RouteStore.addWorkspace
+);
+
+addSecureRoute(
+ Method.POST,
+ (user, res, req) => {
+ let form = new formidable.IncomingForm()
+ form.uploadDir = __dirname + "/public/files/"
+ form.keepExtensions = true
+ // let path = req.body.path;
+ console.log("upload")
+ form.parse(req, (err, fields, files) => {
+ console.log("parsing")
+ let names: any[] = [];
+ for (const name in files) {
+ let file = files[name];
+ names.push(`/files/` + path.basename(file.path));
+ }
+ res.send(names);
+ });
+ },
+ undefined,
+ RouteStore.upload
+);
// AUTHENTICATION