aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2019-07-11 02:26:24 -0400
committerSam Wilkins <samwilkins333@gmail.com>2019-07-11 02:26:24 -0400
commita1c6b6df4eb6a30bca9603dac449dc937fb479fc (patch)
tree3d0dd2d041f333fc8ddf594c0c4415df7b6d8e56
parent4b3c7ea33d564711566232acf2e8450aee1219fc (diff)
parentb1b69f8a4f9e34f0c8e667ec95f9fe16ebc8b2e4 (diff)
merged with master and fixed linter / errors
-rw-r--r--package.json1
-rw-r--r--src/Utils.ts2
-rw-r--r--src/client/DocServer.ts139
-rw-r--r--src/client/util/DocumentManager.ts2
-rw-r--r--src/client/util/History.ts136
-rw-r--r--src/client/util/SerializationHelper.ts2
-rw-r--r--src/client/views/DocumentDecorations.tsx21
-rw-r--r--src/client/views/MainView.tsx27
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackingView.scss3
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx156
-rw-r--r--src/client/views/collections/CollectionSubView.tsx2
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx22
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx8
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx29
-rw-r--r--src/client/views/nodes/DocumentView.tsx43
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx60
-rw-r--r--src/client/views/nodes/ImageBox.tsx4
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx21
-rw-r--r--src/client/views/presentationview/PresentationElement.tsx4
-rw-r--r--src/client/views/search/SearchItem.tsx6
-rw-r--r--src/new_fields/Doc.ts33
-rw-r--r--src/new_fields/Types.ts4
-rw-r--r--src/new_fields/util.ts1
-rw-r--r--src/server/GarbageCollector.ts13
-rw-r--r--src/server/Message.ts2
-rw-r--r--src/server/index.ts23
27 files changed, 440 insertions, 326 deletions
diff --git a/package.json b/package.json
index 51d1bab5d..85fc7f1be 100644
--- a/package.json
+++ b/package.json
@@ -164,6 +164,7 @@
"prosemirror-transform": "^1.1.3",
"prosemirror-view": "^1.8.3",
"pug": "^2.0.3",
+ "query-string": "^6.8.1",
"raw-loader": "^1.0.0",
"react": "^16.8.4",
"react-anime": "^2.2.0",
diff --git a/src/Utils.ts b/src/Utils.ts
index 64abd7fc1..a62f9b4ff 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -73,7 +73,7 @@ export class Utils {
};
}
- public static emit<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T) {
+ public static Emit<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T) {
this.log("Emit", message.Name, args, false);
socket.emit(message.Message, args);
}
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index e8f1aa1b8..895177841 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -20,9 +20,7 @@ import { Id, HandleUpdate } from '../new_fields/FieldSymbols';
* or update ourselves based on the server's update message, that occurs here
*/
export namespace DocServer {
- // a document cache for efficient document retrieval
- const _cache: { [id: string]: RefField | Promise<Opt<RefField>> } = {};
- // the handle / client side endpoint of the web socket (https://bit.ly/2TeALea for more info) connection established with the server
+ let _cache: { [id: string]: RefField | Promise<Opt<RefField>> } = {};
const _socket = OpenSocket(`${window.location.protocol}//${window.location.hostname}:4321`);
// this client's distinct GUID created at initialization
const GUID: string = Utils.GenerateGuid();
@@ -54,6 +52,11 @@ export namespace DocServer {
export function makeEditable() {
if (!_isReadOnly) return;
location.reload();
+ // _isReadOnly = false;
+ // _CreateField = _CreateFieldImpl;
+ // _UpdateField = _UpdateFieldImpl;
+ // _respondToUpdate = _respondToUpdateImpl;
+ // _cache = {};
}
export function isReadOnly() { return _isReadOnly; }
@@ -82,24 +85,7 @@ export namespace DocServer {
* all documents in the database.
*/
export function deleteDatabase() {
- Utils.emit(_socket, MessageStore.DeleteAll, {});
- }
-
- /**
- * This disables this client's ability to write new fields,
- * update existing fields, and update and reflect the changes if
- * other clients update shared fields. Thus, the client can only read
- * a static snapshot of their workspaces
- *
- * Currently this is conditionally called in MainView.tsx when analyzing
- * the document's url.
- */
- export function makeReadOnly() {
- _CreateField = field => {
- _cache[field[Id]] = field;
- };
- _UpdateField = emptyFunction;
- _RespondToUpdate = emptyFunction;
+ Utils.Emit(_socket, MessageStore.DeleteAll, {});
}
}
@@ -261,6 +247,15 @@ export namespace DocServer {
return map;
}
+ function _UpdateFieldImpl(id: string, diff: any) {
+ if (id === updatingId) {
+ return;
+ }
+ Utils.Emit(_socket, MessageStore.UpdateField, { id, diff });
+ }
+
+ let _UpdateField = _UpdateFieldImpl;
+
// WRITE A NEW DOCUMENT TO THE SERVER
/**
@@ -273,17 +268,13 @@ export namespace DocServer {
_CreateField(field);
}
- /**
- * The default behavior for field creation. This inserts the [Doc] instance
- * in the cache at its id, serializes the [Doc]'s initial state
- * and finally sends that seruialized data to the server.
- * @param field the [RefField] to be serialized and sent to the server to be stored in the database
- */
- let _CreateField = (field: RefField) => {
+ function _CreateFieldImpl(field: RefField) {
_cache[field[Id]] = field;
const initialState = SerializationHelper.Serialize(field);
- Utils.emit(_socket, MessageStore.CreateField, initialState);
- };
+ Utils.Emit(_socket, MessageStore.CreateField, initialState);
+ }
+
+ let _CreateField = _CreateFieldImpl;
// NOTIFY THE SERVER OF AN UPDATE TO A DOC'S STATE
@@ -299,52 +290,7 @@ export namespace DocServer {
_UpdateField(id, updatedState);
}
- /**
- * The default behavior for indicating to the server that we've locally updated
- * a document.
- * @param id the id of the [Doc] whose state has been updated in our client
- * @param updatedState the new value of the document. At some point, this
- * should actually be a proper diff, to improve efficiency
- */
- let _UpdateField = (id: string, updatedState: any) => {
- // don't emit a duplicate message if the server is already
- // (asynchronously) still updating this document's state.
- if (id === updatingId) {
- return;
- }
- // creates the diff object to send to the server
- let diff: Diff = { id, diff: updatedState };
- // emit this diff to notify server
- Utils.emit(_socket, MessageStore.UpdateField, diff);
- };
-
- // RESPOND TO THE SERVER'S INDICATION THAT A DOC'S STATE HAS BEEN UPDATED
-
- /**
- * Whenever the client receives an update, execute the
- * current behavior.
- */
- Utils.AddServerHandler(_socket, MessageStore.UpdateField, RespondToUpdate);
-
- /**
- * A wrapper around the function local variable _respondToUpdate.
- * This allows us to swap in different executions while comfortably
- * calling the same function throughout the code base (such as in Util.makeReadonly())
- * @param diff kept as [any], but actually the [Diff] object sent from the server containing
- * the [Doc]'s id and its new state
- */
- function RespondToUpdate(diff: any) {
- _RespondToUpdate(diff);
- }
-
- /**
- * The default behavior for responding to another client's indication
- * that it has updated the state of a [Doc] that is also in use by
- * this client
- * @param diff kept as [any], but actually the [Diff] object sent from the server containing
- * the [Doc]'s id and its new state
- */
- let _RespondToUpdate = (diff: any) => {
+ function _respondToUpdateImpl(diff: any) {
const id = diff.id;
// to be valid, the Diff object must reference
// a document's id
@@ -377,6 +323,45 @@ export namespace DocServer {
// otherwise, just execute the update
update(field);
}
- };
+ }
+
+ export function DeleteDocument(id: string) {
+ Utils.Emit(_socket, MessageStore.DeleteField, id);
+ }
+
+ export function DeleteDocuments(ids: string[]) {
+ Utils.Emit(_socket, MessageStore.DeleteFields, ids);
+ }
+
+
+ function _respondToDeleteImpl(ids: string | string[]) {
+ function deleteId(id: string) {
+ delete _cache[id];
+ }
+ if (typeof ids === "string") {
+ deleteId(ids);
+ } else if (Array.isArray(ids)) {
+ ids.map(deleteId);
+ }
+ }
+
+ let _RespondToUpdate = _respondToUpdateImpl;
+ let _respondToDelete = _respondToDeleteImpl;
+
+ function respondToUpdate(diff: any) {
+ _RespondToUpdate(diff);
+ }
+
+ function respondToDelete(ids: string | string[]) {
+ _respondToDelete(ids);
+ }
+
+ function connected() {
+ _socket.emit(MessageStore.Bar.Message, GUID);
+ }
+ Utils.AddServerHandler(_socket, MessageStore.Foo, connected);
+ Utils.AddServerHandler(_socket, MessageStore.UpdateField, respondToUpdate);
+ Utils.AddServerHandler(_socket, MessageStore.DeleteField, respondToDelete);
+ Utils.AddServerHandler(_socket, MessageStore.DeleteFields, respondToDelete);
} \ No newline at end of file
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index acd8dcef7..8fa460bca 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -106,7 +106,7 @@ export class DocumentManager {
@computed
public get LinkedDocumentViews() {
- let pairs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => {
+ let pairs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush)).reduce((pairs, dv) => {
let linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document);
pairs.push(...linksList.reduce((pairs, link) => {
if (link) {
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 545ea8629..1a807b581 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -2,6 +2,8 @@ import { Doc, Opt, Field } from "../../new_fields/Doc";
import { DocServer } from "../DocServer";
import { RouteStore } from "../../server/RouteStore";
import { MainView } from "../views/MainView";
+import * as qs from 'query-string';
+import { Utils, OmitKeys } from "../../Utils";
export namespace HistoryUtil {
export interface DocInitializerList {
@@ -11,9 +13,11 @@ export namespace HistoryUtil {
export interface DocUrl {
type: "doc";
docId: string;
- initializers: {
+ initializers?: {
[docId: string]: DocInitializerList;
};
+ readonly?: boolean;
+ nro?: boolean;
}
export type ParsedUrl = DocUrl;
@@ -21,7 +25,7 @@ export namespace HistoryUtil {
// const handlers: ((state: ParsedUrl | null) => void)[] = [];
function onHistory(e: PopStateEvent) {
if (window.location.pathname !== RouteStore.home) {
- const url = e.state as ParsedUrl || parseUrl(window.location.pathname);
+ const url = e.state as ParsedUrl || parseUrl(window.location);
if (url) {
switch (url.type) {
case "doc":
@@ -62,42 +66,111 @@ export namespace HistoryUtil {
// }
// }
- export function parseUrl(pathname: string): ParsedUrl | undefined {
- let pathnameSplit = pathname.split("/");
- if (pathnameSplit.length !== 2) {
- return undefined;
+ const parsers: { [type: string]: (pathname: string[], opts: qs.ParsedQuery) => ParsedUrl | undefined } = {};
+ const stringifiers: { [type: string]: (state: ParsedUrl) => string } = {};
+
+ type ParserValue = true | "none" | "json" | ((value: string) => any);
+
+ type Parser = {
+ [key: string]: ParserValue
+ };
+
+ function addParser(type: string, requiredFields: Parser, optionalFields: Parser, customParser?: (pathname: string[], opts: qs.ParsedQuery, current: ParsedUrl) => ParsedUrl | null | undefined) {
+ function parse(parser: ParserValue, value: string | string[] | null | undefined) {
+ if (value === undefined || value === null) {
+ return value;
+ }
+ if (Array.isArray(value)) {
+ } else if (parser === true || parser === "json") {
+ value = JSON.parse(value);
+ } else if (parser === "none") {
+ } else {
+ value = parser(value);
+ }
+ return value;
}
- const type = pathnameSplit[0];
- const data = pathnameSplit[1];
+ parsers[type] = (pathname, opts) => {
+ const current: any = { type };
+ for (const required in requiredFields) {
+ if (!(required in opts)) {
+ return undefined;
+ }
+ const parser = requiredFields[required];
+ let value = opts[required];
+ value = parse(parser, value);
+ if (value !== null && value !== undefined) {
+ current[required] = value;
+ }
+ }
+ for (const opt in optionalFields) {
+ if (!(opt in opts)) {
+ continue;
+ }
+ const parser = optionalFields[opt];
+ let value = opts[opt];
+ value = parse(parser, value);
+ if (value !== undefined) {
+ current[opt] = value;
+ }
+ }
+ if (customParser) {
+ const val = customParser(pathname, opts, current);
+ if (val === null) {
+ return undefined;
+ } else if (val === undefined) {
+ return current;
+ } else {
+ return val;
+ }
+ }
+ return current;
+ };
+ }
- if (type === "doc") {
- const s = data.split("?");
- if (s.length < 1 || s.length > 2) {
- return undefined;
+ function addStringifier(type: string, keys: string[], customStringifier?: (state: ParsedUrl, current: string) => string) {
+ stringifiers[type] = state => {
+ let path = DocServer.prepend(`/${type}`);
+ if (customStringifier) {
+ path = customStringifier(state, path);
}
- const docId = s[0];
- const initializers = s.length === 2 ? JSON.parse(decodeURIComponent(s[1])) : {};
- return {
- type: "doc",
- docId,
- initializers
- };
+ const queryObj = OmitKeys(state, keys).extract;
+ const query: any = {};
+ Object.keys(queryObj).forEach(key => query[key] = queryObj[key] === null ? null : queryObj[key]);
+ const queryString = qs.stringify(query);
+ return path + (queryString ? `?${queryString}` : "");
+ };
+ }
+
+ addParser("doc", {}, { readonly: true, initializers: true, nro: true }, (pathname, opts, current) => {
+ if (pathname.length !== 2) return undefined;
+
+ current.initializers = current.initializers || {};
+ const docId = pathname[1];
+ current.docId = docId;
+ });
+ addStringifier("doc", ["initializers", "readonly", "nro"], (state, current) => {
+ return `${current}/${state.docId}`;
+ });
+
+
+ export function parseUrl(location: Location | URL): ParsedUrl | undefined {
+ const pathname = location.pathname.substring(1);
+ const search = location.search;
+ const opts = qs.parse(search, { sort: false });
+ let pathnameSplit = pathname.split("/");
+
+ const type = pathnameSplit[0];
+
+ if (type in parsers) {
+ return parsers[type](pathnameSplit, opts);
}
return undefined;
}
export function createUrl(params: ParsedUrl): string {
- let baseUrl = DocServer.prepend(`/${params.type}`);
- switch (params.type) {
- case "doc":
- const initializers = encodeURIComponent(JSON.stringify(params.initializers));
- const id = params.docId;
- let url = baseUrl + `/${id}`;
- if (Object.keys(params.initializers).length) {
- url += `?${initializers}`;
- }
- return url;
+ if (params.type in stringifiers) {
+ return stringifiers[params.type](params);
}
return "";
}
@@ -112,7 +185,10 @@ export namespace HistoryUtil {
async function onDocUrl(url: DocUrl) {
const field = await DocServer.GetRefField(url.docId);
- await Promise.all(Object.keys(url.initializers).map(id => initDoc(id, url.initializers[id])));
+ const init = url.initializers;
+ if (init) {
+ await Promise.all(Object.keys(init).map(id => initDoc(id, init[id])));
+ }
if (field instanceof Doc) {
MainView.Instance.openWorkspace(field, true);
}
diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts
index a7246d7c4..17ae407c4 100644
--- a/src/client/util/SerializationHelper.ts
+++ b/src/client/util/SerializationHelper.ts
@@ -9,7 +9,7 @@ export namespace SerializationHelper {
export function Serialize(obj: Field): any {
if (obj === undefined || obj === null) {
- return undefined;
+ return null;
}
if (typeof obj !== 'object') {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2e26a2286..c24d66ee4 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -84,26 +84,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
let fieldTemplate = fieldTemplateView.props.Document;
let docTemplate = fieldTemplateView.props.ContainingCollectionView!.props.Document;
let metaKey = text.slice(1, text.length);
-
- // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
- let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
- let layout = StrCast(fieldTemplate.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
- if (backgroundLayout) {
- layout = StrCast(fieldTemplate.layout).replace(/fieldKey={"annotations"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`);
- backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
- }
- let nw = Cast(fieldTemplate.nativeWidth, "number");
- let nh = Cast(fieldTemplate.nativeHeight, "number");
-
- fieldTemplate.title = metaKey;
- fieldTemplate.layout = layout;
- fieldTemplate.backgroundLayout = backgroundLayout;
- fieldTemplate.nativeWidth = nw;
- fieldTemplate.nativeHeight = nh;
- fieldTemplate.embed = true;
- fieldTemplate.isTemplate = true;
- fieldTemplate.templates = new List<string>([Templates.TitleBar(metaKey)]);
- fieldTemplate.proto = Doc.GetProto(docTemplate);
+ Doc.MakeTemplate(fieldTemplate, metaKey, Doc.GetProto(docTemplate));
}
else {
if (SelectionManager.SelectedDocuments().length > 0) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 1f628228a..b9e5719fd 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -13,7 +13,7 @@ import { Id } from '../../new_fields/FieldSymbols';
import { InkTool } from '../../new_fields/InkField';
import { List } from '../../new_fields/List';
import { listSpec } from '../../new_fields/Schema';
-import { Cast, FieldValue, NumCast } from '../../new_fields/Types';
+import { Cast, FieldValue, NumCast, BoolCast } from '../../new_fields/Types';
import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
import { RouteStore } from '../../server/RouteStore';
import { emptyFunction, returnOne, returnTrue } from '../../Utils';
@@ -93,15 +93,6 @@ export class MainView extends React.Component {
MainView.Instance = this;
// causes errors to be generated when modifying an observable outside of an action
configure({ enforceActions: "observed" });
- if (window.location.search.includes("readonly")) {
- DocServer.Util.makeReadOnly();
- }
- if (window.location.search.includes("safe")) {
- if (!window.location.search.includes("nro")) {
- DocServer.Util.makeReadOnly();
- }
- CollectionBaseView.SetSafeMode(true);
- }
if (window.location.pathname !== RouteStore.home) {
let pathname = window.location.pathname.substr(1).split("/");
if (pathname.length > 1) {
@@ -195,7 +186,21 @@ export class MainView extends React.Component {
openWorkspace = async (doc: Doc, fromHistory = false) => {
CurrentUserUtils.MainDocId = doc[Id];
this.mainContainer = doc;
- fromHistory || HistoryUtil.pushState({ type: "doc", docId: doc[Id], initializers: {} });
+ const state = HistoryUtil.parseUrl(window.location) || {} as any;
+ fromHistory || HistoryUtil.pushState({ type: "doc", docId: doc[Id], readonly: state.readonly, nro: state.nro });
+ if (state.readonly === true || state.readonly === null) {
+ DocServer.Control.makeReadOnly();
+ } else if (state.safe) {
+ if (!state.nro) {
+ DocServer.Control.makeReadOnly();
+ }
+ CollectionBaseView.SetSafeMode(true);
+ } else if (state.nro || state.nro === null || state.readonly === false) {
+ } else if (BoolCast(doc.readOnly)) {
+ DocServer.Control.makeReadOnly();
+ } else {
+ DocServer.Control.makeEditable();
+ }
const col = await Cast(CurrentUserUtils.UserDocument.optionalRightCollection, Doc);
// 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(async () => {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index cf39e26a8..33329d1ed 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -405,6 +405,7 @@ interface CollectionSchemaPreviewProps {
renderDepth: number;
width: () => number;
height: () => number;
+ showOverlays?: (doc: Doc) => { title?: string, caption?: string };
CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView;
getTransform: () => Transform;
addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
@@ -488,6 +489,7 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre
fitToBox={this.props.fitToBox}
renderDepth={this.props.renderDepth + 1}
selectOnLoad={false}
+ showOverlays={this.props.showOverlays}
addDocument={this.props.addDocument}
removeDocument={this.props.removeDocument}
moveDocument={this.props.moveDocument}
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index bc733f152..7e886304d 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -56,6 +56,9 @@
margin-left: -5;
}
+ .collectionStackingView-columnDoc{
+ display: inline-block;
+ }
.collectionStackingView-columnDoc,
.collectionStackingView-masonryDoc {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 416c536f6..9266fc8fd 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -12,6 +12,8 @@ import "./CollectionStackingView.scss";
import { CollectionSubView } from "./CollectionSubView";
import { undoBatch } from "../../util/UndoManager";
import { DragManager } from "../../util/DragManager";
+import { DocTypes } from "../../documents/Documents";
+import { Transform } from "../../util/Transform";
@observer
export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -19,6 +21,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
_draggerRef = React.createRef<HTMLDivElement>();
_heightDisposer?: IReactionDisposer;
_gridSize = 1;
+ _docXfs: any[] = [];
@computed get xMargin() { return NumCast(this.props.Document.xMargin, 2 * this.gridGap); }
@computed get yMargin() { return NumCast(this.props.Document.yMargin, 2 * this.gridGap); }
@computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); }
@@ -26,18 +29,11 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
@computed get columnWidth() { return this.singleColumn ? (this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin) : Math.min(this.props.PanelWidth() - 2 * this.xMargin, NumCast(this.props.Document.columnWidth, 250)); }
@computed get filteredChildren() { return this.childDocs.filter(d => !d.isMinimized); }
- singleColDocHeight(d: Doc) {
- let nw = NumCast(d.nativeWidth);
- let nh = NumCast(d.nativeHeight);
- let aspect = nw && nh ? nh / nw : 1;
- let wid = Math.min(d[WidthSym](), this.columnWidth);
- return (nw && nh) ? wid * aspect : d[HeightSym]();
- }
componentDidMount() {
this._heightDisposer = reaction(() => [this.yMargin, this.gridGap, this.columnWidth, this.childDocs.map(d => [d.height, d.width, d.zoomBasis, d.nativeHeight, d.nativeWidth, d.isMinimized])],
() => this.singleColumn &&
(this.props.Document.height = this.filteredChildren.reduce((height, d, i) =>
- height + this.singleColDocHeight(d) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap), this.yMargin))
+ height + this.getDocHeight(d) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap), this.yMargin))
, { fireImmediately: true });
}
componentWillUnmount() {
@@ -48,90 +44,86 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
moveDocument = (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean): boolean => {
return this.props.removeDocument(doc) && addDocument(doc);
}
- getSingleDocTransform(doc: Doc, ind: number, width: number) {
- let localY = this.filteredChildren.reduce((height, d, i) =>
- height + (i < ind ? this.singleColDocHeight(d) + this.gridGap : 0), this.yMargin);
- let translate = this.props.ScreenToLocalTransform().inverse().transformPoint((this.props.PanelWidth() - width) / 2, localY);
- let outerXf = Utils.GetScreenTransform(this._masonryGridRef!);
- let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translate[0], outerXf.translateY - translate[1]);
- return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]).scale(NumCast(doc.width, 1) / this.columnWidth);
- }
createRef = (ele: HTMLDivElement | null) => {
this._masonryGridRef = ele;
this.createDropTarget(ele!);
}
- @computed
- get singleColumnChildren() {
- return this.filteredChildren.map((d, i) => {
- let layoutDoc = Doc.expandTemplateLayout(d, this.props.DataDoc);
- let width = () => d.nativeWidth ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth;
- let height = () => this.singleColDocHeight(layoutDoc);
- let dxf = () => this.getSingleDocTransform(layoutDoc, i, width()).scale(this.columnWidth / d[WidthSym]());
- let gap = i === 0 ? 0 : this.gridGap;
- return <div className="collectionStackingView-columnDoc"
- key={d[Id]}
- style={{ width: width(), display: "inline-block", marginTop: gap, height: `${height() / (this.props.Document[HeightSym]() - 2 * this.yMargin) * 100}%` }} >
- <CollectionSchemaPreview
- Document={layoutDoc}
- DataDocument={d !== this.props.DataDoc ? this.props.DataDoc : undefined}
- renderDepth={this.props.renderDepth}
- width={width}
- height={height}
- 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}
- addDocTab={this.props.addDocTab}
- setPreviewScript={emptyFunction}
- previewScript={undefined}>
- </CollectionSchemaPreview>
- </div>;
- });
+ overlays = (doc: Doc) => {
+ return doc.type === DocTypes.IMG ? { title: "title", caption: "caption" } : {};
}
- getDocTransform(doc: Doc, dref: HTMLDivElement) {
- let { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
+
+ getDisplayDoc(layoutDoc: Doc, d: Doc, dxf: () => Transform) {
+ let dataDoc = d !== this.props.DataDoc ? this.props.DataDoc : undefined;
+ let width = () => d.nativeWidth ? Math.min(layoutDoc[WidthSym](), this.columnWidth) : this.columnWidth;
+ let height = () => this.getDocHeight(layoutDoc);
+ let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]());
+ return <CollectionSchemaPreview
+ Document={layoutDoc}
+ DataDocument={dataDoc}
+ showOverlays={this.overlays}
+ renderDepth={this.props.renderDepth}
+ width={width}
+ height={height}
+ getTransform={finalDxf}
+ CollectionView={this.props.CollectionView}
+ addDocument={this.props.addDocument}
+ moveDocument={this.props.moveDocument}
+ removeDocument={this.props.removeDocument}
+ active={this.props.active}
+ whenActiveChanged={this.props.whenActiveChanged}
+ addDocTab={this.props.addDocTab}
+ setPreviewScript={emptyFunction}
+ previewScript={undefined}>
+ </CollectionSchemaPreview>;
+ }
+ getDocHeight(d: Doc) {
+ let nw = NumCast(d.nativeWidth);
+ let nh = NumCast(d.nativeHeight);
+ let aspect = nw && nh ? nh / nw : 1;
+ let wid = Math.min(d[WidthSym](), this.columnWidth);
+ return (nw && nh) ? wid * aspect : d[HeightSym]();
+ }
+
+
+ offsetTransform(doc: Doc, translateX: number, translateY: number) {
let outerXf = Utils.GetScreenTransform(this._masonryGridRef!);
let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]).scale(NumCast(doc.width, 1) / this.columnWidth);
}
- docXfs: any[] = [];
+ getDocTransform(doc: Doc, dref: HTMLDivElement) {
+ let { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
+ return this.offsetTransform(doc, translateX, translateY);
+ }
+ getSingleDocTransform(doc: Doc, ind: number, width: number) {
+ let localY = this.filteredChildren.reduce((height, d, i) =>
+ height + (i < ind ? this.getDocHeight(Doc.expandTemplateLayout(d, this.props.DataDoc)) + this.gridGap : 0), this.yMargin);
+ let translate = this.props.ScreenToLocalTransform().inverse().transformPoint((this.props.PanelWidth() - width) / 2, localY);
+ return this.offsetTransform(doc, translate[0], translate[1]);
+ }
+
@computed
get children() {
- this.docXfs.length = 0;
+ this._docXfs.length = 0;
return this.filteredChildren.map((d, i) => {
- let aspect = d.nativeHeight ? NumCast(d.nativeWidth) / NumCast(d.nativeHeight) : undefined;
- let dref = React.createRef<HTMLDivElement>();
- let dxf = () => this.getDocTransform(d, dref.current!).scale(this.columnWidth / d[WidthSym]());
- let width = () => d.nativeWidth ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth;
- let height = () => aspect ? width() / aspect : d[HeightSym]();
- let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap));
- this.docXfs.push({ dxf: dxf, width: width, height: height });
- return (<div className="collectionStackingView-masonryDoc"
- key={d[Id]}
- ref={dref}
- style={{ gridRowEnd: `span ${rowSpan}` }} >
- <CollectionSchemaPreview
- Document={d}
- DataDocument={this.props.Document.layout instanceof Doc ? this.props.Document : this.props.DataDoc}
- renderDepth={this.props.renderDepth}
- CollectionView={this.props.CollectionView}
- addDocument={this.props.addDocument}
- moveDocument={this.props.moveDocument}
- removeDocument={this.props.removeDocument}
- getTransform={dxf}
- width={width}
- height={height}
- active={this.props.active}
- addDocTab={this.props.addDocTab}
- whenActiveChanged={this.props.whenActiveChanged}
- setPreviewScript={emptyFunction}
- previewScript={undefined}>
- </CollectionSchemaPreview>
- </div>);
+ let layoutDoc = Doc.expandTemplateLayout(d, this.props.DataDoc);
+ let width = () => d.nativeWidth ? Math.min(layoutDoc[WidthSym](), this.columnWidth) : this.columnWidth;
+ let height = () => this.getDocHeight(layoutDoc);
+ if (this.singleColumn) {
+ let dxf = () => this.getSingleDocTransform(layoutDoc, i, width());
+ let rowHgtPcnt = height() / (this.props.Document[HeightSym]() - 2 * this.yMargin) * 100;
+ return <div className="collectionStackingView-columnDoc" key={d[Id]} style={{ width: width(), marginTop: i === 0 ? 0 : this.gridGap, height: `${rowHgtPcnt}%` }} >
+ {this.getDisplayDoc(layoutDoc, d, dxf)}
+ </div>;
+ } else {
+ let dref = React.createRef<HTMLDivElement>();
+ let dxf = () => this.getDocTransform(layoutDoc, dref.current!);
+ let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap));
+ this._docXfs.push({ dxf: dxf, width: width, height: height });
+ return <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={{ gridRowEnd: `span ${rowSpan}` }} >
+ {this.getDisplayDoc(layoutDoc, d, dxf)}
+ </div>;
+ }
});
}
@@ -178,7 +170,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
let targInd = -1;
let where = [de.x, de.y];
if (de.data instanceof DragManager.DocumentDragData) {
- this.docXfs.map((cd, i) => {
+ this._docXfs.map((cd, i) => {
let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
@@ -204,7 +196,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
onDrop = (e: React.DragEvent): void => {
let where = [e.clientX, e.clientY];
let targInd = -1;
- this.docXfs.map((cd, i) => {
+ this._docXfs.map((cd, i) => {
let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height());
if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
@@ -241,7 +233,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
gridAutoRows: this.singleColumn ? undefined : `${this._gridSize}px`
}}
>
- {this.singleColumn ? this.singleColumnChildren : this.children}
+ {this.children}
{this.singleColumn ? (null) : this.columnDragger}
</div>
</div>
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index a7614b605..7e38fb0b2 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -102,7 +102,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
} else if (de.data.moveDocument) {
let movedDocs = de.data.options === this.props.Document[Id] ? de.data.draggedDocuments : de.data.droppedDocuments;
added = movedDocs.reduce((added: boolean, d) =>
- de.data.moveDocument(d, this.props.Document, this.props.addDocument) || added, false);
+ de.data.moveDocument(d, this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.addDocument) || added, false);
} else {
added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false);
}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ef340d770..e88f1a9d0 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,7 +1,7 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faAngleRight, faCamera, faExpand, faTrash, faBell, faCaretDown, faCaretRight, faCaretSquareDown, faCaretSquareRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, trace } from "mobx";
+import { action, computed, observable, trace, untracked } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast, HeightSym, WidthSym, Opt } from '../../../new_fields/Doc';
import { Id } from '../../../new_fields/FieldSymbols';
@@ -73,9 +73,10 @@ class TreeView extends React.Component<TreeViewProps> {
@observable _collapsed: boolean = true;
@computed get fieldKey() {
- let keys = Array.from(Object.keys(this.resolvedDataDoc));
+ let keys = Array.from(Object.keys(this.resolvedDataDoc)); // bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set
if (this.resolvedDataDoc.proto instanceof Doc) {
- keys.push(...Array.from(Object.keys(this.resolvedDataDoc.proto)));
+ let arr = Array.from(Object.keys(this.resolvedDataDoc.proto));// bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set
+ keys.push(...arr);
while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1);
}
let keyList: string[] = [];
@@ -113,12 +114,12 @@ class TreeView extends React.Component<TreeViewProps> {
}
}
onPointerLeave = (e: React.PointerEvent): void => {
- this.props.document.libraryBrush = false;
+ this.props.document.libraryBrush = undefined;
this._header!.current!.className = "treeViewItem-header";
document.removeEventListener("pointermove", this.onDragMove, true);
}
onDragMove = (e: PointerEvent): void => {
- this.props.document.libraryBrush = false;
+ this.props.document.libraryBrush = undefined;
let x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
let rect = this._header!.current!.getBoundingClientRect();
let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
@@ -203,7 +204,7 @@ class TreeView extends React.Component<TreeViewProps> {
let onItemDown = SetupDrag(reference, () => this.resolvedDataDoc, this.move, this.props.dropAction, this.props.treeViewId, true);
let headerElements = (
- <span className="collectionTreeView-keyHeader" key={this._chosenKey}
+ <span className="collectionTreeView-keyHeader" key={this._chosenKey + "chosen"}
onPointerDown={action(() => {
let ind = this.keyList.indexOf(this._chosenKey);
ind = (ind + 1) % this.keyList.length;
@@ -219,8 +220,8 @@ class TreeView extends React.Component<TreeViewProps> {
return <>
<div className="docContainer" id={`docContainer-${this.props.parentKey}`} ref={reference} onPointerDown={onItemDown}
style={{
- background: BoolCast(this.props.document.libraryBrush, false) ? "#06121212" : "0",
- outline: BoolCast(this.props.document.workspaceBrush, false) ? "dashed 1px #06123232" : undefined,
+ background: BoolCast(this.props.document.libraryBrush) ? "#06121212" : "0",
+ outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined,
pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? "all" : "none"
}}
>
@@ -354,7 +355,7 @@ class TreeView extends React.Component<TreeViewProps> {
</ul >;
} else {
let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc);
- contentElement = <div ref={this._dref} style={{ display: "inline-block", height: this.docHeight() }} key={this.props.document[Id]}>
+ contentElement = <div ref={this._dref} style={{ display: "inline-block", height: this.docHeight() }} key={this.props.document[Id] + this.props.document.title}>
<CollectionSchemaPreview
Document={layoutDoc}
DataDocument={this.resolvedDataDoc}
@@ -427,7 +428,7 @@ class TreeView extends React.Component<TreeViewProps> {
dataDoc={dataDoc}
containingCollection={containingCollection}
treeViewId={treeViewId}
- key={child[Id]}
+ key={child[Id] + "child " + i}
indentDocument={indent}
renderDepth={renderDepth}
deleteDoc={remove}
@@ -526,7 +527,6 @@ export class CollectionTreeView extends CollectionSubView(Document) {
let dropAction = StrCast(this.props.Document.dropAction) as dropActionType;
let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc);
-
return !this.childDocs ? (null) : (
<div id="body" className="collectionTreeView-dropTarget"
style={{ overflow: "auto" }}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1777c287c..1867938a7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -286,9 +286,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
// TODO This technically isn't correct if type !== "doc", as
// currently nothing is done, but we should probably push a new state
if (state.type === "doc" && panX !== undefined && panY !== undefined) {
- const init = state.initializers[id];
+ const init = state.initializers![id];
if (!init) {
- state.initializers[id] = {
+ state.initializers![id] = {
panX, panY
};
HistoryUtil.pushState(state);
@@ -302,7 +302,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
const newPanX = NumCast(doc.x) + NumCast(doc.width) / NumCast(doc.zoomBasis, 1) / 2;
const newPanY = NumCast(doc.y) + NumCast(doc.height) / NumCast(doc.zoomBasis, 1) / 2;
const newState = HistoryUtil.getState();
- newState.initializers[id] = { panX: newPanX, panY: newPanY };
+ newState.initializers![id] = { panX: newPanX, panY: newPanY };
HistoryUtil.pushState(newState);
this.setPan(newPanX, newPanY);
@@ -501,7 +501,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`;
const easing = () => this.props.Document.panTransformType === "Ease";
- if (this.props.fieldExt) Doc.UpdateDocumentExtensionForField(this.extensionDoc, this.props.fieldKey);
+ if (this.props.fieldExt) Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey);
return (
<div className={containerName} ref={this.createDropTarget} onWheel={this.onPointerWheel}
style={{ borderRadius: "inherit" }}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index eb786d537..ed6b224a7 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -89,34 +89,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
return new List<string>();
}
@computed get finalLayout() {
- const baseLayout = this.props.layoutKey === "overlayLayout" ? "<div/>" : this.layout;
- let base = baseLayout;
- let layout = baseLayout;
-
- // bcz: templates are intended only for a document's primary layout or overlay (not background). However,
- // a DocumentContentsView is used to render annotation overlays, so we detect that here
- // by checking the layoutKey. This should probably be moved into
- // a prop so that the overlay can explicitly turn off templates.
- if ((this.props.layoutKey === "overlayLayout" && StrCast(this.props.Document.layout).indexOf("CollectionView") !== -1) ||
- (this.props.layoutKey === "layout" && StrCast(this.props.Document.layout).indexOf("CollectionView") === -1) ||
- (this.props.layoutKey === "layout" && NumCast(this.props.Document.viewType)) !== CollectionViewType.Freeform) {
- this.templates.forEach(template => {
- let self = this;
- // this scales constants in the markup by the scaling applied to the document, but caps the constants to be smaller
- // than the width/height of the containing document
- function convertConstantsToNative(match: string, offset: number, x: string) {
- let px = Number(match.replace("px", ""));
- return `${Math.min(NumCast(self.props.Document.height, 0),
- Math.min(NumCast(self.props.Document.width, 0),
- px * self.props.ScreenToLocalTransform().Scale))}px`;
- }
- // let nativizedTemplate = template.replace(/([0-9]+)px/g, convertConstantsToNative);
- // layout = nativizedTemplate.replace("{layout}", base);
- layout = template.replace("{layout}", base);
- base = layout;
- });
- }
- return layout;
+ return this.props.layoutKey === "overlayLayout" ? "<div/>" : this.layout;
}
render() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5b5653309..718552dc9 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -8,9 +8,9 @@ import { ObjectField } from "../../../new_fields/ObjectField";
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";
+import { emptyFunction, Utils, returnFalse, returnTrue } from "../../../Utils";
import { DocServer } from "../../DocServer";
-import { Docs, DocUtils } from "../../documents/Documents";
+import { Docs, DocUtils, DocTypes } from "../../documents/Documents";
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager, dropActionType } from "../../util/DragManager";
import { SearchUtil } from "../../util/SearchUtil";
@@ -34,6 +34,7 @@ import { ContextMenuProps } from '../ContextMenuItem';
import { list, object, createSimpleSchema } from 'serializr';
import { LinkManager } from '../../util/LinkManager';
import { RouteStore } from '../../../server/RouteStore';
+import { FormattedTextBox } from './FormattedTextBox';
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
library.add(fa.faTrash);
@@ -78,6 +79,7 @@ export interface DocumentViewProps {
moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
renderDepth: number;
+ showOverlays?: (doc: Doc) => { title?: string, caption?: string };
ContentScaling: () => number;
PanelWidth: () => number;
PanelHeight: () => number;
@@ -291,7 +293,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
fullScreenAlias.templates = new List<string>();
this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab");
SelectionManager.DeselectAll();
- this.props.Document.libraryBrush = false;
+ this.props.Document.libraryBrush = undefined;
}
else if (CurrentUserUtils.MainDocId !== this.props.Document[Id] &&
(Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
@@ -573,7 +575,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
onPointerEnter = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = true; };
- onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; };
+ onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = undefined; };
isSelected = () => SelectionManager.IsSelected(this);
@action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
@@ -593,6 +595,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
let foregroundColor = StrCast(this.props.Document.layout instanceof Doc ? this.props.Document.layout.color : this.props.Document.color);
var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%";
var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
+ let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.props.Document) : undefined;
+ let showTitle = showOverlays && showOverlays.title ? showOverlays.title : StrCast(this.props.Document.showTitle);
+ let showCaption = showOverlays && showOverlays.caption ? showOverlays.caption : StrCast(this.props.Document.showCaption);
+ let templates = Cast(this.props.Document.templates, listSpec("string"));
+ if (templates instanceof List) {
+ templates.map(str => {
+ if (str.indexOf("{props.Document.title}") !== -1) showTitle = "title";
+ if (str.indexOf("fieldKey={\"caption\"}") !== -1) showCaption = "caption";
+ });
+ }
+ let showTextTitle = showTitle && StrCast(this.props.Document.layout).startsWith("<FormattedTextBox") || (this.props.Document.layout instanceof Doc && StrCast(this.props.Document.layout.layout).startsWith("<FormattedTextBox")) ? showTitle : undefined;
return (
<div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
ref={this._mainCont}
@@ -614,7 +627,27 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}
>
- {this.contents}
+ {!showTitle && !showCaption ? this.contents :
+ <div style={{ position: "absolute", display: "inline-block", width: "100%", height: "100%", pointerEvents: "none" }}>
+ {!showTitle ? (null) :
+ <div style={{
+ position: showTextTitle ? "relative" : "absolute", top: 0, textAlign: "center", textOverflow: "ellipsis", whiteSpace: "pre",
+ overflow: "hidden", width: `${100 * this.props.ContentScaling()}%`, height: 25, background: "rgba(0, 0, 0, .4)", color: "white",
+ transformOrigin: "top left", transform: `scale(${1 / this.props.ContentScaling()})`
+ }}>
+ <span>{this.props.Document[showTitle]}</span>
+ </div>
+ }
+ {!showCaption ? (null) :
+ <div style={{ position: "absolute", bottom: 0, transformOrigin: "bottom left", width: `${100 * this.props.ContentScaling()}%`, transform: `scale(${1 / this.props.ContentScaling()})` }}>
+ <FormattedTextBox {...this.props} DataDoc={this.dataDoc} active={returnTrue} isSelected={this.isSelected} focus={emptyFunction} select={this.select} selectOnLoad={this.props.selectOnLoad} fieldExt={""} hideOnLeave={true} fieldKey={showCaption} />
+ </div>
+ }
+ <div style={{ width: "100%", height: showTextTitle ? "calc(100% - 25px)" : "100%", display: "inline-block", position: showTextTitle ? "relative" : "absolute" }}>
+ {this.contents}
+ </div>
+ </div>
+ }
</div>
);
}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 96ee6f200..fb59470da 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,6 +1,6 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faEdit, faSmile } from '@fortawesome/free-solid-svg-icons';
-import { action, IReactionDisposer, observable, reaction, runInAction, computed } from "mobx";
+import { action, IReactionDisposer, observable, reaction, runInAction, computed, trace } from "mobx";
import { observer } from "mobx-react";
import { baseKeymap } from "prosemirror-commands";
import { history } from "prosemirror-history";
@@ -9,11 +9,11 @@ import { NodeType } from 'prosemirror-model';
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 { Id, Copy } from '../../../new_fields/FieldSymbols';
import { List } from '../../../new_fields/List';
import { RichTextField } from "../../../new_fields/RichTextField";
import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
+import { BoolCast, Cast, NumCast, StrCast, DateCast } from "../../../new_fields/Types";
import { DocServer } from "../../DocServer";
import { Docs } from '../../documents/Documents';
import { DocumentManager } from '../../util/DocumentManager';
@@ -33,6 +33,8 @@ import { Templates } from '../Templates';
import { FieldView, FieldViewProps } from "./FieldView";
import "./FormattedTextBox.scss";
import React = require("react");
+import { DateField } from '../../../new_fields/DateField';
+import { thisExpression } from 'babel-types';
library.add(faEdit);
library.add(faSmile);
@@ -68,6 +70,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
private _applyingChange: boolean = false;
private _linkClicked = "";
private _reactionDisposer: Opt<IReactionDisposer>;
+ private _textReactionDisposer: Opt<IReactionDisposer>;
private _proxyReactionDisposer: Opt<IReactionDisposer>;
private dropDisposer?: DragManager.DragDropDisposer;
public get CurrentDiv(): HTMLDivElement { return this._ref.current!; }
@@ -121,22 +124,24 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
}
- @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; }
+ @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(this.dataDoc, this.props.fieldKey, "dummy"); }
+
+ @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
dispatchTransaction = (tx: Transaction) => {
if (this._editorView) {
const state = this._editorView.state.apply(tx);
this._editorView.updateState(state);
this._applyingChange = true;
- Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new RichTextField(JSON.stringify(state.toJSON()));
- Doc.GetProto(this.dataDoc)[this.props.fieldKey + "_text"] = state.doc.textBetween(0, state.doc.content.size, "\n\n");
+ if (this.extensionDoc) this.extensionDoc.text = state.doc.textBetween(0, state.doc.content.size, "\n\n");
+ if (this.extensionDoc) this.extensionDoc.lastModified = new DateField(new Date(Date.now()));
+ this.dataDoc[this.props.fieldKey] = new RichTextField(JSON.stringify(state.toJSON()));
this._applyingChange = false;
let title = StrCast(this.dataDoc.title);
if (title && title.startsWith("-") && this._editorView) {
let str = this._editorView.state.doc.textContent;
let titlestr = str.substr(0, Math.min(40, str.length));
- let target = this.dataDoc.proto ? this.dataDoc.proto : this.dataDoc;
- target.title = "-" + titlestr + (str.length > 40 ? "..." : "");
+ this.dataDoc.title = "-" + titlestr + (str.length > 40 ? "..." : "");
}
}
}
@@ -226,9 +231,24 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
const field = this.dataDoc ? Cast(this.dataDoc[this.props.fieldKey], RichTextField) : undefined;
return field ? field.Data : `{"doc":{"type":"doc","content":[]},"selection":{"type":"text","anchor":0,"head":0}}`;
},
- field => this._editorView && !this._applyingChange &&
- this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field)))
+ field2 => {
+ if (StrCast(this.props.Document.layout).indexOf("\"" + this.props.fieldKey + "\"") !== -1) {// bcz: UGH! why is this needed... something is happening out of order. test with making a collection, then adding a text note and converting that to a template field.
+ this._editorView && !this._applyingChange &&
+ this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field2)));
+ }
+ }
);
+
+ this._textReactionDisposer = reaction(
+ () => this.extensionDoc,
+ () => {
+ if (this.dataDoc.text || this.dataDoc.lastModified) {
+ this.extensionDoc.text = this.dataDoc.text;
+ this.extensionDoc.lastModified = DateCast(this.dataDoc.lastModified)[Copy]();
+ this.dataDoc.text = undefined;
+ this.dataDoc.lastModified = undefined;
+ }
+ }, { fireImmediately: true });
this.setupEditor(config, this.dataDoc, this.props.fieldKey);
}
@@ -267,15 +287,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
componentWillUnmount() {
- if (this._editorView) {
- this._editorView.destroy();
- }
- if (this._reactionDisposer) {
- this._reactionDisposer();
- }
- if (this._proxyReactionDisposer) {
- this._proxyReactionDisposer();
- }
+ this._editorView && this._editorView.destroy();
+ this._reactionDisposer && this._reactionDisposer();
+ this._proxyReactionDisposer && this._proxyReactionDisposer();
+ this._textReactionDisposer && this._textReactionDisposer();
}
onPointerDown = (e: React.PointerEvent): void => {
@@ -392,8 +407,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (StrCast(this.dataDoc.title).startsWith("-") && this._editorView) {
let str = this._editorView.state.doc.textContent;
let titlestr = str.substr(0, Math.min(40, str.length));
- let target = this.dataDoc.proto ? this.dataDoc.proto : this.dataDoc;
- target.title = "-" + titlestr + (str.length > 40 ? "..." : "");
+ this.dataDoc.title = "-" + titlestr + (str.length > 40 ? "..." : "");
}
if (!this._undoTyping) {
this._undoTyping = UndoManager.StartBatch("undoTyping");
@@ -404,13 +418,14 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
@action
tryUpdateHeight() {
if (this.props.isOverlay && this.props.Document.autoHeight) {
+ let self = this;
let xf = this._ref.current!.getBoundingClientRect();
let scrBounds = this.props.ScreenToLocalTransform().transformBounds(0, 0, xf.width, xf.height);
let nh = NumCast(this.dataDoc.nativeHeight, 0);
let dh = NumCast(this.props.Document.height, 0);
let sh = scrBounds.height;
this.props.Document.height = nh ? dh / nh * sh : sh;
- this.dataDoc.proto!.nativeHeight = nh ? sh : undefined;
+ this.dataDoc.nativeHeight = nh ? sh : undefined;
}
}
@@ -436,6 +451,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
let style = this.props.isOverlay ? "scroll" : "hidden";
let rounded = StrCast(this.props.Document.borderRounding) === "100%" ? "-rounded" : "";
let interactive = InkingControl.Instance.selectedTool ? "" : "interactive";
+ Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
return (
<div className={`formattedTextBox-cont-${style}`} ref={this._ref}
style={{
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index f0363d0b8..77e44b807 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -86,6 +86,8 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
e.stopPropagation();
}
}
+ } else if (!this.props.isSelected()) {
+ e.stopPropagation();
}
}));
// de.data.removeDocument() bcz: need to implement
@@ -234,7 +236,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
let paths: string[] = ["http://www.cs.brown.edu/~bcz/noImage.png"];
// this._curSuffix = "";
// if (w > 20) {
- Doc.UpdateDocumentExtensionForField(this.extensionDoc, this.props.fieldKey);
+ Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
let alts = DocListCast(this.extensionDoc.Alternates);
let altpaths: string[] = alts.filter(doc => doc.data instanceof ImageField).map(doc => this.choosePath((doc.data as ImageField).url));
let field = this.dataDoc[this.props.fieldKey];
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index c40840227..eb8abf2c7 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -178,26 +178,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
let fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey);
let previousViewType = fieldTemplate.viewType;
-
- // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
- let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
- let layout = StrCast(fieldTemplate.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
- if (backgroundLayout) {
- layout = StrCast(fieldTemplate.layout).replace(/fieldKey={"annotations"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`);
- backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
- }
- let nw = NumCast(fieldTemplate.nativeWidth);
- let nh = NumCast(fieldTemplate.nativeHeight);
-
- fieldTemplate.title = metaKey;
- fieldTemplate.layout = layout;
- fieldTemplate.backgroundLayout = backgroundLayout;
- fieldTemplate.nativeWidth = nw;
- fieldTemplate.nativeHeight = nh;
- fieldTemplate.embed = true;
- fieldTemplate.isTemplate = true;
- fieldTemplate.templates = new List<string>([Templates.TitleBar(metaKey)]);
- fieldTemplate.proto = Doc.GetProto(parentStackingDoc);
+ Doc.MakeTemplate(fieldTemplate, metaKey, Doc.GetProto(parentStackingDoc));
previousViewType && (fieldTemplate.viewType = previousViewType);
Cast(parentStackingDoc.data, listSpec(Doc))!.push(fieldTemplate);
diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx
index 6896ee452..a16d7bc76 100644
--- a/src/client/views/presentationview/PresentationElement.tsx
+++ b/src/client/views/presentationview/PresentationElement.tsx
@@ -370,14 +370,14 @@ export default class PresentationElement extends React.Component<PresentationEle
className += " presentationView-selected";
}
let onEnter = (e: React.PointerEvent) => { p.document.libraryBrush = true; };
- let onLeave = (e: React.PointerEvent) => { p.document.libraryBrush = false; };
+ let onLeave = (e: React.PointerEvent) => { p.document.libraryBrush = undefined; };
return (
<div className={className} key={p.document[Id] + p.index}
onPointerEnter={onEnter} onPointerLeave={onLeave}
style={{
outlineColor: "maroon",
outlineStyle: "dashed",
- outlineWidth: BoolCast(p.document.libraryBrush, false) ? `1px` : "0px",
+ outlineWidth: BoolCast(p.document.libraryBrush) ? `1px` : "0px",
}}
onClick={e => { p.gotoDocument(p.index, NumCast(this.props.mainDocument.selectedDoc)); e.stopPropagation(); }}>
<strong className="presentationView-name">
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index 804ffa7f5..87cae5487 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -206,13 +206,13 @@ export class SearchItem extends React.Component<SearchItemProps> {
let doc1 = Cast(this.props.doc.anchor1, Doc, null);
let doc2 = Cast(this.props.doc.anchor2, Doc, null);
- doc1 && (doc1.libraryBrush = false);
- doc2 && (doc2.libraryBrush = false);
+ doc1 && (doc1.libraryBrush = undefined);
+ doc2 && (doc2.libraryBrush = undefined);
}
} else {
let docViews: DocumentView[] = DocumentManager.Instance.getAllDocumentViews(this.props.doc);
docViews.forEach(element => {
- element.props.Document.libraryBrush = false;
+ element.props.Document.libraryBrush = undefined;
});
}
}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 0fe3f76e3..c9937af87 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -3,7 +3,7 @@ import { serializable, primitive, map, alias, list } from "serializr";
import { autoObject, SerializationHelper, Deserializable } from "../client/util/SerializationHelper";
import { DocServer } from "../client/DocServer";
import { setter, getter, getField, updateFunction, deleteProperty, makeEditable, makeReadOnly } from "./util";
-import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast } from "./Types";
+import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast, BoolCast, StrCast } from "./Types";
import { listSpec } from "./Schema";
import { ObjectField } from "./ObjectField";
import { RefField, FieldId } from "./RefField";
@@ -332,10 +332,11 @@ export namespace Doc {
if (expandedTemplateLayout instanceof Doc) {
return expandedTemplateLayout;
}
- if (expandedTemplateLayout === undefined) {
+ if (expandedTemplateLayout === undefined && BoolCast(templateLayoutDoc.isTemplate)) {
setTimeout(() => {
templateLayoutDoc["_expanded_" + dataDoc[Id]] = Doc.MakeDelegate(templateLayoutDoc);
(templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).title = templateLayoutDoc.title + " applied to " + dataDoc.title;
+ (templateLayoutDoc["_expanded_" + dataDoc[Id]] as Doc).isExpandedTemplate = templateLayoutDoc;
}, 0);
}
return templateLayoutDoc;
@@ -373,4 +374,32 @@ export namespace Doc {
delegate.proto = doc;
return delegate;
}
+
+ export function MakeTemplate(fieldTemplate: Doc, metaKey: string, proto: Doc) {
+ // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
+ let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
+ let fieldLayoutDoc = fieldTemplate;
+ if (fieldTemplate.layout instanceof Doc) {
+ fieldLayoutDoc = Doc.MakeDelegate(fieldTemplate.layout);
+ }
+ let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
+ if (backgroundLayout) {
+ layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"annotations"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`);
+ backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
+ }
+ let nw = Cast(fieldTemplate.nativeWidth, "number");
+ let nh = Cast(fieldTemplate.nativeHeight, "number");
+
+ let layoutDelegate = fieldTemplate.layout instanceof Doc ? fieldLayoutDoc : fieldTemplate;
+ layoutDelegate.layout = layout;
+
+ fieldTemplate.title = metaKey;
+ fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout;
+ fieldTemplate.backgroundLayout = backgroundLayout;
+ fieldTemplate.nativeWidth = nw;
+ fieldTemplate.nativeHeight = nh;
+ fieldTemplate.isTemplate = true;
+ fieldTemplate.showTitle = "title";
+ fieldTemplate.proto = proto;
+ }
} \ No newline at end of file
diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts
index 39d384d64..f8a4a30b4 100644
--- a/src/new_fields/Types.ts
+++ b/src/new_fields/Types.ts
@@ -1,6 +1,7 @@
import { Field, Opt, FieldResult, Doc } from "./Doc";
import { List } from "./List";
import { RefField } from "./RefField";
+import { DateField } from "./DateField";
export type ToType<T extends InterfaceValue> =
T extends "string" ? string :
@@ -80,6 +81,9 @@ export function StrCast(field: FieldResult, defaultVal: string | null = "") {
export function BoolCast(field: FieldResult, defaultVal: boolean | null = null) {
return Cast(field, "boolean", defaultVal);
}
+export function DateCast(field: FieldResult) {
+ return Cast(field, DateField, null);
+}
type WithoutList<T extends Field> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts
index b5e50d501..47e467041 100644
--- a/src/new_fields/util.ts
+++ b/src/new_fields/util.ts
@@ -50,6 +50,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
target.__fields[prop] = value;
}
if (value === undefined) target[Update]({ '$unset': { ["fields." + prop]: "" } });
+ if (typeof value === "object" && !(value instanceof ObjectField)) debugger;
else target[Update]({ '$set': { ["fields." + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) : (value === undefined ? null : value) } });
UndoManager.AddEvent({
redo: () => receiver[prop] = value,
diff --git a/src/server/GarbageCollector.ts b/src/server/GarbageCollector.ts
index 268239481..59682e51e 100644
--- a/src/server/GarbageCollector.ts
+++ b/src/server/GarbageCollector.ts
@@ -94,8 +94,17 @@ async function GarbageCollect() {
const toDeleteCursor = await Database.Instance.query({ _id: { $nin: Array.from(visited) } }, { _id: 1 });
const toDelete: string[] = (await toDeleteCursor.toArray()).map(doc => doc._id);
toDeleteCursor.close();
- const result = await Database.Instance.delete({ _id: { $in: toDelete } }, "newDocuments");
- console.log(`${result.deletedCount} documents deleted`);
+ let i = 0;
+ let deleted = 0;
+ while (i < toDelete.length) {
+ const count = Math.min(toDelete.length, 5000);
+ const toDeleteDocs = toDelete.slice(i, i + count);
+ i += count;
+ const result = await Database.Instance.delete({ _id: { $in: toDeleteDocs } }, "newDocuments");
+ deleted += result.deletedCount || 0;
+ }
+ // const result = await Database.Instance.delete({ _id: { $in: toDelete } }, "newDocuments");
+ console.log(`${deleted} documents deleted`);
await Search.Instance.deleteDocuments(toDelete);
console.log("Cleared search documents");
diff --git a/src/server/Message.ts b/src/server/Message.ts
index e9a8b0f0c..19e0a48aa 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -45,4 +45,6 @@ export namespace MessageStore {
export const GetRefFields = new Message<string[]>("Get Ref Fields");
export const UpdateField = new Message<Diff>("Update Ref Field");
export const CreateField = new Message<Reference>("Create Ref Field");
+ export const DeleteField = new Message<string>("Delete field");
+ export const DeleteFields = new Message<string[]>("Delete fields");
}
diff --git a/src/server/index.ts b/src/server/index.ts
index 287ba6058..21adff9e5 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -25,7 +25,7 @@ import { getForgot, getLogin, getLogout, getReset, getSignup, postForgot, postLo
import { DashUserModel } from './authentication/models/user_model';
import { Client } from './Client';
import { Database } from './database';
-import { MessageStore, Transferable, Types, Diff } from "./Message";
+import { MessageStore, Transferable, Types, Diff, Message } from "./Message";
import { RouteStore } from './RouteStore';
const app = express();
const config = require('../../webpack.config');
@@ -453,7 +453,7 @@ let clients: Map = {};
server.on("connection", function (socket: Socket) {
console.log("a user has connected");
- Utils.emit(socket, MessageStore.Foo, "handshooken");
+ Utils.Emit(socket, MessageStore.Foo, "handshooken");
Utils.AddServerHandler(socket, MessageStore.Bar, barReceived);
Utils.AddServerHandler(socket, MessageStore.SetField, (args) => setField(socket, args));
@@ -465,6 +465,8 @@ server.on("connection", function (socket: Socket) {
Utils.AddServerHandler(socket, MessageStore.CreateField, CreateField);
Utils.AddServerHandler(socket, MessageStore.UpdateField, diff => UpdateField(socket, diff));
+ Utils.AddServerHandler(socket, MessageStore.DeleteField, id => DeleteField(socket, id));
+ Utils.AddServerHandler(socket, MessageStore.DeleteFields, ids => DeleteFields(socket, ids));
Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField);
Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields);
});
@@ -591,6 +593,23 @@ function UpdateField(socket: Socket, diff: Diff) {
}
}
+function DeleteField(socket: Socket, id: string) {
+ Database.Instance.delete({ _id: id }, "newDocuments").then(() => {
+ socket.broadcast.emit(MessageStore.DeleteField.Message, id);
+ });
+
+ Search.Instance.deleteDocuments([id]);
+}
+
+function DeleteFields(socket: Socket, ids: string[]) {
+ Database.Instance.delete({ _id: { $in: ids } }, "newDocuments").then(() => {
+ socket.broadcast.emit(MessageStore.DeleteFields.Message, ids);
+ });
+
+ Search.Instance.deleteDocuments(ids);
+
+}
+
function CreateField(newValue: any) {
Database.Instance.insert(newValue, "newDocuments");
}