From d4accdd22cabdd78fc7bd76dd7ce3cb59e4b102f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 1 Aug 2019 00:31:50 -0400 Subject: more treeview cleanup and fixes. --- src/server/authentication/models/current_user_utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/server') diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 1c52a3f11..91d7ba87d 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -71,7 +71,7 @@ export class CurrentUserUtils { doc.sidebar = sidebar; } StrCast(doc.title).indexOf("@") !== -1 && (doc.title = StrCast(doc.title).split("@")[0] + "'s Library"); - + doc.width = 100; } public static loadCurrentUser() { -- cgit v1.2.3-70-g09d2 From 572c4196e0f41ec6bae8cae403812f9b97d5a3c7 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 6 Aug 2019 13:27:47 -0400 Subject: post factored out, buxton pivot viewer route --- src/Utils.ts | 11 +++++++++++ src/client/views/nodes/DocumentView.tsx | 1 + src/server/index.ts | 19 +++++++++++++++++++ 3 files changed, 31 insertions(+) (limited to 'src/server') diff --git a/src/Utils.ts b/src/Utils.ts index 502540eb0..c1737a084 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -3,6 +3,7 @@ import v5 = require("uuid/v5"); import { Socket } from 'socket.io'; import { Message } from './server/Message'; import { RouteStore } from './server/RouteStore'; +import requestPromise = require('request-promise'); export class Utils { @@ -173,4 +174,14 @@ export namespace JSONUtils { return results; } +} + +export function PostToServer(relativeRoute: string, body: any) { + let options = { + method: "POST", + uri: Utils.prepend(relativeRoute), + json: true, + body: body + }; + return requestPromise.post(options); } \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 547d858ce..dbbf95479 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -39,6 +39,7 @@ import { FormattedTextBox } from './FormattedTextBox'; import React = require("react"); import { DictationManager } from '../../util/DictationManager'; import { MainView } from '../MainView'; +import requestPromise = require('request-promise'); const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(fa.faTrash); diff --git a/src/server/index.ts b/src/server/index.ts index 10a84c823..1e811c1b2 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -148,6 +148,25 @@ app.get("/pull", (req, res) => res.redirect("/"); })); +app.get("/buxton/:clear", (req, res) => { + if (req.params.clear === "true") { + deleteFields().then(() => upload_buxton_docs(res)); + } else { + upload_buxton_docs(res); + } +}); + +let upload_buxton_docs = (res: Response) => { + let buxton_scraping = path.join(__dirname, '../scraping/buxton'); + exec('python scraper.py', { cwd: buxton_scraping }, (err, stdout, sterr) => { + if (err) { + res.send(err.message); + return; + } + res.redirect("/"); + }); +}; + app.get("/version", (req, res) => { exec('"C:\\Program Files\\Git\\bin\\git.exe" rev-parse HEAD', (err, stdout, stderr) => { if (err) { -- cgit v1.2.3-70-g09d2 From d8c065935a0cb5a89b6d76407b7729f8584424eb Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 6 Aug 2019 22:03:59 -0400 Subject: pivot view --- src/client/views/Main.tsx | 2 + src/client/views/collections/CollectionView.tsx | 1 + .../collectionFreeForm/CollectionFreeFormView.tsx | 32 +++++++++++++++ src/scraping/buxton/scraper.py | 15 +++---- src/scraping/buxton/scripts/initialization.txt | 46 ++++++++++++++++++++++ src/scraping/buxton/scripts/layout.txt | 1 + src/server/index.ts | 29 +++++++++----- src/server/remapUrl.ts | 2 + 8 files changed, 111 insertions(+), 17 deletions(-) create mode 100644 src/scraping/buxton/scripts/initialization.txt create mode 100644 src/scraping/buxton/scripts/layout.txt (limited to 'src/server') diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 1cf13aa74..daa778ed3 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -7,6 +7,7 @@ import { Cast } from "../../new_fields/Types"; import { Doc, DocListCastAsync } from "../../new_fields/Doc"; import { List } from "../../new_fields/List"; import { DocServer } from "../DocServer"; +import { PivotView } from "./collections/collectionFreeForm/CollectionFreeFormView"; let swapDocs = async () => { let oldDoc = await Cast(CurrentUserUtils.UserDocument.linkManagerDoc, Doc); @@ -33,6 +34,7 @@ let swapDocs = async () => { DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email); await Docs.Prototypes.initialize(); await CurrentUserUtils.loadUserDocument(info); + await PivotView.loadLayouts(); // updates old user documents to prevent chrome on tree view. (await Cast(CurrentUserUtils.UserDocument.workspaces, Doc))!.chromeStatus = "disabled"; (await Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc))!.chromeStatus = "disabled"; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index f59fee985..59572b7e7 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -97,6 +97,7 @@ export class CollectionView extends React.Component { switch (this.props.Document.viewType) { case CollectionViewType.Freeform: { subItems.push({ description: "Custom", icon: "fingerprint", event: CollectionFreeFormView.AddCustomLayout(this.props.Document, this.props.fieldKey) }); + subItems.push({ description: "Pivot", icon: "fingerprint", event: () => CollectionFreeFormView.SetPivotLayout(this.props.Document) }); break; } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 7ba13e961..17c4e83b0 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -37,6 +37,7 @@ import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import { DocumentType, Docs } from "../../../documents/Documents"; +import { RouteStore } from "../../../../server/RouteStore"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard); @@ -48,6 +49,20 @@ export const panZoomSchema = createSchema({ arrangeInit: ScriptField, }); +export namespace PivotView { + + export let arrangeInit: string; + export let arrangeScript: string; + + export async function loadLayouts() { + let response = await fetch(Utils.prepend("/layoutscripts")); + let scripts = JSON.parse(await response.text()); + arrangeInit = scripts[0]; + arrangeScript = scripts[1]; + } + +} + type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof positionSchema, typeof pageSchema]>; const PanZoomDocument = makeInterface(panZoomSchema, positionSchema, pageSchema); @@ -787,6 +802,23 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }; } + public static SetPivotLayout = (target: Doc) => { + let setSpecifiedLayoutField = (originalText: string, key: string, params: Record, requiredType?: string) => { + const script = CompileScript(originalText, { + params, + requiredType, + typecheck: false + }); + if (!script.compiled) { + console.log(script.errors.map(error => error.messageText).join("\n")); + return; + } + target[key] = new ScriptField(script); + }; + setSpecifiedLayoutField(PivotView.arrangeInit, "arrangeInit", { collection: "Doc", docs: "Doc[]" }, undefined); + setSpecifiedLayoutField(PivotView.arrangeScript, "arrangeScript", { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}"); + } + render() { const easing = () => this.props.Document.panTransformType === "Ease"; Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey); diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py index f0f45d8f9..1c19118fd 100644 --- a/src/scraping/buxton/scraper.py +++ b/src/scraping/buxton/scraper.py @@ -17,6 +17,7 @@ dist = "../../server/public/files" db = MongoClient("localhost", 27017)["Dash"] target_collection = db.newDocuments +target_doc_title = "Workspace 1" schema_guids = [] common_proto_id = "" @@ -69,7 +70,7 @@ def text_doc_map(string_list): return listify(proxify_guids(list(map(guid_map, string_list)))) -def write_schema(parse_results, display_fields, storage_key): +def write_collection(parse_results, display_fields, storage_key, viewType=2): view_guids = parse_results["child_guids"] data_doc = parse_results["schema"] @@ -90,7 +91,7 @@ def write_schema(parse_results, display_fields, storage_key): "zoomBasis": 1, "zIndex": 2, "libraryBrush": False, - "viewType": 2 + "viewType": viewType }, "__type": "Doc" } @@ -237,7 +238,7 @@ def parse_document(file_name: str): copyfile(dir_path + "/" + image, dir_path + "/" + image.replace(".", "_o.", 1)) copyfile(dir_path + "/" + image, dir_path + - "/" + image.replace(".", "_m.", 1)) + "/" + image.replace(".", "_m.", 1)) print(f"extracted {count} images...") def sanitize(line): return re.sub("[\n\t]+", "", line).replace(u"\u00A0", " ").replace( @@ -381,22 +382,22 @@ candidates = 0 for file_name in os.listdir(source): if file_name.endswith('.docx'): candidates += 1 - schema_guids.append(write_schema( + schema_guids.append(write_collection( parse_document(file_name), ["title", "data"], "image_data")) print("writing parent schema...") -parent_guid = write_schema({ +parent_guid = write_collection({ "schema": { "_id": guid(), "fields": {}, "__type": "Doc" }, "child_guids": schema_guids -}, ["title", "short_description", "original_price"], "data") +}, ["title", "short_description", "original_price"], "data", 1) print("appending parent schema to main workspace...\n") target_collection.update_one( - {"fields.title": "WS collection 1"}, + {"fields.title": target_doc_title}, {"$push": {"fields.data.fields": {"fieldId": parent_guid, "__type": "proxy"}}} ) diff --git a/src/scraping/buxton/scripts/initialization.txt b/src/scraping/buxton/scripts/initialization.txt new file mode 100644 index 000000000..53f3f0d53 --- /dev/null +++ b/src/scraping/buxton/scripts/initialization.txt @@ -0,0 +1,46 @@ +const field = collection.pivotField || "title"; +const width = collection.pivotWidth || 200; + +const groups = new Map; + +for (const doc of docs) { + const val = doc[field]; + if (val === undefined) continue; + + const l = groups.get(val); + if (l) { + l.push(doc); + } else { + groups.set(val, [doc]); + } + +} + +let minSize = Infinity; + +groups.forEach((val, key) => { + minSize = Math.min(minSize, val.length); +}); + +const numCols = collection.pivotNumColumns || Math.ceil(Math.sqrt(minSize)); + +const docMap = new Map; +const groupNames = []; + +let x = 0; +groups.forEach((val, key) => { + let y = 0; + let xCount = 0; + groupNames.push({type:"text", text:String(key), x, y:width + 50, width: width * 1.25 * numCols, height:100, fontSize:collection.pivotFontSize}); + for (const doc of val) { + docMap.set(doc, {x: x + xCount * width * 1.25, y:-y, width, height:width}); + xCount++; + if (xCount >= numCols) { + xCount = 0; + y += width * 1.25; + } + } + x += width * 1.25 * (numCols + 1); +}); + +return {state:{ map: docMap}, views:groupNames }; \ No newline at end of file diff --git a/src/scraping/buxton/scripts/layout.txt b/src/scraping/buxton/scripts/layout.txt new file mode 100644 index 000000000..46b6dbaac --- /dev/null +++ b/src/scraping/buxton/scripts/layout.txt @@ -0,0 +1 @@ +return state.map.get(doc) \ No newline at end of file diff --git a/src/server/index.ts b/src/server/index.ts index 1e811c1b2..d735a6d8c 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -148,24 +148,33 @@ app.get("/pull", (req, res) => res.redirect("/"); })); -app.get("/buxton/:clear", (req, res) => { - if (req.params.clear === "true") { - deleteFields().then(() => upload_buxton_docs(res)); - } else { - upload_buxton_docs(res); - } -}); - -let upload_buxton_docs = (res: Response) => { +app.get("/buxton", (req, res) => { let buxton_scraping = path.join(__dirname, '../scraping/buxton'); exec('python scraper.py', { cwd: buxton_scraping }, (err, stdout, sterr) => { if (err) { res.send(err.message); return; } + console.log(stdout); res.redirect("/"); }); -}; +}); + +app.get('/layoutscripts', (req, res) => { + let scripts: string[] = []; + let handler = (err: NodeJS.ErrnoException | null, data: Buffer) => { + if (err) { + console.log(err.message); + return; + } + scripts.push(data.toString()); + if (scripts.length === 2) { + res.send(JSON.stringify(scripts)); + } + }; + fs.readFile(path.join(__dirname, '../scraping/buxton/scripts/initialization.txt'), handler); + fs.readFile(path.join(__dirname, '../scraping/buxton/scripts/layout.txt'), handler); +}); app.get("/version", (req, res) => { exec('"C:\\Program Files\\Git\\bin\\git.exe" rev-parse HEAD', (err, stdout, stderr) => { diff --git a/src/server/remapUrl.ts b/src/server/remapUrl.ts index 69c766d56..5218a239a 100644 --- a/src/server/remapUrl.ts +++ b/src/server/remapUrl.ts @@ -2,6 +2,8 @@ import { Database } from "./database"; import { Search } from "./Search"; import * as path from 'path'; +//npx ts-node src/server/remapUrl.ts + const suffixMap: { [type: string]: true } = { "video": true, "pdf": true, -- cgit v1.2.3-70-g09d2 From 4d3962cf221cb2421835c7016562433077e7b200 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 7 Aug 2019 01:48:15 -0400 Subject: pivot view script retrieval and context menu behavior improvements --- src/client/views/collections/CollectionView.tsx | 14 ++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 12 ++--- src/scraping/buxton/source/Silverlight.dmg | Bin 0 -> 15097394 bytes src/server/index.ts | 52 ++++++++++++--------- 4 files changed, 44 insertions(+), 34 deletions(-) create mode 100644 src/scraping/buxton/source/Silverlight.dmg (limited to 'src/server') diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 59572b7e7..60ede17e7 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,6 +1,6 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faEye } from '@fortawesome/free-regular-svg-icons'; -import { faColumns, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons'; +import { faColumns, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree, faCopy } from '@fortawesome/free-solid-svg-icons'; import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from "mobx-react"; import * as React from 'react'; @@ -20,7 +20,7 @@ import { CollectionTreeView } from "./CollectionTreeView"; import { CollectionViewBaseChrome } from './CollectionViewChromes'; export const COLLECTION_BORDER_WIDTH = 2; -library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any); +library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy); @observer export class CollectionView extends React.Component { @@ -86,7 +86,13 @@ export class CollectionView extends React.Component { onContextMenu = (e: React.MouseEvent): void => { if (!this.isAnnotationOverlay && !e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 let subItems: ContextMenuProps[] = []; - subItems.push({ description: "Freeform", event: () => this.props.Document.viewType = CollectionViewType.Freeform, icon: "signature" }); + subItems.push({ + description: "Freeform", event: () => { + this.props.Document.viewType = CollectionViewType.Freeform; + delete this.props.Document.arrangeInit; + delete this.props.Document.arrangeScript; + }, icon: "signature" + }); if (CollectionBaseView.InSafeMode()) { ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" }); } @@ -97,7 +103,7 @@ export class CollectionView extends React.Component { switch (this.props.Document.viewType) { case CollectionViewType.Freeform: { subItems.push({ description: "Custom", icon: "fingerprint", event: CollectionFreeFormView.AddCustomLayout(this.props.Document, this.props.fieldKey) }); - subItems.push({ description: "Pivot", icon: "fingerprint", event: () => CollectionFreeFormView.SetPivotLayout(this.props.Document) }); + subItems.push({ description: "Pivot", icon: "copy", event: () => CollectionFreeFormView.SetPivotLayout(this.props.Document) }); break; } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 17c4e83b0..110ac2f25 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -51,14 +51,10 @@ export const panZoomSchema = createSchema({ export namespace PivotView { - export let arrangeInit: string; - export let arrangeScript: string; + export let scripts: { arrangeInit: string, arrangeScript: string }; export async function loadLayouts() { - let response = await fetch(Utils.prepend("/layoutscripts")); - let scripts = JSON.parse(await response.text()); - arrangeInit = scripts[0]; - arrangeScript = scripts[1]; + scripts = JSON.parse(await (await fetch(Utils.prepend("/layoutscripts"))).text()); } } @@ -815,8 +811,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } target[key] = new ScriptField(script); }; - setSpecifiedLayoutField(PivotView.arrangeInit, "arrangeInit", { collection: "Doc", docs: "Doc[]" }, undefined); - setSpecifiedLayoutField(PivotView.arrangeScript, "arrangeScript", { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}"); + setSpecifiedLayoutField(PivotView.scripts.arrangeInit, "arrangeInit", { collection: "Doc", docs: "Doc[]" }, undefined); + setSpecifiedLayoutField(PivotView.scripts.arrangeScript, "arrangeScript", { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}"); } render() { diff --git a/src/scraping/buxton/source/Silverlight.dmg b/src/scraping/buxton/source/Silverlight.dmg new file mode 100644 index 000000000..d88290362 Binary files /dev/null and b/src/scraping/buxton/source/Silverlight.dmg differ diff --git a/src/server/index.ts b/src/server/index.ts index d735a6d8c..a0101e3f8 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -1,6 +1,6 @@ require('dotenv').config(); import * as bodyParser from 'body-parser'; -import { exec } from 'child_process'; +import { exec, ExecOptions } from 'child_process'; import * as cookieParser from 'cookie-parser'; import * as express from 'express'; import * as session from 'express-session'; @@ -149,31 +149,39 @@ app.get("/pull", (req, res) => })); app.get("/buxton", (req, res) => { - let buxton_scraping = path.join(__dirname, '../scraping/buxton'); - exec('python scraper.py', { cwd: buxton_scraping }, (err, stdout, sterr) => { - if (err) { - res.send(err.message); - return; + let cwd = '../scraping/buxton'; + + let onResolved = (stdout: string) => { console.log(stdout); res.redirect("/"); }; + let onRejected = (err: any) => { console.error(err.message); res.send(err); }; + let tryPython3 = () => command_line('python3 scraper.py', cwd).then(onResolved, onRejected); + + command_line('python scraper.py', cwd).then(onResolved, tryPython3); +}); + +const command_line = (command: string, fromDirectory?: string) => { + return new Promise((resolve, reject) => { + let options: ExecOptions = {}; + if (fromDirectory) { + options.cwd = path.join(__dirname, fromDirectory); } - console.log(stdout); - res.redirect("/"); + exec(command, options, (err, stdout) => err ? reject(err) : resolve(stdout)); }); -}); +}; + +const read_text_file = (relativePath: string) => { + let target = path.join(__dirname, relativePath); + return new Promise((resolve, reject) => { + fs.readFile(target, (err, data) => err ? reject(err) : resolve(data.toString())); + }); +}; app.get('/layoutscripts', (req, res) => { - let scripts: string[] = []; - let handler = (err: NodeJS.ErrnoException | null, data: Buffer) => { - if (err) { - console.log(err.message); - return; - } - scripts.push(data.toString()); - if (scripts.length === 2) { - res.send(JSON.stringify(scripts)); - } - }; - fs.readFile(path.join(__dirname, '../scraping/buxton/scripts/initialization.txt'), handler); - fs.readFile(path.join(__dirname, '../scraping/buxton/scripts/layout.txt'), handler); + let prefix = '../scraping/buxton/scripts/'; + read_text_file(prefix + 'initialization.txt').then(arrangeInit => { + read_text_file(prefix + 'layout.txt').then(arrangeScript => { + res.send(JSON.stringify({ arrangeInit, arrangeScript })); + }); + }); }); app.get("/version", (req, res) => { -- cgit v1.2.3-70-g09d2 From 35cb61f0d93983464b29152f159d09ea6bd4edf9 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 7 Aug 2019 21:37:14 -0400 Subject: Mostly have field level read only working --- src/client/DocServer.ts | 34 +++++++++++++++++++++++++++++++++- src/client/views/MainView.tsx | 8 ++++++++ src/client/views/nodes/WebBox.tsx | 3 ++- src/new_fields/Doc.ts | 38 +++++++++++++++++++++++++++++++------- src/new_fields/util.ts | 11 +++++++++-- src/server/GarbageCollector.ts | 2 +- 6 files changed, 84 insertions(+), 12 deletions(-) (limited to 'src/server') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 87a87be92..5af89cf49 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,6 +1,6 @@ import * as OpenSocket from 'socket.io-client'; import { MessageStore, Diff, YoutubeQueryTypes } from "./../server/Message"; -import { Opt } from '../new_fields/Doc'; +import { Opt, Doc } from '../new_fields/Doc'; import { Utils, emptyFunction } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; import { RefField } from '../new_fields/RefField'; @@ -26,6 +26,38 @@ export namespace DocServer { let GUID: string; // indicates whether or not a document is currently being udpated, and, if so, its id + export enum WriteMode { + Always = 0, + None = 1, + SameUser = 2, + } + + const fieldWriteModes: { [field: string]: WriteMode } = {}; + const docsWithUpdates: { [field: string]: Doc[] } = {}; + + export function setFieldWriteMode(field: string, writeMode: WriteMode) { + fieldWriteModes[field] = writeMode; + if (writeMode === WriteMode.Always) { + const docs = docsWithUpdates[field]; + if (docs) { + docs.forEach(doc => Doc.RunCachedUpdate(doc, field)); + delete docsWithUpdates[field]; + } + } + } + + export function getFieldWriteMode(field: string) { + return fieldWriteModes[field]; + } + + export function registerDocWithCachedUpdate(doc: Doc, field: string) { + let list = docsWithUpdates[field]; + if (!list) { + list = docsWithUpdates[field] = []; + } + list.push(doc); + } + export function init(protocol: string, hostname: string, port: number, identifier: string) { _cache = {}; GUID = identifier; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 53f589684..7bc31e961 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -422,6 +422,14 @@ export class MainView extends React.Component { )}
  • +