From 042eb977ad7733919daf468001b17dbee4e1fa9f Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 26 Feb 2019 18:58:07 -0500 Subject: beginning multiple workspaces backend and ui --- .../authentication/controllers/user_controller.ts | 291 +++++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 src/server/authentication/controllers/user_controller.ts (limited to 'src/server/authentication/controllers/user_controller.ts') diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts new file mode 100644 index 000000000..899912ab7 --- /dev/null +++ b/src/server/authentication/controllers/user_controller.ts @@ -0,0 +1,291 @@ +import { default as User, DashUserModel, AuthToken } from "../models/user_model"; +import { Request, Response, NextFunction } from "express"; +import * as passport from "passport"; +import { IVerifyOptions } from "passport-local"; +import "../config/passport"; +import * as request from "express-validator"; +const flash = require("express-flash"); +import * as session from "express-session"; +import * as pug from 'pug'; +import * as async from 'async'; +import * as nodemailer from 'nodemailer'; +import c = require("crypto"); + + +/** + * GET / + * Whenever a user navigates to the root of Dash + * (doesn't specify a sub-route), redirect to login. + * If the user is already signed in, it will effectively + * automatically redirect them to /home instead + */ +export let getEntry = (req: Request, res: Response) => { + res.redirect("/login"); +} + +/** + * GET /signup + * Directs user to the signup page + * modeled by signup.pug in views + */ +export let getSignup = (req: Request, res: Response) => { + if (req.user) { + let user = req.user; + return res.redirect("/home"); + } + res.render("signup.pug", { + title: "Sign Up", + user: req.user, + }); +}; + +/** + * POST /signup + * Create a new local account. + */ +export let postSignup = (req: Request, res: Response, next: NextFunction) => { + req.assert("email", "Email is not valid").isEmail(); + req.assert("password", "Password must be at least 4 characters long").len({ min: 4 }); + req.assert("confirmPassword", "Passwords do not match").equals(req.body.password); + req.sanitize("email").normalizeEmail({ gmail_remove_dots: false }); + + const errors = req.validationErrors(); + + if (errors) { + res.render("signup.pug", { + title: "Sign Up", + user: req.user, + }); + return res.redirect("/signup"); + } + + const email = req.body.email; + const password = req.body.password; + + const user = new User({ + email, + password, + userDoc: "document here" + }); + + User.findOne({ email }, (err, existingUser) => { + if (err) { return next(err); } + if (existingUser) { + return res.redirect("/login"); + } + user.save((err) => { + if (err) { return next(err); } + req.logIn(user, (err) => { + if (err) { + return next(err); + } + res.redirect("/home"); + }); + }); + }); + +}; + + +/** + * GET /login + * Login page. + */ +export let getLogin = (req: Request, res: Response) => { + if (req.user) { + return res.redirect("/home"); + } + res.render("login.pug", { + title: "Log In", + user: req.user + }); +}; + +/** + * POST /login + * Sign in using email and password. + * On failure, redirect to login page + */ +export let postLogin = (req: Request, res: Response, next: NextFunction) => { + req.assert("email", "Email is not valid").isEmail(); + req.assert("password", "Password cannot be blank").notEmpty(); + req.sanitize("email").normalizeEmail({ gmail_remove_dots: false }); + + const errors = req.validationErrors(); + + if (errors) { + req.flash("errors", "Unable to login at this time. Please try again."); + return res.redirect("/signup"); + } + + passport.authenticate("local", (err: Error, user: DashUserModel, info: IVerifyOptions) => { + if (err) { return next(err); } + if (!user) { + return res.redirect("/signup"); + } + req.logIn(user, (err) => { + if (err) { return next(err); } + res.redirect("/home"); + }); + })(req, res, next); +}; + +export let getWorkspaces = (req: Request, res: Response) => { + const user: DashUserModel = req.user; + if (!user) { + return res.redirect("/login"); + } + res.render("workspace.pug", { + ids: user.allWorkspaceIds + }); +} + +/** + * GET /logout + * Invokes the logout function on the request + * and destroys the user's current session. + */ +export let getLogout = (req: Request, res: Response) => { + const dashUser: DashUserModel | undefined = req.user; + if (dashUser) { + dashUser.update({ $set: { didSelectSessionWorkspace: false } }, () => { }) + console.log("UPDATED :)"); + } else { + console.log("NO USER BY LOGOUT"); + } + req.logout(); + const sess = req.session; + if (sess) { + sess.destroy((err) => { if (err) { console.log(err); } }); + } + res.redirect('/login'); +} + +export let getForgot = function (req: Request, res: Response) { + res.render("forgot.pug", { + title: "Recover Password", + user: req.user, + }); +} + +export let postForgot = function (req: Request, res: Response, next: NextFunction) { + const email = req.body.email; + async.waterfall([ + function (done: any) { + let token: string; + c.randomBytes(20, function (err: any, buffer: Buffer) { + if (err) { + done(null); + return; + } + done(null, buffer.toString('hex')); + }) + }, + function (token: string, done: any) { + User.findOne({ email }, function (err, user: DashUserModel) { + if (!user) { + // NO ACCOUNT WITH SUBMITTED EMAIL + return res.redirect('/forgot'); + } + user.passwordResetToken = token; + user.passwordResetExpires = new Date(Date.now() + 3600000); // 1 HOUR + user.save(function (err: any) { + done(null, token, user); + }); + }); + }, + function (token: Uint16Array, user: DashUserModel, done: any) { + const smtpTransport = nodemailer.createTransport({ + service: 'Gmail', + auth: { + user: 'brownptcdash@gmail.com', + pass: 'browngfx1' + } + }); + const mailOptions = { + to: user.email, + from: 'brownptcdash@gmail.com', + subject: 'Dash Password Reset', + text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' + + 'Please click on the following link, or paste this into your browser to complete the process:\n\n' + + 'http://' + req.headers.host + '/reset/' + token + '\n\n' + + 'If you did not request this, please ignore this email and your password will remain unchanged.\n' + }; + smtpTransport.sendMail(mailOptions, function (err) { + // req.flash('info', 'An e-mail has been sent to ' + user.email + ' with further instructions.'); + done(null, err, 'done'); + }); + } + ], function (err) { + if (err) return next(err); + res.redirect('/forgot'); + }) +} + +export let getReset = function (req: Request, res: Response) { + User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err, user: DashUserModel) { + if (!user || err) { + return res.redirect('/forgot'); + } + res.render("reset.pug", { + title: "Reset Password", + user: req.user, + }); + }); +} + +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, user: DashUserModel) { + if (!user || err) { + 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); + + if (req.validationErrors()) { + return res.redirect('back'); + } + + user.password = req.body.password; + user.passwordResetToken = undefined; + user.passwordResetExpires = undefined; + + user.save(function (err) { + if (err) { + return res.redirect("/login"); + } + req.logIn(user, function (err) { + if (err) { + return; + } + }); + done(null, user); + }); + }); + }, + function (user: DashUserModel, done: any) { + const smtpTransport = nodemailer.createTransport({ + service: 'Gmail', + auth: { + user: 'brownptcdash@gmail.com', + pass: 'browngfx1' + } + }); + const mailOptions = { + to: user.email, + from: 'brownptcdash@gmail.com', + 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' + }; + smtpTransport.sendMail(mailOptions, function (err) { + done(null, err); + }); + } + ], function (err) { + res.redirect('/login'); + }); +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From f60be11aef0e76cbc636933611962b3e1a4ec71e Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 27 Feb 2019 15:24:51 -0500 Subject: mostly finished workspace manipulation: golden-layout bug when creating a new workspace --- src/client/views/Main.tsx | 48 ++++++++++------------ .../authentication/controllers/WorkspacesMenu.tsx | 42 +++++++++++++++---- .../authentication/controllers/user_controller.ts | 3 -- src/server/index.ts | 1 - 4 files changed, 55 insertions(+), 39 deletions(-) (limited to 'src/server/authentication/controllers/user_controller.ts') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 2c2149a1e..febf7489b 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -37,33 +37,27 @@ document.addEventListener("pointerdown", action(function (e: PointerEvent) { } }), true) -let mainDocId: string; +// Load the user's active workspace, or create a new one if initial session after signup request.get(window.location.origin + "/getActiveWorkspaceId", (error, response, body) => { - const here = window.location.origin; - let workspaceId: string; - if (body) { - workspaceId = body; - } else { - workspaceId = Utils.GenerateGuid(); - request.post(here + "/addWorkspaceId", { - body: { - target: mainDocId - }, - json: true - }) - request.post(here + "/setActiveWorkspaceId", { - body: { - target: mainDocId - }, - json: true - }) - } - load(workspaceId); -}) + init(body ? body : getNewWorkspace()); +}); -function load(workspaceId: string) { - mainDocId = workspaceId; - init(); +function getNewWorkspace(): string { + let newId = Utils.GenerateGuid(); + const here = window.location.origin; + request.post(here + "/addWorkspaceId", { + body: { + target: newId + }, + json: true + }) + request.post(here + "/setActiveWorkspaceId", { + body: { + target: newId + }, + json: true + }) + return newId; } //runInAction(() => @@ -82,7 +76,7 @@ function load(workspaceId: string) { // schemaDocs[4].SetData(KS.Author, "Bob", TextField); // schemaDocs.push(doc2); // const doc7 = Documents.SchemaDocument(schemaDocs) -function init() { +function init(mainDocId: string) { Documents.initProtos(() => { Utils.EmitCallback(Server.Socket, MessageStore.GetField, mainDocId, (res: any) => { console.log("HELLO WORLD") @@ -197,7 +191,7 @@ function init() { left: '4px', width: '150px' }} onClick={() => WorkspacesMenu.Instance.toggle()}>Workspaces - + ), document.getElementById('root')); }) diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx index 8edd63fba..d9d884c14 100644 --- a/src/server/authentication/controllers/WorkspacesMenu.tsx +++ b/src/server/authentication/controllers/WorkspacesMenu.tsx @@ -1,12 +1,13 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { observable, action, configure, reaction, computed } from 'mobx'; +import { observable, action, configure, reaction, computed, ObservableMap } from 'mobx'; import { observer } from "mobx-react"; import * as request from 'request' import './WorkspacesMenu.css' export interface WorkspaceMenuProps { load: (workspaceId: string) => void; + new: () => string; } @observer @@ -14,10 +15,28 @@ export class WorkspacesMenu extends React.Component { static Instance: WorkspacesMenu; @observable private workspacesExposed: boolean = false; @observable private workspaceIds: Array = []; + @observable private selectedWorkspaceId: string = ""; constructor(props: WorkspaceMenuProps) { super(props); WorkspacesMenu.Instance = this; + this.loadExistingWorkspace = this.loadExistingWorkspace.bind(this); + this.addNewWorkspace = this.addNewWorkspace.bind(this); + } + + @action + addNewWorkspace() { + let newId = this.props.new(); + this.selectedWorkspaceId = newId; + this.props.load(newId); + this.toggle(); + } + + @action + loadExistingWorkspace = (e: React.MouseEvent) => { + let id = e.currentTarget.innerHTML; + this.props.load(id); + this.selectedWorkspaceId = id; } @action @@ -42,11 +61,8 @@ export class WorkspacesMenu extends React.Component { } } - setWorkspaceId = (e: React.MouseEvent) => { - this.props.load(e.currentTarget.innerHTML); - } - render() { + let p = this.props; return (
{ padding: 10, }} > + {this.workspaceIds.map(s =>
  • {s}
  • )}
    diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts index 899912ab7..1f1f43684 100644 --- a/src/server/authentication/controllers/user_controller.ts +++ b/src/server/authentication/controllers/user_controller.ts @@ -149,9 +149,6 @@ export let getLogout = (req: Request, res: Response) => { const dashUser: DashUserModel | undefined = req.user; if (dashUser) { dashUser.update({ $set: { didSelectSessionWorkspace: false } }, () => { }) - console.log("UPDATED :)"); - } else { - console.log("NO USER BY LOGOUT"); } req.logout(); const sess = req.session; diff --git a/src/server/index.ts b/src/server/index.ts index 1d16f65ed..c707f12a0 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -118,7 +118,6 @@ app.post("/setActiveWorkspaceId", (req, res) => { if (!dashUser) { return; } - console.log(`Updating active workspace ID to ${req.body.target}`); dashUser.update({ $set: { activeWorkspaceId: req.body.target } }, () => { }); }) -- cgit v1.2.3-70-g09d2 From 45d9a5bc32aa5d5aa7695c4e6576f3c42f24c5c9 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 27 Feb 2019 17:30:10 -0500 Subject: clean up --- src/server/authentication/controllers/WorkspacesMenu.tsx | 5 ++++- src/server/authentication/controllers/user_controller.ts | 10 ---------- src/server/index.ts | 2 +- views/workspace.pug | 13 ------------- 4 files changed, 5 insertions(+), 25 deletions(-) delete mode 100644 views/workspace.pug (limited to 'src/server/authentication/controllers/user_controller.ts') diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx index 284a8ec66..94d168a05 100644 --- a/src/server/authentication/controllers/WorkspacesMenu.tsx +++ b/src/server/authentication/controllers/WorkspacesMenu.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { observable, action, configure, reaction, computed, ObservableMap } from 'mobx'; +import { observable, action, configure, reaction, computed, ObservableMap, runInAction } from 'mobx'; import { observer } from "mobx-react"; import * as request from 'request' import './WorkspacesMenu.css' @@ -32,6 +32,9 @@ export class WorkspacesMenu extends React.Component { this.selectedWorkspaceId = newId; this.props.load(newId); this.toggle(); + // setTimeout(action(() => { + + // }), 100); } @action diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts index 1f1f43684..7b89b5152 100644 --- a/src/server/authentication/controllers/user_controller.ts +++ b/src/server/authentication/controllers/user_controller.ts @@ -130,16 +130,6 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => { })(req, res, next); }; -export let getWorkspaces = (req: Request, res: Response) => { - const user: DashUserModel = req.user; - if (!user) { - return res.redirect("/login"); - } - res.render("workspace.pug", { - ids: user.allWorkspaceIds - }); -} - /** * GET /logout * Invokes the logout function on the request diff --git a/src/server/index.ts b/src/server/index.ts index c707f12a0..5115142b8 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -17,7 +17,7 @@ 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, getEntry, postReset, getForgot, postForgot, getReset, getWorkspaces } from './authentication/controllers/user_controller'; +import { getLogin, postLogin, getSignup, postSignup, getLogout, getEntry, postReset, getForgot, postForgot, getReset } from './authentication/controllers/user_controller'; const config = require('../../webpack.config'); const compiler = webpack(config); const port = 1050; // default port to listen diff --git a/views/workspace.pug b/views/workspace.pug deleted file mode 100644 index 8bbc3e02c..000000000 --- a/views/workspace.pug +++ /dev/null @@ -1,13 +0,0 @@ - -extends ./layout - -block content - style - include ./stylesheets/authentication.css - form.form-horizontal(id='workspace-form', method='POST', action='/home') - input(type='hidden', name='_csrf', value=_csrf) - .overlay(id='overlay_workspaces') - h3.workspace-header Select A Workspace - ul.workspaceList - each val, index in ids - li.workspaceId(onclick='console.log("' + val + '")')= (index + 1) + ') ' + val \ No newline at end of file -- cgit v1.2.3-70-g09d2 From bece3110a3d6fa18dd89415a5e636c6792aea4e1 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 20 Mar 2019 05:07:52 -0400 Subject: Refactored users to have a single user document instead of a list of workspaces --- src/client/views/Main.tsx | 132 ++++++++++++--------- src/fields/KeyStore.ts | 2 + src/mobile/ImageUpload.tsx | 38 +++--- src/server/RouteStore.ts | 5 +- .../authentication/controllers/WorkspacesMenu.tsx | 19 +-- .../authentication/controllers/user_controller.ts | 3 +- .../authentication/models/current_user_utils.ts | 46 +++++-- src/server/authentication/models/user_model.ts | 11 +- src/server/index.ts | 33 +----- 9 files changed, 144 insertions(+), 145 deletions(-) (limited to 'src/server/authentication/controllers/user_controller.ts') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index ac51a7d87..a1a6cc475 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -8,6 +8,7 @@ import "./Main.scss"; import { MessageStore } from '../../server/Message'; import { Utils } from '../../Utils'; import * as request from 'request' +import * as rp from 'request-promise' import { Documents } from '../documents/Documents'; import { Server } from '../Server'; import { setupDrag } from '../util/DragManager'; @@ -40,7 +41,7 @@ import Measure from 'react-measure'; import { DashUserModel } from '../../server/authentication/models/user_model'; import { ServerUtils } from '../../server/ServerUtil'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; -import { Field, Opt } from '../../fields/Field'; +import { Field, Opt, FieldWaiting } from '../../fields/Field'; import { ListField } from '../../fields/ListField'; import { map } from 'bluebird'; import { Gateway, Settings } from '../northstar/manager/Gateway'; @@ -49,13 +50,26 @@ import { Catalog } from '../northstar/model/idea/idea'; @observer export class Main extends React.Component { // dummy initializations keep the compiler happy - @observable private mainContainer?: Document; @observable private mainfreeform?: Document; - @observable private userWorkspaces: Document[] = []; @observable public pwidth: number = 0; @observable public pheight: number = 0; @observable private _northstarCatalog: Catalog | undefined = undefined; + @computed private get mainContainer(): Document | undefined { + let doc = this.userDocument.GetT(KeyStore.ActiveWorkspace, Document); + return doc == FieldWaiting ? undefined : doc; + } + + private set mainContainer(doc: Document | undefined) { + if (doc) { + this.userDocument.Set(KeyStore.ActiveWorkspace, doc); + } + } + + private get userDocument(): Document { + return CurrentUserUtils.UserDocument; + } + public mainDocId: string | undefined; private currentUser?: DashUserModel; public static Instance: Main; @@ -67,12 +81,12 @@ export class Main extends React.Component { configure({ enforceActions: "observed" }); if (window.location.pathname !== RouteStore.home) { let pathname = window.location.pathname.split("/"); - this.mainDocId = pathname[pathname.length - 1]; + if (pathname.length > 1 && pathname[pathname.length - 2] == 'doc') { + this.mainDocId = pathname[pathname.length - 1]; + } }; - this.initializeNorthstar(); - - CurrentUserUtils.loadCurrentUser(); + // this.initializeNorthstar(); library.add(faFont); library.add(faImage); @@ -143,63 +157,50 @@ 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(ServerUtils.prepend(RouteStore.getActiveWorkspace), (error, response, body) => { - if (this.mainDocId || body) { - Server.GetField(this.mainDocId || body, field => { - if (field instanceof Document) { - this.openWorkspace(field); - this.populateWorkspaces(); - } else { - this.createNewWorkspace(true, this.mainDocId); - } - }); - } else { - this.createNewWorkspace(true, this.mainDocId); - } - }); - } - - @action - createNewWorkspace = (init: boolean, id?: string): void => { - let mainDoc = Documents.DockDocument(JSON.stringify({ content: [{ type: 'row', content: [] }] }), { title: `Main Container ${this.userWorkspaces.length + 1}` }, id); - let newId = mainDoc.Id; - request.post(ServerUtils.prepend(RouteStore.addWorkspace), { - body: { target: newId }, - json: true - }, () => { if (init) this.populateWorkspaces(); }); - - // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) - setTimeout(() => { - let freeformDoc = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" }); - var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] }; - mainDoc.SetText(KeyStore.Data, JSON.stringify(dockingLayout)); - mainDoc.Set(KeyStore.ActiveFrame, freeformDoc); - this.openWorkspace(mainDoc); - let pendingDocument = Documents.SchemaDocument([], { title: "New Mobile Uploads" }) - mainDoc.Set(KeyStore.OptionalRightCollection, pendingDocument); - }, 0); - this.userWorkspaces.push(mainDoc); + if (!this.mainDocId) { + this.userDocument.GetTAsync(KeyStore.ActiveWorkspace, Document).then(doc => { + if (doc) { + this.openWorkspace(doc); + } else { + this.createNewWorkspace(); + } + }) + } else { + Server.GetField(this.mainDocId).then(field => { + if (field instanceof Document) { + this.openWorkspace(field) + } else { + this.createNewWorkspace(this.mainDocId); + } + }) + } } @action - populateWorkspaces = () => { - // retrieve all workspace documents from the server - 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))); - }); + createNewWorkspace = (id?: string): void => { + this.userDocument.GetTAsync>(KeyStore.Workspaces, ListField).then(action((list: Opt>) => { + if (list) { + let freeformDoc = Documents.FreeformDocument([], { x: 0, y: 400, title: "mini collection" }); + var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc)] }] }; + let mainDoc = Documents.DockDocument(JSON.stringify(dockingLayout), { title: `Main Container ${list.Data.length + 1}` }, id); + list.Data.push(mainDoc); + // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) + setTimeout(() => { + mainDoc.Set(KeyStore.ActiveFrame, freeformDoc); + this.openWorkspace(mainDoc); + let pendingDocument = Documents.SchemaDocument([], { title: "New Mobile Uploads" }) + mainDoc.Set(KeyStore.OptionalRightCollection, pendingDocument); + }, 0); + } + })); } @action openWorkspace = (doc: Document, fromHistory = false): void => { - request.post(ServerUtils.prepend(RouteStore.setActiveWorkspace), { - body: { target: doc.Id }, - json: true - }); this.mainContainer = doc; fromHistory || window.history.pushState(null, doc.Title, "/doc/" + doc.Id); this.mainContainer.GetTAsync(KeyStore.ActiveFrame, Document, field => this.mainfreeform = field); - this.mainContainer.GetTAsync(KeyStore.OptionalRightCollection, Document, col => { + this.userDocument.GetTAsync(KeyStore.OptionalRightCollection, Document).then(col => { // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized) setTimeout(() => { if (col) { @@ -213,10 +214,15 @@ export class Main extends React.Component { }); } + @observable + workspacesShown: boolean = false; + + areWorkspacesShown = () => { + return this.workspacesShown; + } + @action toggleWorkspaces = () => { - if (WorkspacesMenu.Instance) { - WorkspacesMenu.Instance.toggle() - } + this.workspacesShown = !this.workspacesShown; } screenToLocalTransform = () => Transform.Identity @@ -310,6 +316,12 @@ export class Main extends React.Component { } render() { + let workspaceMenu: any = null; + let workspaces = this.userDocument.GetT>(KeyStore.Workspaces, ListField); + if (workspaces && workspaces !== FieldWaiting) { + workspaceMenu = + } return (
    runInAction(() => { @@ -326,11 +338,13 @@ export class Main extends React.Component { {this.nodesMenu} {this.miscButtons} - + {workspaceMenu}
    ); } } -ReactDOM.render(
    , document.getElementById('root')); +CurrentUserUtils.loadCurrentUser().then(() => { + ReactDOM.render(
    , document.getElementById('root')); +}); diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts index 68883d6f1..891caaa81 100644 --- a/src/fields/KeyStore.ts +++ b/src/fields/KeyStore.ts @@ -28,6 +28,7 @@ export namespace KeyStore { export const SchemaSplitPercentage = new Key("SchemaSplitPercentage"); export const Caption = new Key("Caption"); export const ActiveFrame = new Key("ActiveFrame"); + export const ActiveWorkspace = new Key("ActiveWorkspace"); export const DocumentText = new Key("DocumentText"); export const LinkedToDocs = new Key("LinkedToDocs"); export const LinkedFromDocs = new Key("LinkedFromDocs"); @@ -41,4 +42,5 @@ export namespace KeyStore { export const OptionalRightCollection = new Key("OptionalRightCollection"); export const Archives = new Key("Archives"); export const Updated = new Key("Updated"); + export const Workspaces = new Key("Workspaces"); } diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index 16808a598..47b9d8f0b 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -7,7 +7,7 @@ import { Server } from '../client/Server'; import { Documents } from '../client/documents/Documents'; import { ListField } from '../fields/ListField'; import { ImageField } from '../fields/ImageField'; -import request = require('request'); +import * as rp from 'request-promise' import { ServerUtils } from '../server/ServerUtil'; import { RouteStore } from '../server/RouteStore'; @@ -40,28 +40,24 @@ const onFileLoad = (file: any) => { json.map((file: any) => { let path = window.location.origin + file var doc: Document = Documents.ImageDocument(path, { nativeWidth: 200, width: 200 }) - doc.GetTAsync(KeyStore.Data, ImageField, (i) => { - if (i) { - document.getElementById("message")!.innerText = i.Data.href; + + rp.get(ServerUtils.prepend(RouteStore.getUserDocumentId)).then(res => { + if (res) { + return Server.GetField(res); + } + throw new Error("No user id returned"); + }).then(field => { + if (field instanceof Document) { + return field.GetTAsync(KeyStore.OptionalRightCollection, Document) } - }) - request.get(ServerUtils.prepend(RouteStore.getActiveWorkspace), (error, response, body) => { - if (body) { - Server.GetField(body, field => { - if (field instanceof Document) { - field.GetTAsync(KeyStore.OptionalRightCollection, Document, - pending => { - if (pending) { - pending.GetOrCreateAsync(KeyStore.Data, ListField, list => { - list.Data.push(doc); - }) - } - }) - } - } - ); + }).then(pending => { + if (pending) { + pending.GetOrCreateAsync(KeyStore.Data, ListField, list => { + list.Data.push(doc); + }) } - }) + }); + // console.log(window.location.origin + file[0]) //imgPrev.setAttribute("src", window.location.origin + files[0].name) diff --git a/src/server/RouteStore.ts b/src/server/RouteStore.ts index fb06b878b..fdf5b6a5c 100644 --- a/src/server/RouteStore.ts +++ b/src/server/RouteStore.ts @@ -15,10 +15,7 @@ export enum RouteStore { // USER AND WORKSPACES getCurrUser = "/getCurrentUser", - addWorkspace = "/addWorkspaceId", - getAllWorkspaces = "/getAllWorkspaceIds", - getActiveWorkspace = "/getActiveWorkspaceId", - setActiveWorkspace = "/setActiveWorkspaceId", + getUserDocumentId = "/getUserDocumentId", updateCursor = "/updateCursor", openDocumentWithId = "/doc/:docId", diff --git a/src/server/authentication/controllers/WorkspacesMenu.tsx b/src/server/authentication/controllers/WorkspacesMenu.tsx index 1533b1e62..8e14cf98e 100644 --- a/src/server/authentication/controllers/WorkspacesMenu.tsx +++ b/src/server/authentication/controllers/WorkspacesMenu.tsx @@ -9,30 +9,23 @@ import { KeyStore } from '../../../fields/KeyStore'; export interface WorkspaceMenuProps { active: Document | undefined; open: (workspace: Document) => void; - new: (init: boolean) => void; + new: () => void; allWorkspaces: Document[]; + isShown: () => boolean; + toggle: () => void; } @observer export class WorkspacesMenu extends React.Component { - static Instance: WorkspacesMenu; - @observable private workspacesExposed: boolean = false; - constructor(props: WorkspaceMenuProps) { super(props); - WorkspacesMenu.Instance = this; this.addNewWorkspace = this.addNewWorkspace.bind(this); } @action addNewWorkspace() { - this.props.new(false); - this.toggle(); - } - - @action - toggle() { - this.workspacesExposed = !this.workspacesExposed; + this.props.new(); + this.props.toggle(); } render() { @@ -45,7 +38,7 @@ export class WorkspacesMenu extends React.Component { borderRadius: 5, position: "absolute", top: 78, - left: this.workspacesExposed ? 11 : -500, + left: this.props.isShown() ? 11 : -500, background: "white", border: "black solid 2px", transition: "all 1s ease", diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts index 2cef958e8..e365b8dce 100644 --- a/src/server/authentication/controllers/user_controller.ts +++ b/src/server/authentication/controllers/user_controller.ts @@ -11,6 +11,7 @@ import * as async from 'async'; import * as nodemailer from 'nodemailer'; import c = require("crypto"); import { RouteStore } from "../../RouteStore"; +import { Utils } from "../../../Utils"; /** * GET /signup @@ -54,7 +55,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => { const user = new User({ email, password, - userDoc: "document here" + userDocumentId: Utils.GenerateGuid() }); User.findOne({ email }, (err, existingUser) => { diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index cc433eb73..17a6d493b 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -1,29 +1,61 @@ import { DashUserModel } from "./user_model"; -import * as request from 'request' +import * as rp from 'request-promise'; import { RouteStore } from "../../RouteStore"; import { ServerUtils } from "../../ServerUtil"; +import { Server } from "../../../client/Server"; +import { Document } from "../../../fields/Document"; +import { KeyStore } from "../../../fields/KeyStore"; +import { ListField } from "../../../fields/ListField"; +import { Documents } from "../../../client/documents/Documents"; export class CurrentUserUtils { private static curr_email: string; private static curr_id: string; + private static user_document: Document; - public static get email() { + public static get email(): string { return CurrentUserUtils.curr_email; } - public static get id() { + public static get id(): string { return CurrentUserUtils.curr_id; } - public static loadCurrentUser() { - request.get(ServerUtils.prepend(RouteStore.getCurrUser), (error, response, body) => { - if (body) { - let obj = JSON.parse(body); + public static get UserDocument(): Document { + return CurrentUserUtils.user_document; + } + + private static createUserDocument(id: string): Document { + let doc = new Document(id); + + doc.Set(KeyStore.Workspaces, new ListField()); + doc.Set(KeyStore.OptionalRightCollection, Documents.SchemaDocument([], { title: "Pending documents" })) + return doc; + } + + public static loadCurrentUser(): Promise { + let userPromise = rp.get(ServerUtils.prepend(RouteStore.getCurrUser)).then((response) => { + if (response) { + let obj = JSON.parse(response); CurrentUserUtils.curr_id = obj.id as string; CurrentUserUtils.curr_email = obj.email as string; } else { throw new Error("There should be a user! Why does Dash think there isn't one?") } }); + let userDocPromise = rp.get(ServerUtils.prepend(RouteStore.getUserDocumentId)).then(id => { + if (id) { + return Server.GetField(id).then(field => { + if (field instanceof Document) { + this.user_document = field; + } else { + this.user_document = this.createUserDocument(id); + } + }) + } else { + throw new Error("There should be a user id! Why does Dash think there isn't one?") + } + }); + return Promise.all([userPromise, userDocPromise]); } } \ No newline at end of file diff --git a/src/server/authentication/models/user_model.ts b/src/server/authentication/models/user_model.ts index 3d4ed6896..81580aad5 100644 --- a/src/server/authentication/models/user_model.ts +++ b/src/server/authentication/models/user_model.ts @@ -21,9 +21,7 @@ export type DashUserModel = mongoose.Document & { passwordResetToken: string | undefined, passwordResetExpires: Date | undefined, - allWorkspaceIds: Array, - activeWorkspaceId: String, - activeUsersId: String, + userDocumentId: string; profile: { name: string, @@ -49,12 +47,7 @@ const userSchema = new mongoose.Schema({ passwordResetToken: String, passwordResetExpires: Date, - allWorkspaceIds: { - type: Array, - default: [] - }, - activeWorkspaceId: String, - activeUsersId: String, + userDocumentId: String, facebook: String, twitter: String, diff --git a/src/server/index.ts b/src/server/index.ts index d1eb6847d..16304f1c5 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -156,16 +156,9 @@ addSecureRoute( addSecureRoute( Method.GET, - (user, res) => res.send(user.activeWorkspaceId || ""), + (user, res) => res.send(user.userDocumentId || ""), undefined, - RouteStore.getActiveWorkspace, -); - -addSecureRoute( - Method.GET, - (user, res) => res.send(JSON.stringify(user.allWorkspaceIds)), - undefined, - RouteStore.getAllWorkspaces + RouteStore.getUserDocumentId, ); addSecureRoute( @@ -182,28 +175,6 @@ addSecureRoute( // SETTERS -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) => { -- cgit v1.2.3-70-g09d2