From 60ceef0a3a8c11d85434a154e542424d34d9562c Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 13 Jul 2019 16:46:49 -0400 Subject: Refactored style in DocDecs and added metadata entry to DocDecs --- src/client/views/MetadataEntryMenu.tsx | 74 ++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/client/views/MetadataEntryMenu.tsx (limited to 'src/client/views/MetadataEntryMenu.tsx') diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx new file mode 100644 index 000000000..0dc7e0220 --- /dev/null +++ b/src/client/views/MetadataEntryMenu.tsx @@ -0,0 +1,74 @@ +import * as React from 'react'; +import "./MetadataEntryMenu.scss"; +import { observer } from 'mobx-react'; +import { observable, action } from 'mobx'; +import { KeyValueBox } from './nodes/KeyValueBox'; +import { Doc } from '../../new_fields/Doc'; + +export type DocLike = Doc | Doc[] | Promise | Promise; +export interface MetadataEntryProps { + docs: DocLike | (() => DocLike); + onError?: () => boolean; +} + +@observer +export class MetadataEntryMenu extends React.Component{ + @observable private _currentKey: string = ""; + @observable private _currentValue: string = ""; + + @action + onKeyChange = (e: React.ChangeEvent) => { + this._currentKey = e.target.value; + } + + @action + onValueChange = (e: React.ChangeEvent) => { + this._currentValue = e.target.value; + } + + onValueKeyDown = async (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + const script = KeyValueBox.CompileKVPScript(this._currentValue); + if (!script) return; + let doc = this.props.docs; + if (typeof doc === "function") { + doc = doc(); + } + doc = await doc; + let success: boolean; + if (doc instanceof Doc) { + success = KeyValueBox.ApplyKVPScript(doc, this._currentKey, script); + } else { + success = doc.every(d => KeyValueBox.ApplyKVPScript(d, this._currentKey, script)); + } + if (!success) { + if (this.props.onError) { + if (this.props.onError()) { + this.clearInputs(); + } + } else { + this.clearInputs(); + } + } else { + this.clearInputs(); + } + } + } + + @action + clearInputs = () => { + this._currentKey = ""; + this._currentValue = ""; + } + + render() { + return ( +
+ Key: + + Value: + +
+ ); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 8e45625865b20a9e25b120d6938d3ec31aa880e3 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 13 Jul 2019 20:12:27 -0400 Subject: Kinda added auto complete --- package.json | 2 ++ src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/MetadataEntryMenu.tsx | 49 ++++++++++++++++++++++++++++++-- 3 files changed, 50 insertions(+), 3 deletions(-) (limited to 'src/client/views/MetadataEntryMenu.tsx') diff --git a/package.json b/package.json index 6681c4c1c..22b3a6b21 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "@types/prosemirror-view": "^1.3.1", "@types/pug": "^2.0.4", "@types/react": "^16.8.7", + "@types/react-autosuggest": "^9.3.9", "@types/react-color": "^2.14.1", "@types/react-measure": "^2.0.4", "@types/react-table": "^6.7.22", @@ -169,6 +170,7 @@ "raw-loader": "^1.0.0", "react": "^16.8.4", "react-anime": "^2.2.0", + "react-autosuggest": "^9.4.3", "react-bootstrap": "^1.0.0-beta.5", "react-bootstrap-dropdown-menu": "^1.1.15", "react-color": "^2.17.0", diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 56fbd75a0..2cb3de50f 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -639,7 +639,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> return (
SelectionManager.SelectedDocuments().map(dv => dv.props.Document)} />}>{/* tfs: @bcz This might need to be the data document? */} + content={ SelectionManager.SelectedDocuments().map(dv => dv.props.Document)} suggestWithFunction />}>{/* tfs: @bcz This might need to be the data document? */}
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx index 0dc7e0220..0c8fd3909 100644 --- a/src/client/views/MetadataEntryMenu.tsx +++ b/src/client/views/MetadataEntryMenu.tsx @@ -1,20 +1,23 @@ import * as React from 'react'; import "./MetadataEntryMenu.scss"; import { observer } from 'mobx-react'; -import { observable, action } from 'mobx'; +import { observable, action, runInAction } from 'mobx'; import { KeyValueBox } from './nodes/KeyValueBox'; import { Doc } from '../../new_fields/Doc'; +import * as Autosuggest from 'react-autosuggest'; export type DocLike = Doc | Doc[] | Promise | Promise; export interface MetadataEntryProps { docs: DocLike | (() => DocLike); onError?: () => boolean; + suggestWithFunction?: boolean; } @observer export class MetadataEntryMenu extends React.Component{ @observable private _currentKey: string = ""; @observable private _currentValue: string = ""; + @observable private suggestions: string[] = []; @action onKeyChange = (e: React.ChangeEvent) => { @@ -61,11 +64,53 @@ export class MetadataEntryMenu extends React.Component{ this._currentValue = ""; } + getKeySuggestions = async (value: string): Promise => { + value = value.toLowerCase(); + let docs = this.props.docs; + if (typeof docs === "function") { + if (this.props.suggestWithFunction) { + docs = docs(); + } else { + return []; + } + } + docs = await docs; + if (docs instanceof Doc) { + return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value)); + } else { + const keys = new Set(); + docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key))); + return Array.from(keys).filter(key => key.toLowerCase().startsWith(value)); + } + } + getSuggestionValue = (suggestion: string) => suggestion; + + renderSuggestion = (suggestion: string) => { + return

{suggestion}

; + } + + onSuggestionFetch = async ({ value }: { value: string }) => { + const sugg = await this.getKeySuggestions(value); + runInAction(() => { + this.suggestions = sugg; + }); + } + + @action + onSuggestionClear = () => { + this.suggestions = []; + } + render() { return (
Key: - + Value:
-- cgit v1.2.3-70-g09d2 From 86bf6857e98743502fe6bc85e7de6807808c41c4 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sun, 14 Jul 2019 12:36:46 -0400 Subject: Fixed some of the auto suggest stuff --- src/client/views/MetadataEntryMenu.scss | 57 +++++++++++++++++++++++++++++++++ src/client/views/MetadataEntryMenu.tsx | 7 ++-- 2 files changed, 61 insertions(+), 3 deletions(-) (limited to 'src/client/views/MetadataEntryMenu.tsx') diff --git a/src/client/views/MetadataEntryMenu.scss b/src/client/views/MetadataEntryMenu.scss index 73e5b6a73..469843350 100644 --- a/src/client/views/MetadataEntryMenu.scss +++ b/src/client/views/MetadataEntryMenu.scss @@ -7,4 +7,61 @@ .metadataEntry-outerDiv { display: flex; width: 300px; +} + +.react-autosuggest__container { + position: relative; +} + +.react-autosuggest__input { + width: 240px; + height: 30px; + padding: 10px 20px; + font-family: Helvetica, sans-serif; + font-weight: 300; + font-size: 16px; + border: 1px solid #aaa; + border-radius: 4px; +} + +.react-autosuggest__input--focused { + outline: none; +} + +.react-autosuggest__input--open { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.react-autosuggest__suggestions-container { + display: none; +} + +.react-autosuggest__suggestions-container--open { + display: block; + position: fixed; + width: 280px; + border: 1px solid #aaa; + background-color: #fff; + font-family: Helvetica, sans-serif; + font-weight: 300; + font-size: 16px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + z-index: 2; +} + +.react-autosuggest__suggestions-list { + margin: 0; + padding: 0; + list-style-type: none; +} + +.react-autosuggest__suggestion { + cursor: pointer; + padding: 10px 20px; +} + +.react-autosuggest__suggestion--highlighted { + background-color: #ddd; } \ No newline at end of file diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx index 0c8fd3909..cb574aa96 100644 --- a/src/client/views/MetadataEntryMenu.tsx +++ b/src/client/views/MetadataEntryMenu.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import "./MetadataEntryMenu.scss"; import { observer } from 'mobx-react'; -import { observable, action, runInAction } from 'mobx'; +import { observable, action, runInAction, trace } from 'mobx'; import { KeyValueBox } from './nodes/KeyValueBox'; import { Doc } from '../../new_fields/Doc'; import * as Autosuggest from 'react-autosuggest'; @@ -20,8 +20,8 @@ export class MetadataEntryMenu extends React.Component{ @observable private suggestions: string[] = []; @action - onKeyChange = (e: React.ChangeEvent) => { - this._currentKey = e.target.value; + onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => { + this._currentKey = newValue; } @action @@ -102,6 +102,7 @@ export class MetadataEntryMenu extends React.Component{ } render() { + trace(); return (
Key: -- cgit v1.2.3-70-g09d2 From d09679619e7cf0fbae60e1f9220a6dbeb9de1bd7 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sun, 14 Jul 2019 14:35:50 -0400 Subject: Fixed metadata entry css --- src/client/views/MetadataEntryMenu.scss | 23 ++++++++++------------- src/client/views/MetadataEntryMenu.tsx | 11 +++++++++-- 2 files changed, 19 insertions(+), 15 deletions(-) (limited to 'src/client/views/MetadataEntryMenu.tsx') diff --git a/src/client/views/MetadataEntryMenu.scss b/src/client/views/MetadataEntryMenu.scss index 469843350..a6df3cd1e 100644 --- a/src/client/views/MetadataEntryMenu.scss +++ b/src/client/views/MetadataEntryMenu.scss @@ -1,9 +1,3 @@ -.metadataEntry-input { - width: 40%; - margin-left: 5px; - margin-right: 5px; -} - .metadataEntry-outerDiv { display: flex; width: 300px; @@ -13,15 +7,18 @@ position: relative; } +.react-autosuggest__container, +.metadataEntry-input { + width: 100%; + margin-left: 5px; + margin-right: 5px; +} + +.metadataEntry-input, .react-autosuggest__input { - width: 240px; - height: 30px; - padding: 10px 20px; - font-family: Helvetica, sans-serif; - font-weight: 300; - font-size: 16px; border: 1px solid #aaa; border-radius: 4px; + width: 100%; } .react-autosuggest__input--focused { @@ -40,7 +37,7 @@ .react-autosuggest__suggestions-container--open { display: block; position: fixed; - width: 280px; + width: 180px; border: 1px solid #aaa; background-color: #fff; font-family: Helvetica, sans-serif; diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx index cb574aa96..59de0e2b4 100644 --- a/src/client/views/MetadataEntryMenu.tsx +++ b/src/client/views/MetadataEntryMenu.tsx @@ -19,6 +19,8 @@ export class MetadataEntryMenu extends React.Component{ @observable private _currentValue: string = ""; @observable private suggestions: string[] = []; + private autosuggestRef = React.createRef(); + @action onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => { this._currentKey = newValue; @@ -62,6 +64,10 @@ export class MetadataEntryMenu extends React.Component{ clearInputs = () => { this._currentKey = ""; this._currentValue = ""; + if (this.autosuggestRef.current) { + const input: HTMLInputElement = (this.autosuggestRef.current as any).input; + input && input.focus(); + } } getKeySuggestions = async (value: string): Promise => { @@ -106,12 +112,13 @@ export class MetadataEntryMenu extends React.Component{ return (
Key: - + onSuggestionsClearRequested={this.onSuggestionClear} + ref={this.autosuggestRef} /> Value:
-- cgit v1.2.3-70-g09d2 From 7d9e29690956327d1ed9981cd2882d08b72b5c86 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sun, 14 Jul 2019 16:02:08 -0400 Subject: Added value preview to MetadataEntry --- src/client/views/MetadataEntryMenu.tsx | 46 +++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) (limited to 'src/client/views/MetadataEntryMenu.tsx') diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx index 59de0e2b4..08abb9887 100644 --- a/src/client/views/MetadataEntryMenu.tsx +++ b/src/client/views/MetadataEntryMenu.tsx @@ -3,7 +3,7 @@ import "./MetadataEntryMenu.scss"; import { observer } from 'mobx-react'; import { observable, action, runInAction, trace } from 'mobx'; import { KeyValueBox } from './nodes/KeyValueBox'; -import { Doc } from '../../new_fields/Doc'; +import { Doc, Field } from '../../new_fields/Doc'; import * as Autosuggest from 'react-autosuggest'; export type DocLike = Doc | Doc[] | Promise | Promise; @@ -18,17 +18,60 @@ export class MetadataEntryMenu extends React.Component{ @observable private _currentKey: string = ""; @observable private _currentValue: string = ""; @observable private suggestions: string[] = []; + private userModified = false; private autosuggestRef = React.createRef(); @action onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => { this._currentKey = newValue; + if (!this.userModified) { + this.previewValue(); + } + } + + previewValue = async () => { + let field: Field | undefined | null = null; + let onProto: boolean = false; + let value: string | undefined = undefined; + let docs = this.props.docs; + if (typeof docs === "function") { + if (this.props.suggestWithFunction) { + docs = docs(); + } else { + return; + } + } + docs = await docs; + if (docs instanceof Doc) { + await docs[this._currentKey]; + value = Field.toKeyValueString(docs, this._currentKey); + } else { + for (const doc of docs) { + const v = await doc[this._currentKey]; + onProto = onProto || !Object.keys(doc).includes(this._currentKey); + if (field === null) { + field = v; + } else if (v !== field) { + value = "multiple values"; + } + } + } + if (value === undefined) { + if (field !== null && field !== undefined) { + value = (onProto ? "" : "= ") + Field.toScriptString(field); + } else { + value = ""; + } + } + const s = value; + runInAction(() => this._currentValue = s); } @action onValueChange = (e: React.ChangeEvent) => { this._currentValue = e.target.value; + this.userModified = e.target.value.trim() !== ""; } onValueKeyDown = async (e: React.KeyboardEvent) => { @@ -64,6 +107,7 @@ export class MetadataEntryMenu extends React.Component{ clearInputs = () => { this._currentKey = ""; this._currentValue = ""; + this.userModified = false; if (this.autosuggestRef.current) { const input: HTMLInputElement = (this.autosuggestRef.current as any).input; input && input.focus(); -- cgit v1.2.3-70-g09d2 From e302a00b20ae0f44393548c7da27af60fd56c92b Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sun, 14 Jul 2019 18:49:42 -0400 Subject: Added whosOnline route to server --- src/client/DocServer.ts | 97 +++++++++++++--------- src/client/views/Main.tsx | 5 +- src/client/views/MetadataEntryMenu.tsx | 1 - src/debug/Repl.tsx | 6 +- src/debug/Viewer.tsx | 15 ++-- .../authentication/models/current_user_utils.ts | 17 ++-- src/server/index.ts | 45 +++++++++- 7 files changed, 128 insertions(+), 58 deletions(-) (limited to 'src/client/views/MetadataEntryMenu.tsx') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index d05793ea2..6737657c8 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -5,6 +5,7 @@ import { Utils, emptyFunction } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; import { RefField } from '../new_fields/RefField'; import { Id, HandleUpdate } from '../new_fields/FieldSymbols'; +import { CurrentUserUtils } from '../server/authentication/models/current_user_utils'; /** * This class encapsulates the transfer and cross-client synchronization of @@ -21,12 +22,31 @@ import { Id, HandleUpdate } from '../new_fields/FieldSymbols'; */ export namespace DocServer { let _cache: { [id: string]: RefField | Promise> } = {}; - const _socket = OpenSocket(`${window.location.protocol}//${window.location.hostname}:4321`); + let _socket: SocketIOClient.Socket; // this client's distinct GUID created at initialization - const GUID: string = Utils.GenerateGuid(); + let GUID: string; // indicates whether or not a document is currently being udpated, and, if so, its id let updatingId: string | undefined; + export function init(protocol: string, hostname: string, port: number, identifier: string) { + _cache = {}; + GUID = identifier; + _socket = OpenSocket(`${protocol}//${hostname}:${port}`); + + _GetRefField = _GetRefFieldImpl; + _GetRefFields = _GetRefFieldsImpl; + _CreateField = _CreateFieldImpl; + _UpdateField = _UpdateFieldImpl; + + /** + * Whenever the server sends us its handshake message on our + * websocket, we use the above function to return the handshake. + */ + Utils.AddServerHandler(_socket, MessageStore.Foo, onConnection); + Utils.AddServerHandler(_socket, MessageStore.UpdateField, respondToUpdate); + Utils.AddServerHandler(_socket, MessageStore.DeleteField, respondToDelete); + Utils.AddServerHandler(_socket, MessageStore.DeleteFields, respondToDelete); + } /** * A convenience method. Prepends the full path (i.e. http://localhost:1050) to the * requested extension @@ -36,6 +56,10 @@ export namespace DocServer { return window.location.origin + extension; } + function errorFunc(): never { + throw new Error("Can't use DocServer without calling init first"); + } + export namespace Control { let _isReadOnly = false; @@ -63,22 +87,16 @@ export namespace DocServer { } - export namespace Util { - - /** - * Whenever the server sends us its handshake message on our - * websocket, we use the above function to return the handshake. - */ - Utils.AddServerHandler(_socket, MessageStore.Foo, onConnection); + /** + * This function emits a message (with this client's + * unique GUID) to the server + * indicating that this client has connected + */ + function onConnection() { + _socket.emit(MessageStore.Bar.Message, GUID); + } - /** - * This function emits a message (with this client's - * unique GUID) to the server - * indicating that this client has connected - */ - function onConnection() { - _socket.emit(MessageStore.Bar.Message, GUID); - } + export namespace Util { /** * Emits a message to the server that wipes @@ -98,7 +116,7 @@ export namespace DocServer { * the server if the document has not been cached. * @param id the id of the requested document */ - export async function GetRefField(id: string): Promise> { + const _GetRefFieldImpl = (id: string): Promise> => { // an initial pass through the cache to determine whether the document needs to be fetched, // is already in the process of being fetched or already exists in the // cache @@ -139,8 +157,14 @@ export namespace DocServer { return cached; } else { // CACHED => great, let's just return the cached field we have - return cached; + return Promise.resolve(cached); } + }; + + let _GetRefField: (id: string) => Promise> = errorFunc; + + export function GetRefField(id: string): Promise> { + return _GetRefField(id); } /** @@ -149,7 +173,7 @@ export namespace DocServer { * the server if the document has not been cached. * @param ids the ids that map to the reqested documents */ - export async function GetRefFields(ids: string[]): Promise<{ [id: string]: Opt }> { + const _GetRefFieldsImpl = async (ids: string[]): Promise<{ [id: string]: Opt }> => { const requestedIds: string[] = []; const waitingIds: string[] = []; const promises: Promise>[] = []; @@ -245,16 +269,13 @@ export namespace DocServer { // argument to the caller's promise (i.e. GetRefFields(["_id1_", "_id2_", "_id3_"]).then(map => //do something with map...)) // or it is the direct return result if the promise is awaited (i.e. let fields = await GetRefFields(["_id1_", "_id2_", "_id3_"])). return map; - } + }; - function _UpdateFieldImpl(id: string, diff: any) { - if (id === updatingId) { - return; - } - Utils.Emit(_socket, MessageStore.UpdateField, { id, diff }); - } + let _GetRefFields: (ids: string[]) => Promise<{ [id: string]: Opt }> = errorFunc; - let _UpdateField = _UpdateFieldImpl; + export function GetRefFields(ids: string[]) { + return _GetRefFields(ids); + } // WRITE A NEW DOCUMENT TO THE SERVER @@ -274,7 +295,7 @@ export namespace DocServer { Utils.Emit(_socket, MessageStore.CreateField, initialState); } - let _CreateField = _CreateFieldImpl; + let _CreateField: (field: RefField) => void = errorFunc; // NOTIFY THE SERVER OF AN UPDATE TO A DOC'S STATE @@ -290,6 +311,15 @@ export namespace DocServer { _UpdateField(id, updatedState); } + function _UpdateFieldImpl(id: string, diff: any) { + if (id === updatingId) { + return; + } + Utils.Emit(_socket, MessageStore.UpdateField, { id, diff }); + } + + let _UpdateField: (id: string, diff: any) => void = errorFunc; + function _respondToUpdateImpl(diff: any) { const id = diff.id; // to be valid, the Diff object must reference @@ -355,13 +385,4 @@ export namespace DocServer { 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/views/Main.tsx b/src/client/views/Main.tsx index 589542806..80399e24b 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -6,6 +6,7 @@ import * as React from 'react'; import { Cast } from "../../new_fields/Types"; import { Doc, DocListCastAsync } from "../../new_fields/Doc"; import { List } from "../../new_fields/List"; +import { DocServer } from "../DocServer"; let swapDocs = async () => { let oldDoc = await Cast(CurrentUserUtils.UserDocument.linkManagerDoc, Doc); @@ -28,8 +29,10 @@ let swapDocs = async () => { } (async () => { + const info = await CurrentUserUtils.loadCurrentUser(); + DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email); await Docs.Prototypes.initialize(); - await CurrentUserUtils.loadCurrentUser(); + await CurrentUserUtils.loadUserDocument(info); await swapDocs(); ReactDOM.render(, document.getElementById('root')); })(); \ No newline at end of file diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx index 08abb9887..5ee661944 100644 --- a/src/client/views/MetadataEntryMenu.tsx +++ b/src/client/views/MetadataEntryMenu.tsx @@ -152,7 +152,6 @@ export class MetadataEntryMenu extends React.Component{ } render() { - trace(); return (
Key: diff --git a/src/debug/Repl.tsx b/src/debug/Repl.tsx index 91b711c79..4f4db13d2 100644 --- a/src/debug/Repl.tsx +++ b/src/debug/Repl.tsx @@ -6,6 +6,7 @@ import { CompileScript } from '../client/util/Scripting'; import { makeInterface } from '../new_fields/Schema'; import { ObjectField } from '../new_fields/ObjectField'; import { RefField } from '../new_fields/RefField'; +import { DocServer } from '../client/DocServer'; @observer class Repl extends React.Component { @@ -63,4 +64,7 @@ class Repl extends React.Component { } } -ReactDOM.render(, document.getElementById("root")); \ No newline at end of file +(async function () { + DocServer.init(window.location.protocol, window.location.hostname, 4321, "repl"); + ReactDOM.render(, document.getElementById("root")); +})(); \ No newline at end of file diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx index f48eb696c..2b3eed154 100644 --- a/src/debug/Viewer.tsx +++ b/src/debug/Viewer.tsx @@ -178,9 +178,12 @@ class Viewer extends React.Component { } } -ReactDOM.render(( -
- -
), - document.getElementById('root') -); \ No newline at end of file +(async function () { + await DocServer.init(window.location.protocol, window.location.hostname, 4321, "viewer"); + ReactDOM.render(( +
+ +
), + document.getElementById('root') + ); +})(); \ No newline at end of file diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 384c579de..e796ccb43 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -73,17 +73,21 @@ export class CurrentUserUtils { } - public static async loadCurrentUser(): Promise { - let userPromise = rp.get(DocServer.prepend(RouteStore.getCurrUser)).then(response => { + public static loadCurrentUser() { + return rp.get(DocServer.prepend(RouteStore.getCurrUser)).then(response => { if (response) { - let obj = JSON.parse(response); - CurrentUserUtils.curr_id = obj.id as string; - CurrentUserUtils.curr_email = obj.email as string; + const result: { id: string, email: string } = JSON.parse(response); + return result; } else { throw new Error("There should be a user! Why does Dash think there isn't one?"); } }); - let userDocPromise = await rp.get(DocServer.prepend(RouteStore.getUserDocumentId)).then(id => { + } + + public static async loadUserDocument({ id, email }: { id: string, email: string }) { + this.curr_id = id; + this.curr_email = email; + await rp.get(DocServer.prepend(RouteStore.getUserDocumentId)).then(id => { if (id) { return DocServer.GetRefField(id).then(async field => { if (field instanceof Doc) { @@ -108,7 +112,6 @@ export class CurrentUserUtils { } catch (e) { } - return Promise.all([userPromise, userDocPromise]); } /* Northstar catalog ... really just for testing so this should eventually go away */ diff --git a/src/server/index.ts b/src/server/index.ts index 21adff9e5..9cb43bf4e 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -149,6 +149,32 @@ app.get("/search", async (req, res) => { res.send(results); }); +function msToTime(duration: number) { + let milliseconds = Math.floor((duration % 1000) / 100), + seconds = Math.floor((duration / 1000) % 60), + minutes = Math.floor((duration / (1000 * 60)) % 60), + hours = Math.floor((duration / (1000 * 60 * 60)) % 24); + + let hoursS = (hours < 10) ? "0" + hours : hours; + let minutesS = (minutes < 10) ? "0" + minutes : minutes; + let secondsS = (seconds < 10) ? "0" + seconds : seconds; + + return hoursS + ":" + minutesS + ":" + secondsS + "." + milliseconds; +} + +app.get("/whosOnline", (req, res) => { + let users: any = { active: {}, inactive: {} }; + const now = Date.now(); + + for (const user in timeMap) { + const time = timeMap[user]; + const key = ((now - time) / 1000) < (60 * 5) ? "active" : "inactive"; + users[key][user] = `Last active ${msToTime(now - time)} ago`; + } + + res.send(users); +}); + app.get("/thumbnail/:filename", (req, res) => { let filename = req.params.filename; let noExt = filename.substring(0, filename.length - ".png".length); @@ -450,12 +476,21 @@ interface Map { } let clients: Map = {}; +let socketMap = new Map(); +let timeMap: { [id: string]: number } = {}; + server.on("connection", function (socket: Socket) { - console.log("a user has connected"); + socket.use((packet, next) => { + let id = socketMap.get(socket); + if (id) { + timeMap[id] = Date.now(); + } + next(); + }); Utils.Emit(socket, MessageStore.Foo, "handshooken"); - Utils.AddServerHandler(socket, MessageStore.Bar, barReceived); + Utils.AddServerHandler(socket, MessageStore.Bar, guid => barReceived(socket, guid)); Utils.AddServerHandler(socket, MessageStore.SetField, (args) => setField(socket, args)); Utils.AddServerHandlerCallback(socket, MessageStore.GetField, getField); Utils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields); @@ -485,8 +520,10 @@ async function deleteAll() { await Search.Instance.clear(); } -function barReceived(guid: String) { - clients[guid.toString()] = new Client(guid.toString()); +function barReceived(socket: SocketIO.Socket, guid: string) { + clients[guid] = new Client(guid.toString()); + console.log(`User ${guid} has connected`); + socketMap.set(socket, guid); } function getField([id, callback]: [string, (result?: Transferable) => void]) { -- cgit v1.2.3-70-g09d2 From 68c135f23c54cc44a84fb17223bc49e3b7bff1a8 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sun, 14 Jul 2019 19:20:03 -0400 Subject: Fixed a couple issues --- src/client/views/MetadataEntryMenu.scss | 2 ++ src/client/views/MetadataEntryMenu.tsx | 1 + .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src/client/views/MetadataEntryMenu.tsx') diff --git a/src/client/views/MetadataEntryMenu.scss b/src/client/views/MetadataEntryMenu.scss index a6df3cd1e..bcfc9a82d 100644 --- a/src/client/views/MetadataEntryMenu.scss +++ b/src/client/views/MetadataEntryMenu.scss @@ -37,6 +37,8 @@ .react-autosuggest__suggestions-container--open { display: block; position: fixed; + overflow-y: auto; + max-height: 400px; width: 180px; border: 1px solid #aaa; background-color: #fff; diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx index 5ee661944..bd5a307b3 100644 --- a/src/client/views/MetadataEntryMenu.tsx +++ b/src/client/views/MetadataEntryMenu.tsx @@ -158,6 +158,7 @@ export class MetadataEntryMenu extends React.Component{ { + // tslint:disable-next-line: no-unnecessary-callback-wrapper + let scriptingBox = overlayDisposer()} onSave={(text, onError) => { const script = CompileScript(text, { params, requiredType, -- cgit v1.2.3-70-g09d2