aboutsummaryrefslogtreecommitdiff
path: root/src/client/DocServer.ts
diff options
context:
space:
mode:
authorljungster <parkerljung@gmail.com>2022-08-09 11:52:07 -0500
committerljungster <parkerljung@gmail.com>2022-08-09 11:52:07 -0500
commitda3cb00f809a482a9fdf732f6a656fbc467cce27 (patch)
tree9eb1fd278bc71d080d71bbfb7e3aec482d35f439 /src/client/DocServer.ts
parent1638527259a072dfc2ab286bd27bbb1751e8434e (diff)
parent26670c8b9eb6e2fd981c3a0997bff5556b60504b (diff)
Merge branch 'parker' of https://github.com/brown-dash/Dash-Web into parker
Diffstat (limited to 'src/client/DocServer.ts')
-rw-r--r--src/client/DocServer.ts151
1 files changed, 70 insertions, 81 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index e498a7cca..5a34fcf11 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,27 +1,27 @@
-import * as io from 'socket.io-client';
-import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "./../server/Message";
-import { Opt, Doc, UpdatingFromServer, updateCachedAcls } from '../fields/Doc';
-import { Utils, emptyFunction } from '../Utils';
-import { SerializationHelper } from './util/SerializationHelper';
-import { RefField } from '../fields/RefField';
-import { Id, HandleUpdate, Parent } from '../fields/FieldSymbols';
-import { GestureOverlay } from './views/GestureOverlay';
-import MobileInkOverlay from '../mobile/MobileInkOverlay';
import { runInAction } from 'mobx';
+import * as rp from 'request-promise';
+import * as io from 'socket.io-client';
+import { Doc, Opt, UpdatingFromServer } from '../fields/Doc';
+import { HandleUpdate, Id, Parent } from '../fields/FieldSymbols';
import { ObjectField } from '../fields/ObjectField';
+import { RefField } from '../fields/RefField';
import { StrCast } from '../fields/Types';
-import * as rp from 'request-promise';
+import MobileInkOverlay from '../mobile/MobileInkOverlay';
+import { emptyFunction, Utils } from '../Utils';
+import { GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, YoutubeQueryTypes } from './../server/Message';
+import { SerializationHelper } from './util/SerializationHelper';
+import { GestureOverlay } from './views/GestureOverlay';
/**
* This class encapsulates the transfer and cross-client synchronization of
* data stored only in documents (RefFields). In the process, it also
* creates and maintains a cache of documents so that they can be accessed
* more efficiently. Currently, there is no cache eviction scheme in place.
- *
+ *
* NOTE: while this class is technically abstracted to work with any [RefField], because
* [Doc] instances are the only [RefField] we need / have implemented at the moment, the documentation
* will treat all data used here as [Doc]s
- *
+ *
* Any time we want to write a new field to the database (via the server)
* or update ourselves based on the server's update message, that occurs here
*/
@@ -32,12 +32,12 @@ export namespace DocServer {
const strings: string[] = [];
Array.from(Object.keys(_cache)).forEach(key => {
const doc = _cache[key];
- if (doc instanceof Doc) strings.push(StrCast(doc.author) + " " + StrCast(doc.title) + " " + StrCast(Doc.GetT(doc, "title", "string", true)));
+ if (doc instanceof Doc) strings.push(StrCast(doc.author) + ' ' + StrCast(doc.title) + ' ' + StrCast(Doc.GetT(doc, 'title', 'string', true)));
});
- print && strings.sort().forEach((str, i) => console.log(i.toString() + " " + str));
- rp.post(Utils.prepend("/setCacheDocumentIds"), {
+ print && strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
+ rp.post(Utils.prepend('/setCacheDocumentIds'), {
body: {
- cacheDocumentIds: Array.from(Object.keys(_cache)).join(";"),
+ cacheDocumentIds: Array.from(Object.keys(_cache)).join(';'),
},
json: true,
});
@@ -50,8 +50,8 @@ export namespace DocServer {
export enum WriteMode {
Default = 0, //Anything goes
Playground = 1, //Playground (write own/no read other updates)
- LiveReadonly = 2,//Live Readonly (no write/read others)
- LivePlayground = 3,//Live Playground (write own/read others)
+ LiveReadonly = 2, //Live Readonly (no write/read others)
+ LivePlayground = 3, //Live Playground (write own/read others)
}
const fieldWriteModes: { [field: string]: WriteMode } = {};
const docsWithUpdates: { [field: string]: Set<Doc> } = {};
@@ -62,7 +62,7 @@ export namespace DocServer {
livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.Playground));
}
export function IsPlaygroundField(field: string) {
- return DocServer.PlaygroundFields?.includes(field.replace(/^_/, ""));
+ return DocServer.PlaygroundFields?.includes(field.replace(/^_/, ''));
}
export function setFieldWriteMode(field: string, writeMode: WriteMode) {
@@ -77,13 +77,13 @@ export namespace DocServer {
}
export function getFieldWriteMode(field: string) {
- return fieldWriteModes[field] || WriteMode.Default;
+ return Doc.CurrentUserEmail === 'guest' ? WriteMode.LiveReadonly : fieldWriteModes[field] || WriteMode.Default;
}
export function registerDocWithCachedUpdate(doc: Doc, field: string, oldValue: any) {
let list = docsWithUpdates[field];
if (!list) {
- list = docsWithUpdates[field] = new Set;
+ list = docsWithUpdates[field] = new Set();
}
if (!list.has(doc)) {
Doc.AddCachedUpdate(doc, field, oldValue);
@@ -92,7 +92,6 @@ export namespace DocServer {
}
export namespace Mobile {
-
export function dispatchGesturePoints(content: GestureContent) {
Utils.Emit(_socket, MessageStore.GesturePoints, content);
}
@@ -111,17 +110,17 @@ export namespace DocServer {
}
}
- const instructions = "This page will automatically refresh after this alert is closed. Expect to reconnect after about 30 seconds.";
+ const instructions = 'This page will automatically refresh after this alert is closed. Expect to reconnect after about 30 seconds.';
function alertUser(connectionTerminationReason: string) {
switch (connectionTerminationReason) {
- case "crash":
+ case 'crash':
alert(`Dash has temporarily crashed. Administrators have been notified and the server is restarting itself. ${instructions}`);
break;
- case "temporary":
+ case 'temporary':
alert(`An administrator has chosen to restart the server. ${instructions}`);
break;
- case "exit":
- alert("An administrator has chosen to kill the server. Do not expect to reconnect until administrators start the server.");
+ case 'exit':
+ alert('An administrator has chosen to kill the server. Do not expect to reconnect until administrators start the server.');
break;
default:
console.log(`Received an unknown ConnectionTerminated message: ${connectionTerminationReason}`);
@@ -132,7 +131,7 @@ export namespace DocServer {
export function init(protocol: string, hostname: string, port: number, identifier: string) {
_cache = {};
GUID = identifier;
- protocol = protocol.startsWith("https") ? "wss" : "ws";
+ protocol = protocol.startsWith('https') ? 'wss' : 'ws';
_socket = io.connect(`${protocol}://${hostname}:${port}`);
// io.connect(`https://7f079dda.ngrok.io`);// if using ngrok, create a special address for the websocket
@@ -153,17 +152,17 @@ export namespace DocServer {
Utils.AddServerHandler(_socket, MessageStore.ConnectionTerminated, alertUser);
// mobile ink overlay socket events to communicate between mobile view and desktop view
- _socket.addEventListener("receiveGesturePoints", (content: GestureContent) => {
+ _socket.addEventListener('receiveGesturePoints', (content: GestureContent) => {
MobileInkOverlay.Instance.drawStroke(content);
});
- _socket.addEventListener("receiveOverlayTrigger", (content: MobileInkOverlayContent) => {
+ _socket.addEventListener('receiveOverlayTrigger', (content: MobileInkOverlayContent) => {
GestureOverlay.Instance.enableMobileInkOverlay(content);
MobileInkOverlay.Instance.initMobileInkOverlay(content);
});
- _socket.addEventListener("receiveUpdateOverlayPosition", (content: UpdateMobileInkOverlayPositionContent) => {
+ _socket.addEventListener('receiveUpdateOverlayPosition', (content: UpdateMobileInkOverlayPositionContent) => {
MobileInkOverlay.Instance.updatePosition(content);
});
- _socket.addEventListener("receiveMobileDocumentUpload", (content: MobileDocumentUploadContent) => {
+ _socket.addEventListener('receiveMobileDocumentUpload', (content: MobileDocumentUploadContent) => {
MobileInkOverlay.Instance.uploadDocument(content);
});
}
@@ -173,14 +172,13 @@ export namespace DocServer {
}
export namespace Control {
-
let _isReadOnly = false;
export function makeReadOnly() {
if (!_isReadOnly) {
_isReadOnly = true;
- _CreateField = field => _cache[field[Id]] = field;
+ _CreateField = field => (_cache[field[Id]] = field);
_UpdateField = emptyFunction;
- _RespondToUpdate = emptyFunction;
+ // _RespondToUpdate = emptyFunction; // bcz: option: don't clear RespondToUpdate to continue to receive updates as others change the DB
}
}
@@ -195,8 +193,9 @@ export namespace DocServer {
}
}
- export function isReadOnly() { return _isReadOnly; }
-
+ export function isReadOnly() {
+ return _isReadOnly;
+ }
}
/**
@@ -209,7 +208,6 @@ export namespace DocServer {
}
export namespace Util {
-
/**
* Emits a message to the server that wipes
* all documents in the database.
@@ -217,7 +215,6 @@ export namespace DocServer {
export function deleteDatabase() {
Utils.Emit(_socket, MessageStore.DeleteAll, {});
}
-
}
// RETRIEVE DOCS FROM SERVER
@@ -258,8 +255,7 @@ export namespace DocServer {
});
cached[UpdatingFromServer] = false;
return cached;
- }
- else if (field !== undefined) {
+ } else if (field !== undefined) {
_cache[id] = field;
} else {
delete _cache[id];
@@ -272,7 +268,7 @@ export namespace DocServer {
// here, indicate that the document associated with this id is currently
// being retrieved and cached
!force && (_cache[id] = deserializeField);
- return force ? cached as any : deserializeField;
+ return force ? (cached as any) : deserializeField;
} else if (cached instanceof Promise) {
// BEING RETRIEVED AND CACHED => some other caller previously (likely recently) called GetRefField(s),
// and requested the document I'm looking for. Shouldn't fetch again, just
@@ -316,7 +312,6 @@ export namespace DocServer {
Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.VideoDetails, videoIds: videoIds }, callBack);
}
-
/**
* Given a list of Doc GUIDs, this utility function will asynchronously attempt to each id's associated
* field, first looking in the RefField cache and then communicating with
@@ -364,40 +359,36 @@ export namespace DocServer {
const proms: Promise<void>[] = [];
runInAction(() => {
for (const field of fields) {
- if (field !== undefined && field !== null && !_cache[field.id]) {
+ const cached = _cache[field.id];
+ if (!cached) {
// deserialize
- const cached = _cache[field.id];
- if (!cached) {
- const prom = SerializationHelper.Deserialize(field).then(deserialized => {
- fieldMap[field.id] = deserialized;
-
- //overwrite or delete any promises (that we inserted as flags
- // to indicate that the field was in the process of being fetched). Now everything
- // should be an actual value within or entirely absent from the cache.
- if (deserialized !== undefined) {
- _cache[field.id] = deserialized;
- } else {
- delete _cache[field.id];
- }
- return deserialized;
- });
- // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache)
- // we set the value at the field's id to a promise that will resolve to the field.
- // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method).
- // The mapping in the .then call ensures that when other callers await these promises, they'll
- // get the resolved field
- _cache[field.id] = prom;
-
- // adds to a list of promises that will be awaited asynchronously
- proms.push(prom);
- } else if (cached instanceof Promise) {
- proms.push(cached as any);
- }
- } else if (_cache[field.id] instanceof Promise) {
- proms.push(_cache[field.id] as any);
- (_cache[field.id] as any).then((f: any) => fieldMap[field.id] = f);
+ const prom = SerializationHelper.Deserialize(field).then(deserialized => {
+ fieldMap[field.id] = deserialized;
+
+ //overwrite or delete any promises (that we inserted as flags
+ // to indicate that the field was in the process of being fetched). Now everything
+ // should be an actual value within or entirely absent from the cache.
+ if (deserialized !== undefined) {
+ _cache[field.id] = deserialized;
+ } else {
+ delete _cache[field.id];
+ }
+ return deserialized;
+ });
+ // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache)
+ // we set the value at the field's id to a promise that will resolve to the field.
+ // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method).
+ // The mapping in the .then call ensures that when other callers await these promises, they'll
+ // get the resolved field
+ _cache[field.id] = prom;
+
+ // adds to a list of promises that will be awaited asynchronously
+ proms.push(prom);
+ } else if (cached instanceof Promise) {
+ proms.push(cached as any);
+ cached.then((f: any) => (fieldMap[field.id] = f));
} else if (field) {
- proms.push(_cache[field.id] as any);
+ proms.push(cached as any);
fieldMap[field.id] = DocServer.GetCachedRefField(field.id) || field;
}
}
@@ -417,20 +408,19 @@ export namespace DocServer {
const field = fields[id];
map[id] = field;
});
-
}
// 7) those promises we encountered in the else if of 1), which represent
// other callers having already submitted a request to the server for (a) document(s)
// in which we're interested, must still be awaited so that we can return the proper
- // values for those as well.
+ // values for those as well.
//
// fortunately, those other callers will also hit their own version of 6) and clean up
// the shared cache when these promises resolve, so all we have to do is...
const otherCallersFetching = await Promise.all(promises);
// ...extract the RefFields returned from the resolution of those promises and add them to our
// own map.
- waitingIds.forEach((id, index) => map[id] = otherCallersFetching[index]);
+ waitingIds.forEach((id, index) => (map[id] = otherCallersFetching[index]));
// now, we return our completed mapping from all of the ids that were passed into the method
// to their actual RefField | undefined values. This return value either becomes the input
@@ -480,7 +470,7 @@ export namespace DocServer {
}
function _UpdateFieldImpl(id: string, diff: any) {
- (!DocServer.Control.isReadOnly()) && Utils.Emit(_socket, MessageStore.UpdateField, { id, diff });
+ !DocServer.Control.isReadOnly() && Utils.Emit(_socket, MessageStore.UpdateField, { id, diff });
}
let _UpdateField: (id: string, diff: any) => void = errorFunc;
@@ -524,12 +514,11 @@ export namespace DocServer {
Utils.Emit(_socket, MessageStore.DeleteFields, ids);
}
-
function _respondToDeleteImpl(ids: string | string[]) {
function deleteId(id: string) {
delete _cache[id];
}
- if (typeof ids === "string") {
+ if (typeof ids === 'string') {
deleteId(ids);
} else if (Array.isArray(ids)) {
ids.map(deleteId);