aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoryipstanley <stanley_yip@brown.edu>2019-06-18 10:10:58 -0400
committeryipstanley <stanley_yip@brown.edu>2019-06-18 10:10:58 -0400
commitc50ba1c4cc01d5cd085dee0dae6f633164efeb80 (patch)
treef9d0208d5883939dfbafccf0f9173be0512b1e57 /src
parentcc032e2f60015728f64f46ef009c9306e36746a0 (diff)
parent64e6a941639aab8d7109178aa151a50909547309 (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
Diffstat (limited to 'src')
-rw-r--r--src/client/views/MainOverlayTextBox.tsx4
-rw-r--r--src/client/views/MainView.tsx14
-rw-r--r--src/client/views/TemplateMenu.tsx2
-rw-r--r--src/client/views/Templates.tsx8
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx30
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx1
-rw-r--r--src/client/views/collections/CollectionSubView.tsx15
-rw-r--r--src/client/views/nodes/DocumentView.scss1
-rw-r--r--src/client/views/nodes/DocumentView.tsx52
-rw-r--r--src/client/views/nodes/FieldView.tsx2
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx28
-rw-r--r--src/client/views/nodes/PDFBox.tsx3
-rw-r--r--src/client/views/pdf/PDFViewer.tsx394
-rw-r--r--src/server/RouteStore.ts1
-rw-r--r--src/server/database.ts4
-rw-r--r--src/server/index.ts11
16 files changed, 257 insertions, 313 deletions
diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 23e90ece5..4e983c906 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -10,6 +10,7 @@ import { Transform } from '../util/Transform';
import { CollectionDockingView } from './collections/CollectionDockingView';
import "./MainOverlayTextBox.scss";
import { FormattedTextBox } from './nodes/FormattedTextBox';
+import { For } from 'babel-types';
interface MainOverlayTextBoxProps {
}
@@ -25,6 +26,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
private _textProxyDiv: React.RefObject<HTMLDivElement>;
private _textBottom: boolean | undefined;
private _textAutoHeight: boolean | undefined;
+ private _textBox: FormattedTextBox | undefined;
@observable public TextDoc?: Doc;
constructor(props: MainOverlayTextBoxProps) {
@@ -33,6 +35,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
MainOverlayTextBox.Instance = this;
reaction(() => FormattedTextBox.InputBoxOverlay,
(box?: FormattedTextBox) => {
+ this._textBox = box;
if (box) {
this.TextDoc = box.props.Document;
let sxf = Utils.GetScreenTransform(box ? box.CurrentDiv : undefined);
@@ -68,6 +71,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
textScroll = (e: React.UIEvent) => {
if (this._textProxyDiv.current && this._textTargetDiv) {
this._textTargetDiv.scrollTop = (e as any)._targetInst.stateNode.scrollTop;
+ console.log(this._textTargetDiv.scrollTop + " != " + (e as any)._targetInst.stateNode.scrollTop + " != " + (this._textBox!.CurrentDiv ? this._textBox!.CurrentDiv.scrollTop : -1));
}
}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 29015995f..ea49ebd5d 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -3,7 +3,7 @@ import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, configure, observable, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
-import { CirclePicker } from 'react-color';
+import { CirclePicker, SliderPicker, BlockPicker, TwitterPicker } from 'react-color';
import "normalize.css";
import * as React from 'react';
import Measure from 'react-measure';
@@ -230,6 +230,14 @@ export class MainView extends React.Component {
return { fontSize: "50%" };
}
+ onColorClick = (e: React.MouseEvent) => {
+ let target = (e.nativeEvent! as any).path[0];
+ let parent = (e.nativeEvent! as any).path[1];
+ if (target.localName === "input" || parent.localName === "span")
+ e.stopPropagation();
+ }
+
+
@observable private _colorPickerDisplay = false;
/* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */
nodesMenu() {
@@ -257,8 +265,8 @@ export class MainView extends React.Component {
<li key="redo"><button className="add-button round-button" title="Redo" onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button></li>
<li key="color"><button className="add-button round-button" title="Redo" onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} >
- <div className="toolbar-color-picker" style={this._colorPickerDisplay ? { display: "block" } : { display: "none" }}>
- <CirclePicker onChange={InkingControl.Instance.switchColor} circleSize={22} width={"220"} />
+ <div className="toolbar-color-picker" onClick={this.onColorClick} style={this._colorPickerDisplay ? { display: "block" } : { display: "none" }}>
+ <TwitterPicker color={InkingControl.Instance.selectedColor} onChange={InkingControl.Instance.switchColor} />
</div>
</div></button></li>
{btns.map(btn =>
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 3288abd90..a9bc4d3d2 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -45,7 +45,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
if (template.Name === "Bullet") {
let topDocView = this.props.docs[0];
topDocView.addTemplate(template);
- topDocView.props.Document.subBulletDocs = new List<Doc>(this.props.docs.filter(v => v !== topDocView).map(v => v.props.Document.proto!));
+ topDocView.props.Document.subBulletDocs = new List<Doc>(this.props.docs.filter(v => v !== topDocView).map(v => v.props.Document));
} else {
this.props.docs.map(d => d.addTemplate(template));
}
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 3cd525afa..3d5f7b6ea 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -48,12 +48,12 @@ export namespace Templates {
</div>` );
export const Title = new Template("Title", TemplatePosition.InnerTop,
- `<div style="height:100%">
+ `<div>
<div style="height:25px; width:100%; background-color: rgba(0, 0, 0, .4); color: white; ">
<span style="text-align:center;width:100%;font-size:20px;position:absolute;overflow:hidden;white-space:nowrap;text-overflow:ellipsis">{props.Document.title}</span>
</div>
- <div style="display:flex; flex-direction:column; height:calc(100% - 25px);">
- <div style="height:min-content; width:100%;overflow:auto">{layout}</div>
+ <div style="height:calc(100% - 25px);">
+ <div style="width:100%;overflow:auto">{layout}</div>
</div>
</div>` );
@@ -62,7 +62,7 @@ export namespace Templates {
<div style="width:100%; background-color: rgba(0, 0, 0, .4); color: white; ">
<FormattedTextBox {...props} height={"min-content"} color={"white"} fieldKey={"header"} />
</div>
- <div style="width:100%;height:min-content;overflow:auto;">{layout}</div>
+ <div style="width:100%;height:100%;overflow:auto;">{layout}</div>
</div > ` );
export const Bullet = new Template("Bullet", TemplatePosition.InnerTop,
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 7cc00ce07..4b46c73c1 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -2,36 +2,33 @@ import React = require("react");
import { library } from '@fortawesome/fontawesome-svg-core';
import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, untracked, runInAction, trace } from "mobx";
+import { action, computed, observable, trace, untracked } from "mobx";
import { observer } from "mobx-react";
import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table";
-import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss';
import "react-table/react-table.css";
+import { Doc, DocListCast, DocListCastAsync, Field } from "../../../new_fields/Doc";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { List } from "../../../new_fields/List";
+import { listSpec } from "../../../new_fields/Schema";
+import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
import { emptyFunction, returnFalse, returnZero } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
+import { Gateway } from "../../northstar/manager/Gateway";
import { SetupDrag } from "../../util/DragManager";
import { CompileScript } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
-import { COLLECTION_BORDER_WIDTH } from "../../views/globalCssVariables.scss";
+import { COLLECTION_BORDER_WIDTH, MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss';
+import { ContextMenu } from "../ContextMenu";
import { anchorPoints, Flyout } from "../DocumentDecorations";
import '../DocumentDecorations.scss';
import { EditableView } from "../EditableView";
import { DocumentView } from "../nodes/DocumentView";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
+import { CollectionPDFView } from "./CollectionPDFView";
import "./CollectionSchemaView.scss";
import { CollectionSubView } from "./CollectionSubView";
-import { Opt, Field, Doc, DocListCastAsync, DocListCast } from "../../../new_fields/Doc";
-import { Cast, FieldValue, NumCast, StrCast, BoolCast } from "../../../new_fields/Types";
-import { listSpec } from "../../../new_fields/Schema";
-import { List } from "../../../new_fields/List";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { Gateway } from "../../northstar/manager/Gateway";
-import { Docs } from "../../documents/Documents";
-import { ContextMenu } from "../ContextMenu";
-import { CollectionView } from "./CollectionView";
-import { CollectionPDFView } from "./CollectionPDFView";
import { CollectionVideoView } from "./CollectionVideoView";
-import { SelectionManager } from "../../util/SelectionManager";
-import { undoBatch } from "../../util/UndoManager";
+import { CollectionView } from "./CollectionView";
library.add(faCog);
@@ -389,6 +386,7 @@ interface CollectionSchemaPreviewProps {
CollectionView: CollectionView | CollectionPDFView | CollectionVideoView;
getTransform: () => Transform;
addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
+ moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean;
removeDocument: (document: Doc) => boolean;
active: () => boolean;
whenActiveChanged: (isActive: boolean) => void;
@@ -424,7 +422,7 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
{!this.props.Document || !this.props.width ? (null) : (
<div className="collectionSchemaView-previewDoc" style={{ transform: `translate(${this.centeringOffset}px, 0px)`, height: "100%" }}>
<DocumentView Document={this.props.Document} isTopMost={false} selectOnLoad={false}
- addDocument={this.props.addDocument} removeDocument={this.props.removeDocument}
+ addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} moveDocument={this.props.moveDocument}
ScreenToLocalTransform={this.getTransform}
ContentScaling={this.contentScaling}
PanelWidth={this.PanelWidth} PanelHeight={this.PanelHeight}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index ef12545b8..ede37534a 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -81,6 +81,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
getTransform={dxf}
CollectionView={this.props.CollectionView}
addDocument={this.props.addDocument}
+ moveDocument={this.props.moveDocument}
removeDocument={this.props.removeDocument}
active={this.props.active}
whenActiveChanged={this.props.whenActiveChanged}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index b5a3d087e..af0495e4f 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -182,8 +182,19 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
return;
}
if (html && html.indexOf("<img") !== 0 && !html.startsWith("<a")) {
- let htmlDoc = Docs.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text });
- this.props.addDocument(htmlDoc, false);
+ let path = window.location.origin + "/doc/";
+ if (text.startsWith(path)) {
+ let docid = text.replace(DocServer.prepend("/doc/"), "").split("?")[0];
+ DocServer.GetRefField(docid).then(f => {
+ if (f instanceof Doc) {
+ if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
+ (f instanceof Doc) && this.props.addDocument(f, false);
+ }
+ });
+ } else {
+ let htmlDoc = Docs.HtmlDocument(html, { ...options, width: 300, height: 300, documentText: text });
+ this.props.addDocument(htmlDoc, false);
+ }
return;
}
if (text && text.indexOf("www.youtube.com/watch") !== -1) {
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 7c72fb6e6..3a4b46b7e 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -4,6 +4,7 @@
position: inherit;
top: 0;
left:0;
+ pointer-events: all;
// background: $light-color; //overflow: hidden;
transform-origin: left top;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 343f1c748..942228e71 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,11 +1,11 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons';
-import { action, computed, IReactionDisposer, reaction, trace, observable } from "mobx";
+import { action, computed, IReactionDisposer, reaction, trace, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc";
import { List } from "../../../new_fields/List";
import { ObjectField } from "../../../new_fields/ObjectField";
-import { createSchema, makeInterface } from "../../../new_fields/Schema";
+import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema";
import { BoolCast, Cast, FieldValue, StrCast, NumCast, PromiseValue } from "../../../new_fields/Types";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
import { emptyFunction, Utils } from "../../../Utils";
@@ -26,10 +26,12 @@ import { DocComponent } from "../DocComponent";
import { PresentationView } from "../PresentationView";
import { Template } from "./../Templates";
import { DocumentContentsView } from "./DocumentContentsView";
+import * as rp from "request-promise";
import "./DocumentView.scss";
import React = require("react");
import { Id, Copy } from '../../../new_fields/FieldSymbols';
import { ContextMenuProps } from '../ContextMenuItem';
+import { RouteStore } from '../../../server/RouteStore';
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
library.add(faTrash);
@@ -285,8 +287,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
let expandedProtoDocs = expandedDocs.map(doc => Doc.GetProto(doc));
let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace");
let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target);
- if (altKey) {
- maxLocation = this.props.Document.maximizeLocation = (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace");
+ if (altKey || ctrlKey) {
+ maxLocation = this.props.Document.maximizeLocation = (ctrlKey ? maxLocation : (maxLocation === "inPlace" || !maxLocation ? "inTab" : "inPlace"));
if (!maxLocation || maxLocation === "inPlace") {
let hadView = expandedDocs.length === 1 && DocumentManager.Instance.getDocumentView(expandedProtoDocs[0], this.props.ContainingCollectionView);
let wasMinimized = !hadView && expandedDocs.reduce((min, d) => !min && !BoolCast(d.IsMinimized, false), false);
@@ -452,7 +454,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@action
- onContextMenu = (e: React.MouseEvent): void => {
+ onContextMenu = async (e: React.MouseEvent): Promise<void> => {
+ e.persist();
e.stopPropagation();
if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3 ||
e.isDefaultPrevented()) {
@@ -483,14 +486,37 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])), icon: "link" });
cm.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" });
cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" });
- if (!this.topMost) {
- // DocumentViews should stop propagation of this event
- e.stopPropagation();
- }
- ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
- if (!SelectionManager.IsSelected(this)) {
- SelectionManager.SelectDoc(this, false);
- }
+ type User = { email: string, userDocumentId: string };
+ const users: User[] = JSON.parse(await rp.get(DocServer.prepend(RouteStore.getUsers)));
+ let usersMenu: ContextMenuProps[] = users.filter(({ email }) => email !== CurrentUserUtils.email).map(({ email, userDocumentId }) => ({
+ description: email, event: async () => {
+ const userDocument = await Cast(DocServer.GetRefField(userDocumentId), Doc);
+ if (!userDocument) {
+ throw new Error(`Couldn't get user document of user ${email}`);
+ }
+ const notifDoc = await Cast(userDocument.optionalRightCollection, Doc);
+ if (notifDoc instanceof Doc) {
+ const data = await Cast(notifDoc.data, listSpec(Doc));
+ const sharedDoc = Doc.MakeAlias(this.props.Document);
+ if (data) {
+ data.push(sharedDoc);
+ } else {
+ notifDoc.data = new List([sharedDoc]);
+ }
+ }
+ }
+ }));
+ runInAction(() => {
+ cm.addItem({ description: "Share...", subitems: usersMenu });
+ if (!this.topMost) {
+ // DocumentViews should stop propagation of this event
+ e.stopPropagation();
+ }
+ ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15);
+ if (!SelectionManager.IsSelected(this)) {
+ SelectionManager.SelectDoc(this, false);
+ }
+ });
}
onPointerEnter = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = true; };
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index cf6d2012f..4738e90d7 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -109,7 +109,7 @@ export class FieldView extends React.Component<FieldViewProps> {
}
else if (field instanceof List) {
return (<div>
- {field.map(f => f instanceof Doc ? f.title : f.toString()).join(", ")}
+ {field.map(f => f instanceof Doc ? f.title : (f && f.toString && f.toString())).join(", ")}
</div>);
}
// bcz: this belongs here, but it doesn't render well so taking it out for now
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 3c590bd82..aa44995ca 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -10,11 +10,13 @@ import { EditorState, Plugin, Transaction } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { Doc, Opt } from "../../../new_fields/Doc";
import { Id } from '../../../new_fields/FieldSymbols';
+import { List } from '../../../new_fields/List';
import { RichTextField } from "../../../new_fields/RichTextField";
-import { createSchema, makeInterface } from "../../../new_fields/Schema";
+import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema";
import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
import { DocServer } from "../../DocServer";
import { Docs } from '../../documents/Documents';
+import { DocumentManager } from '../../util/DocumentManager';
import { DragManager } from "../../util/DragManager";
import buildKeymap from "../../util/ProsemirrorExampleTransfer";
import { inpRules } from "../../util/RichTextRules";
@@ -27,10 +29,10 @@ import { ContextMenu } from "../../views/ContextMenu";
import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent } from "../DocComponent";
import { InkingControl } from "../InkingControl";
+import { Templates } from '../Templates';
import { FieldView, FieldViewProps } from "./FieldView";
import "./FormattedTextBox.scss";
import React = require("react");
-import { DocumentManager } from '../../util/DocumentManager';
library.add(faEdit);
library.add(faSmile);
@@ -140,6 +142,28 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
let model: NodeType = (url.includes(".mov") || url.includes(".mp4")) ? schema.nodes.video : schema.nodes.image;
this._editorView!.dispatch(this._editorView!.state.tr.insert(0, model.create({ src: url })));
e.stopPropagation();
+ } else {
+ if (de.data instanceof DragManager.DocumentDragData) {
+ let ldocs = Cast(this.props.Document.subBulletDocs, listSpec(Doc));
+ if (!ldocs) {
+ this.props.Document.subBulletDocs = new List<Doc>([]);
+ }
+ ldocs = Cast(this.props.Document.subBulletDocs, listSpec(Doc));
+ if (!ldocs) return;
+ if (!ldocs || !ldocs[0] || ldocs[0] instanceof Promise || StrCast((ldocs[0] as Doc).layout).indexOf("CollectionView") === -1) {
+ ldocs.splice(0, 0, Docs.StackingDocument([], { title: StrCast(this.props.Document.title) + "-subBullets", x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document.height), width: 300, height: 300 }));
+ this.props.addDocument && this.props.addDocument(ldocs[0] as Doc);
+ this.props.Document.templates = new List<string>([Templates.Bullet.Layout]);
+ this.props.Document.isBullet = true;
+ }
+ let stackDoc = (ldocs[0] as Doc);
+ if (de.data.moveDocument) {
+ de.data.moveDocument(de.data.draggedDocuments[0], stackDoc, (doc) => {
+ Cast(stackDoc.data, listSpec(Doc))!.push(doc);
+ return true;
+ })
+ }
+ }
}
}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index ae68a530e..80d274c6d 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -69,7 +69,6 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
loaded = (nw: number, nh: number, np: number) => {
if (this.props.Document) {
let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document;
- console.log("pages = " + np);
doc.numPages = np;
if (doc.nativeWidth && doc.nativeHeight) return;
let oldaspect = NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1);
@@ -97,10 +96,8 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
}
render() {
- trace();
// uses mozilla pdf as default
const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"));
- console.log(pdfUrl);
let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
return (
<div onScroll={this.onScroll}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index e13d11fe6..86a17c0a6 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,29 +1,23 @@
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import React = require("react");
-import { observable, action, runInAction, computed, IReactionDisposer, reaction, trace } from "mobx";
import * as Pdfjs from "pdfjs-dist";
-import { Opt, HeightSym, WidthSym, Doc, DocListCast } from "../../../new_fields/Doc";
-import "./PDFViewer.scss";
import "pdfjs-dist/web/pdf_viewer.css";
-import { PDFBox } from "../nodes/PDFBox";
-import Page from "./Page";
-import { NumCast, Cast, BoolCast, StrCast } from "../../../new_fields/Types";
+import * as rp from "request-promise";
+import { Dictionary } from "typescript-collections";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc";
import { Id } from "../../../new_fields/FieldSymbols";
-import { DocUtils, Docs } from "../../documents/Documents";
-import { DocumentManager } from "../../util/DocumentManager";
-import { SelectionManager } from "../../util/SelectionManager";
import { List } from "../../../new_fields/List";
-import { DocumentContentsView } from "../nodes/DocumentContentsView";
-import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
-import { Transform } from "../../util/Transform";
-import { emptyFunction, returnTrue, returnFalse } from "../../../Utils";
-import { DocumentView } from "../nodes/DocumentView";
-import { DragManager } from "../../util/DragManager";
-import { Dictionary } from "typescript-collections";
-import * as rp from "request-promise";
-import { restProperty } from "babel-types";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { emptyFunction } from "../../../Utils";
import { DocServer } from "../../DocServer";
-import { number } from "prop-types";
+import { Docs, DocUtils } from "../../documents/Documents";
+import { DocumentManager } from "../../util/DocumentManager";
+import { DragManager } from "../../util/DragManager";
+import { DocumentView } from "../nodes/DocumentView";
+import { PDFBox } from "../nodes/PDFBox";
+import Page from "./Page";
+import "./PDFViewer.scss";
+import React = require("react");
import PDFMenu from "./PDFMenu";
export const scale = 2;
@@ -44,29 +38,21 @@ export class PDFViewer extends React.Component<IPDFViewerProps> {
@action
componentDidMount() {
- const pdfUrl = this.props.url;
- console.log("pdf starting to load")
- let promise = Pdfjs.getDocument(pdfUrl).promise;
-
- promise.then((pdf: Pdfjs.PDFDocumentProxy) => {
- runInAction(() => {
- console.log("pdf url received");
- this._pdf = pdf;
- });
- });
+ Pdfjs.getDocument(this.props.url).promise.then(pdf => runInAction(() => this._pdf = pdf));
}
render() {
return (
<div ref={this._mainDiv}>
- <Viewer pdf={this._pdf} loaded={this.props.loaded} scrollY={this.props.scrollY} parent={this.props.parent} mainCont={this._mainDiv} url={this.props.url} />
+ {!this._pdf ? (null) :
+ <Viewer pdf={this._pdf} loaded={this.props.loaded} scrollY={this.props.scrollY} parent={this.props.parent} mainCont={this._mainDiv} url={this.props.url} />}
</div>
);
}
}
interface IViewerProps {
- pdf: Opt<Pdfjs.PDFDocumentProxy>;
+ pdf: Pdfjs.PDFDocumentProxy;
loaded: (nw: number, nh: number, np: number) => void;
scrollY: number;
parent: PDFBox;
@@ -84,100 +70,63 @@ class Viewer extends React.Component<IViewerProps> {
// _visibleElements is the array of JSX elements that gets rendered
@observable.shallow private _visibleElements: JSX.Element[] = [];
// _isPage is an array that tells us whether or not an index is rendered as a page or as a placeholder
- @observable private _isPage: boolean[] = [];
+ @observable private _isPage: string[] = [];
@observable private _pageSizes: { width: number, height: number }[] = [];
- @observable private _startIndex: number = 0;
- @observable private _endIndex: number = 1;
- @observable private _loaded: boolean = false;
- @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
@observable private _annotations: Doc[] = [];
- @observable private _pointerEvents: "all" | "none" = "all";
@observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
private _pageBuffer: number = 1;
- private _annotationLayer: React.RefObject<HTMLDivElement>;
+ private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
private _reactionDisposer?: IReactionDisposer;
private _annotationReactionDisposer?: IReactionDisposer;
- private _pagesLoaded: number = 0;
private _dropDisposer?: DragManager.DragDropDisposer;
- constructor(props: IViewerProps) {
- super(props);
-
- this._annotationLayer = React.createRef();
+ componentDidUpdate = (prevProps: IViewerProps) => {
+ if (this.scrollY !== prevProps.scrollY) {
+ this.renderPages();
+ }
}
@action
componentDidMount = () => {
- let wasSelected = this.props.parent.props.active();
- // reaction for when document gets (de)selected
this._reactionDisposer = reaction(
- () => [this.props.parent.props.active(), this.startIndex],
- () => {
- // if deselected, render images in place of pdf
- if (wasSelected && !this.props.parent.props.active()) {
- this.saveThumbnail();
- }
- // if selected, render pdf
- else if (!wasSelected && this.props.parent.props.active()) {
- this.renderPages(this.startIndex, this.endIndex, true);
- }
- wasSelected = this.props.parent.props.active();
- this._pointerEvents = wasSelected ? "none" : "all";
- },
- { fireImmediately: true }
- );
+ () => [this.props.parent.props.active(), this.startIndex, this.endIndex],
+ async () => {
+ await this.initialLoad();
+ this.renderPages();
+ }, { fireImmediately: true });
- if (this.props.parent.Document) {
- this._annotationReactionDisposer = reaction(
- () => DocListCast(this.props.parent.Document.annotations),
- () => {
- let annotations = DocListCast(this.props.parent.Document.annotations);
- if (annotations && annotations.length) {
- this.renderAnnotations(annotations, true);
- }
- },
- { fireImmediately: true }
- );
- }
+ this._annotationReactionDisposer = reaction(
+ () => this.props.parent.Document && DocListCast(this.props.parent.Document.annotations),
+ (annotations: Doc[]) =>
+ annotations && annotations.length && this.renderAnnotations(annotations, true),
+ { fireImmediately: true });
+ }
- setTimeout(() => {
- // this.renderPages(this.startIndex, this.endIndex, true);
- this.initialLoad();
- }, 1000);
+ componentWillUnmount = () => {
+ this._reactionDisposer && this._reactionDisposer();
+ this._annotationReactionDisposer && this._annotationReactionDisposer();
}
@action
- initialLoad = () => {
- let pdf = this.props.pdf;
- if (pdf) {
- this._pageSizes = Array<{ width: number, height: number }>(pdf.numPages);
- let rendered = 0;
- for (let i = 0; i < pdf.numPages; i++) {
- pdf.getPage(i + 1).then(
- (page: Pdfjs.PDFPageProxy) => {
- runInAction(() => {
- this._pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale };
- });
- console.log(`page ${i} size retreieved`);
- rendered++;
- if (rendered === pdf!.numPages - 1) {
- this.saveThumbnail();
- }
- }
- );
+ initialLoad = async () => {
+ if (this._pageSizes.length === 0) {
+ let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
+ this._isPage = Array<string>(this.props.pdf.numPages);
+ for (let i = 0; i < this.props.pdf.numPages; i++) {
+ await this.props.pdf.getPage(i + 1).then(page => runInAction(() =>
+ pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale }));
}
+ runInAction(() =>
+ Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage));
+ this.props.loaded(pageSizes[0].width, pageSizes[0].height, this.props.pdf.numPages);
}
}
private mainCont = (div: HTMLDivElement | null) => {
- if (this._dropDisposer) {
- this._dropDisposer();
- }
+ this._dropDisposer && this._dropDisposer();
if (div) {
- this._dropDisposer = DragManager.MakeDropTarget(div, {
- handlers: { drop: this.drop.bind(this) }
- });
+ this._dropDisposer = div && DragManager.MakeDropTarget(div, { handlers: { drop: this.drop.bind(this) } });
}
}
@@ -223,154 +172,97 @@ class Viewer extends React.Component<IViewerProps> {
e.stopPropagation();
}
}
-
- componentWillUnmount = () => {
- if (this._reactionDisposer) {
- this._reactionDisposer();
- }
- if (this._annotationReactionDisposer) {
- this._annotationReactionDisposer();
- }
- }
-
+ /**
+ * Called by the Page class when it gets rendered, initializes the lists and
+ * puts a placeholder with all of the correct page sizes when all of the pages have been loaded.
+ */
@action
- saveThumbnail = async () => {
- // file address of the pdf
- const address: string = this.props.url;
- for (let i = 0; i < this._visibleElements.length; i++) {
- if (this._isPage[i]) {
- // change the address to be the file address of the PNG version of each page
- let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${i + 1}.PNG`)));
- let thisAddress = res.path;
- let nWidth = parseInt(res.width);
- let nHeight = parseInt(res.height);
- // replace page with image
- runInAction(() =>
- this._visibleElements[i] = <img key={thisAddress} style={{ width: `${nWidth * scale}px`, height: `${nHeight * scale}px` }} src={thisAddress} />);
- }
- }
- }
-
- @computed get scrollY(): number {
- return this.props.scrollY;
- }
-
- @computed get startIndex(): number {
- return Math.max(0, this.getIndex(this.scrollY) - this._pageBuffer);
- }
-
- @computed get endIndex(): number {
- let width = this._pageSizes.map(i => i ? i.width : 0);
- return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getIndex(this.scrollY + Math.max(...width)) + this._pageBuffer);
- }
-
- componentDidUpdate = (prevProps: IViewerProps) => {
- if (this.scrollY !== prevProps.scrollY || this._pdf !== this.props.pdf) {
- this._pdf = this.props.pdf;
- // render pages if the scorll position changes
- console.log(`START: ${this.startIndex}, END: ${this.endIndex}`);
- this.renderPages(this.startIndex, this.endIndex);
- }
+ pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => {
+ this.props.loaded(page.width, page.height, this.props.pdf.numPages);
}
-
@action
- private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => {
- if (removeOldAnnotations) {
- this._annotations = annotations;
- }
- else {
- this._annotations.push(...annotations);
- this._annotations = new Array<Doc>(...this._annotations);
+ getPlaceholderPage = (page: number) => {
+ if (this._isPage[page] !== "none") {
+ this._isPage[page] = "none";
+ this._visibleElements[page] = (
+ <div key={`placeholder-${page}`} className="pdfviewer-placeholder"
+ style={{ width: this._pageSizes[page].width, height: this._pageSizes[page].height }} />
+ );
}
}
-
- /**
- * @param startIndex: where to start rendering pages
- * @param endIndex: where to end rendering pages
- * @param forceRender: (optional), force pdfs to re-render, even if the page already exists
- */
@action
- renderPages = (startIndex: number, endIndex: number, forceRender: boolean = false) => {
- let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
- if (!this.props.pdf) {
- return;
- }
-
- if (this._pageSizes.length !== numPages) {
- this._pageSizes = new Array(numPages).map(i => ({ width: 0, height: 0 }));
- }
-
- // this is only for an initial render to get all of the pages rendered
- if (this._visibleElements.length !== numPages) {
- let divs = Array.from(Array(numPages).keys()).map(i => i < 5 ? (
+ getRenderedPage = (page: number) => {
+ if (this._isPage[page] !== "page") {
+ this._isPage[page] = "page";
+ this._visibleElements[page] = (
<Page
pdf={this.props.pdf}
- page={i}
- numPages={numPages}
- key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
- name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
+ page={page}
+ numPages={this.props.pdf!.numPages}
+ key={`rendered-${page + 1}`}
+ name={`${this.props.pdf.fingerprint + `-page${page + 1}`}`}
pageLoaded={this.pageLoaded}
parent={this.props.parent}
- renderAnnotations={this.renderAnnotations}
makePin={this.createPinAnnotation}
+ renderAnnotations={this.renderAnnotations}
createAnnotation={this.createAnnotation}
sendAnnotations={this.receiveAnnotations}
makeAnnotationDocuments={this.makeAnnotationDocument}
receiveAnnotations={this.sendAnnotations}
{...this.props} />
- ) :
- (<div key={`pdfviewer-placeholder-${i}`} className="pdfviewer-placeholder" style={{ width: this._pageSizes[i] ? this._pageSizes[i].width : 612 * scale, height: this._pageSizes[i] ? this._pageSizes[i].height : 792 * scale }} />)
);
- let arr = Array.from(Array(numPages).keys()).map(i => i < 5);
- this._visibleElements.push(...divs);
- this._isPage.push(...arr);
}
+ }
- // if nothing changed, return
- if (startIndex === this._startIndex && endIndex === this._endIndex && !forceRender) {
- return;
+ // change the address to be the file address of the PNG version of each page
+ // file address of the pdf
+ @action
+ getPageImage = async (page: number) => {
+ let handleError = () => this.getRenderedPage(page);
+ if (this._isPage[page] != "image") {
+ this._isPage[page] = "image";
+ const address = this.props.url;
+ let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`)));
+ runInAction(() => this._visibleElements[page] =
+ <img key={res.path} src={res.path} onError={handleError}
+ style={{ width: `${parseInt(res.width) * scale}px`, height: `${parseInt(res.height) * scale}px` }} />);
}
+ }
+
+ @computed get scrollY(): number { return this.props.scrollY; }
+
+ // startIndex: where to start rendering pages
+ @computed get startIndex(): number { return Math.max(0, this.getPageFromScroll(this.scrollY) - this._pageBuffer); }
+
+ // endIndex: where to end rendering pages
+ @computed get endIndex(): number {
+ return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY) + this._pageBuffer);
+ }
- // unrender pages outside of the pdf by replacing them with empty stand-in divs
- for (let i = 0; i < numPages; i++) {
- if (i < startIndex || i > endIndex) {
- if (this._isPage[i]) {
- this._visibleElements[i] = (
- <div key={`pdfviewer-placeholder-${i}`} className="pdfviewer-placeholder" style={{ width: this._pageSizes[i] ? this._pageSizes[i].width : 0, height: this._pageSizes[i] ? this._pageSizes[i].height : 0 }} />
- );
+ @action
+ renderPages = () => {
+ for (let i = 0; i < this.props.pdf.numPages; i++) {
+ if (i < this.startIndex || i > this.endIndex) {
+ this.getPlaceholderPage(i); // pages outside of the pdf use empty stand-in divs
+ } else {
+ if (this.props.parent.props.active()) {
+ this.getRenderedPage(i);
+ } else {
+ this.getPageImage(i);
}
- this._isPage[i] = false;
}
}
+ }
- // render pages for any indices that don't already have pages (force rerender will make these render regardless)
- for (let i = startIndex; i <= endIndex; i++) {
- if (!this._isPage[i] || (this._isPage[i] && forceRender)) {
- this._visibleElements[i] = (
- <Page
- pdf={this.props.pdf}
- page={i}
- numPages={numPages}
- key={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
- name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`}
- pageLoaded={this.pageLoaded}
- parent={this.props.parent}
- makePin={this.createPinAnnotation}
- renderAnnotations={this.renderAnnotations}
- createAnnotation={this.createAnnotation}
- sendAnnotations={this.receiveAnnotations}
- makeAnnotationDocuments={this.makeAnnotationDocument}
- receiveAnnotations={this.sendAnnotations}
- {...this.props} />
- );
- this._isPage[i] = true;
- }
+ @action
+ private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => {
+ if (removeOldAnnotations) {
+ this._annotations = annotations;
+ }
+ else {
+ this._annotations.push(...annotations);
+ this._annotations = new Array<Doc>(...this._annotations);
}
-
- this._startIndex = startIndex;
- this._endIndex = endIndex;
-
- return;
}
@action
@@ -390,10 +282,9 @@ class Viewer extends React.Component<IViewerProps> {
createPinAnnotation = (x: number, y: number, page: number): void => {
let targetDoc = Docs.TextDocument({ width: 100, height: 50, title: "New Pin Annotation" });
-
let pinAnno = new Doc();
pinAnno.x = x;
- pinAnno.y = y + this.getPageHeight(page);
+ pinAnno.y = y + this.getScrollFromPage(page);
pinAnno.width = pinAnno.height = PinRadius;
pinAnno.page = page;
pinAnno.target = targetDoc;
@@ -412,51 +303,19 @@ class Viewer extends React.Component<IViewerProps> {
}
// get the page index that the vertical offset passed in is on
- getIndex = (vOffset: number) => {
- // if (this._loaded) {
- let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
+ getPageFromScroll = (vOffset: number) => {
let index = 0;
let currOffset = vOffset;
- while (index < this._pageSizes.length && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) {
- currOffset -= this._pageSizes[index] ? this._pageSizes[index].height : this._pageSizes[0].height;
- index++;
+ while (index < this._pageSizes.length && currOffset - this._pageSizes[index].height > 0) {
+ currOffset -= this._pageSizes[index++].height;
}
return index;
- // }
- return 0;
}
- /**
- * Called by the Page class when it gets rendered, initializes the lists and
- * puts a placeholder with all of the correct page sizes when all of the pages have been loaded.
- */
- @action
- pageLoaded = (index: number, page: Pdfjs.PDFPageViewport): void => {
- if (this._loaded) {
- return;
- }
- let numPages = this.props.pdf ? this.props.pdf.numPages : 0;
- this.props.loaded(page.width, page.height, numPages);
- this._pageSizes[index - 1] = { width: page.width, height: page.height };
- this._pagesLoaded++;
- if (this._pagesLoaded === numPages) {
- this._loaded = true;
- let divs = Array.from(Array(numPages).keys()).map(i => (
- <div key={`pdfviewer-placeholder-${i}`} className="pdfviewer-placeholder" style={{ width: this._pageSizes[i] ? this._pageSizes[i].width : 0, height: this._pageSizes[i] ? this._pageSizes[i].height : 0 }} />
- ));
- this._visibleElements = new Array<JSX.Element>(...divs);
- this.renderPages(this.startIndex, this.endIndex, true);
- }
- }
-
- getPageHeight = (index: number): number => {
+ getScrollFromPage = (index: number): number => {
let counter = 0;
- if (this.props.pdf && index < this.props.pdf.numPages) {
- for (let i = 0; i < index; i++) {
- if (this._pageSizes[i]) {
- counter += this._pageSizes[i].height;
- }
- }
+ for (let i = 0; i < Math.min(this.props.pdf.numPages, index); i++) {
+ counter += this._pageSizes[i].height;
}
return counter;
}
@@ -464,7 +323,7 @@ class Viewer extends React.Component<IViewerProps> {
createAnnotation = (div: HTMLDivElement, page: number) => {
if (this._annotationLayer.current) {
if (div.style.top) {
- div.style.top = (parseInt(div.style.top) + this.getPageHeight(page)).toString();
+ div.style.top = (parseInt(div.style.top) + this.getScrollFromPage(page)).toString();
}
this._annotationLayer.current.append(div);
let savedPage = this._savedAnnotations.getValue(page);
@@ -515,13 +374,16 @@ class Viewer extends React.Component<IViewerProps> {
}
render() {
- trace();
return (
<div ref={this.mainCont} style={{ pointerEvents: "all" }}>
<div className="viewer">
{this._visibleElements}
</div>
- <div className="pdfViewer-annotationLayer" style={{ height: this.props.parent.Document.nativeHeight, width: `100%`, pointerEvents: this._pointerEvents }}>
+ <div className="pdfViewer-annotationLayer"
+ style={{
+ height: this.props.parent.Document.nativeHeight, width: `100%`,
+ pointerEvents: this.props.parent.props.active() ? "none" : "all"
+ }}>
<div className="pdfViewer-annotationLayer-subCont" ref={this._annotationLayer}>
{this._annotations.map(anno => this.renderAnnotation(anno))}
</div>
diff --git a/src/server/RouteStore.ts b/src/server/RouteStore.ts
index c4af5cdaa..5c13495ff 100644
--- a/src/server/RouteStore.ts
+++ b/src/server/RouteStore.ts
@@ -16,6 +16,7 @@ export enum RouteStore {
// USER AND WORKSPACES
getCurrUser = "/getCurrentUser",
+ getUsers = "/getUsers",
getUserDocumentId = "/getUserDocumentId",
updateCursor = "/updateCursor",
diff --git a/src/server/database.ts b/src/server/database.ts
index 70b3efced..d240bd909 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -120,9 +120,9 @@ export class Database {
}
}
- public query(query: any): Promise<mongodb.Cursor> {
+ public query(query: any, collectionName = "newDocuments"): Promise<mongodb.Cursor> {
if (this.db) {
- return Promise.resolve<mongodb.Cursor>(this.db.collection('newDocuments').find(query));
+ return Promise.resolve<mongodb.Cursor>(this.db.collection(collectionName).find(query));
} else {
return new Promise<mongodb.Cursor>(res => {
this.onConnect.push(() => res(this.query(query)));
diff --git a/src/server/index.ts b/src/server/index.ts
index b91c91282..7ef542b01 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -205,6 +205,17 @@ addSecureRoute(
addSecureRoute(
Method.GET,
+ async (_, res) => {
+ const cursor = await Database.Instance.query({}, "users");
+ const results = await cursor.toArray();
+ res.send(results.map(user => ({ email: user.email, userDocumentId: user.userDocumentId })));
+ },
+ undefined,
+ RouteStore.getUsers
+);
+
+addSecureRoute(
+ Method.GET,
(user, res, req) => {
let detector = new mobileDetect(req.headers['user-agent'] || "");
let filename = detector.mobile() !== null ? 'mobile/image.html' : 'index.html';