From f885965824f76be69e6bd26109a5df85dc43eb80 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 26 Apr 2023 13:11:58 -0400
Subject: added stats button to mainview

---
 src/client/util/ServerStats.tsx | 55 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)
 create mode 100644 src/client/util/ServerStats.tsx

(limited to 'src/client/util/ServerStats.tsx')

diff --git a/src/client/util/ServerStats.tsx b/src/client/util/ServerStats.tsx
new file mode 100644
index 000000000..0ab411bff
--- /dev/null
+++ b/src/client/util/ServerStats.tsx
@@ -0,0 +1,55 @@
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { MainViewModal } from '../views/MainViewModal';
+import { DocumentView } from '../views/nodes/DocumentView';
+import './SharingManager.scss';
+
+@observer
+export class ServerStats extends React.Component<{}> {
+    public static Instance: ServerStats;
+    @observable private isOpen = false; // whether the SharingManager modal is open or not
+
+    // private get linkVisible() {
+    //     return this.targetDoc ? this.targetDoc["acl-" + PublicKey] !== SharingPermissions.None : false;
+    // }
+
+    @action
+    public open = async () => {
+        /**
+         * Populates the list of users.
+         */
+        fetch('/stats').then((res: Response) => res.text().then(action(stats => (this._stats = JSON.parse(stats)))));
+
+        this.isOpen = true;
+    };
+
+    public close = action(() => {
+        this.isOpen = false;
+    });
+
+    constructor(props: {}) {
+        super(props);
+        ServerStats.Instance = this;
+    }
+
+    @observable _stats: { [key: string]: any } | undefined;
+
+    /**
+     * @returns the main interface of the SharingManager.
+     */
+    @computed get sharingInterface() {
+        return (
+            <div>
+                <span>Active users:{this._stats?.socketMap.length}</span>
+                {this._stats?.socketMap.map((user: any) => (
+                    <p>{user.username}</p>
+                ))}
+            </div>
+        );
+    }
+
+    render() {
+        return <MainViewModal contents={this.sharingInterface} isDisplayed={this.isOpen} interactive={true} closeOnExternalClick={this.close} />;
+    }
+}
-- 
cgit v1.2.3-70-g09d2


From 1150296b546bb3628c3dac5704150dfb4295434b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 26 Apr 2023 16:03:49 -0400
Subject: added cheatsheet and shortcut for viewing text shortcuts. changed
 menu organization slightly for viewability.

---
 src/client/util/RTFMarkup.tsx                      | 137 +++++++++++++++++++++
 src/client/util/ServerStats.tsx                    |   3 +-
 src/client/views/MainView.tsx                      |   8 +-
 src/client/views/MainViewModal.tsx                 |  37 +++---
 src/client/views/nodes/DocumentView.tsx            |   6 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   2 +
 .../views/nodes/formattedText/RichTextRules.ts     |   7 ++
 7 files changed, 177 insertions(+), 23 deletions(-)
 create mode 100644 src/client/util/RTFMarkup.tsx

(limited to 'src/client/util/ServerStats.tsx')

diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx
new file mode 100644
index 000000000..306d151d7
--- /dev/null
+++ b/src/client/util/RTFMarkup.tsx
@@ -0,0 +1,137 @@
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { MainViewModal } from '../views/MainViewModal';
+
+@observer
+export class RTFMarkup extends React.Component<{}> {
+    static Instance: RTFMarkup;
+    @observable private isOpen = false; // whether the SharingManager modal is open or not
+
+    // private get linkVisible() {
+    //     return this.targetDoc ? this.targetDoc["acl-" + PublicKey] !== SharingPermissions.None : false;
+    // }
+
+    @action
+    public open = () => (this.isOpen = true);
+
+    @action
+    public close = () => (this.isOpen = false);
+
+    constructor(props: {}) {
+        super(props);
+        RTFMarkup.Instance = this;
+    }
+
+    @observable _stats: { [key: string]: any } | undefined;
+
+    /**
+     * @returns the main interface of the SharingManager.
+     */
+    @computed get cheatSheet() {
+        return (
+            <div style={{ textAlign: 'initial', height: '100%' }}>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`wiki:phrase`}</b>
+                    {` display wikipedia page for entered text (terminate with carriage return)`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`#tag `}</b>
+                    {`  add hashtag metadata to document. e.g, #idea`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`#, ## ... ###### `}</b>
+                    {`  set heading style based on number of '#'s between 1 and 6`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`#tag `}</b>
+                    {`  add hashtag metadata to document. e.g, #idea`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`>> `}</b>
+                    {`  add a sidebar text document inline`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`\`\` `}</b>
+                    {`  create a code snippet block`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`%% `}</b>
+                    {`  restore default styling`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`%color `}</b>
+                    {`  changes text color styling. e.g., %green.`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`%num `}</b>
+                    {`  set font size. e.g., %10 for 10pt font`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`%eq `}</b>
+                    {`  creates an equation block for typeset math`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`%alt `}</b>
+                    {`  switch between primary and alternate text (see bottom right Button for hover options).`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`%f `}</b>
+                    {`  create an inline footnote`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`%> `}</b>
+                    {`  create a bockquote section. Terminate with 2 carriage returns`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`%( `}</b>
+                    {`  start a section of inline elidable text. Terminate the inline text with %)`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`%q `}</b>
+                    {`  start a quoted block of text that’s indented on the left and right. Terminate with %q`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`%d `}</b>
+                    {`  start a block text where the first line is indented`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`%h `}</b>
+                    {`  start a block of text that begins with a hanging indent`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`%[ `}</b>
+                    {`  left justify text`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`%^ `}</b>
+                    {`  center text`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`%] `}</b>
+                    {`  right justify text`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`[:doctitle]] `}</b>
+                    {`  hyperlink to document specified by it’s title`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`[[fieldname]] `}</b>
+                    {`  display value of fieldname`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`[[fieldname=value]] `}</b>
+                    {`  assign value to fieldname of document and display it`}
+                </p>
+                <p>
+                    <b style={{ fontSize: 'larger' }}>{`[[fieldname:doctitle]] `}</b>
+                    {`  show value of fieldname from doc specified by it’s title`}
+                </p>
+            </div>
+        );
+    }
+
+    render() {
+        return <MainViewModal contents={this.cheatSheet} isDisplayed={this.isOpen} interactive={true} closeOnExternalClick={this.close} />;
+    }
+}
diff --git a/src/client/util/ServerStats.tsx b/src/client/util/ServerStats.tsx
index 0ab411bff..f84ad8598 100644
--- a/src/client/util/ServerStats.tsx
+++ b/src/client/util/ServerStats.tsx
@@ -1,8 +1,7 @@
-import { action, computed, observable, runInAction } from 'mobx';
+import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { MainViewModal } from '../views/MainViewModal';
-import { DocumentView } from '../views/nodes/DocumentView';
 import './SharingManager.scss';
 
 @observer
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 43f56dd8c..5af9a9f9a 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -14,7 +14,7 @@ import { StrCast } from '../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
-import { Docs, DocUtils } from '../documents/Documents';
+import { Docs } from '../documents/Documents';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { CaptureManager } from '../util/CaptureManager';
 import { DocumentManager } from '../util/DocumentManager';
@@ -22,11 +22,12 @@ import { GroupManager } from '../util/GroupManager';
 import { HistoryUtil } from '../util/History';
 import { Hypothesis } from '../util/HypothesisUtils';
 import { ReportManager } from '../util/ReportManager';
+import { RTFMarkup } from '../util/RTFMarkup';
 import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { SelectionManager } from '../util/SelectionManager';
+import { ServerStats } from '../util/ServerStats';
 import { ColorScheme, SettingsManager } from '../util/SettingsManager';
 import { SharingManager } from '../util/SharingManager';
-import { ServerStats } from '../util/ServerStats';
 import { SnappingManager } from '../util/SnappingManager';
 import { Transform } from '../util/Transform';
 import { TimelineMenu } from './animationtimeline/TimelineMenu';
@@ -41,7 +42,7 @@ import { DashboardView } from './DashboardView';
 import { DictationOverlay } from './DictationOverlay';
 import { DocumentDecorations } from './DocumentDecorations';
 import { GestureOverlay } from './GestureOverlay';
-import { TOPBAR_HEIGHT, LEFT_MENU_WIDTH } from './global/globalCssVariables.scss';
+import { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } from './global/globalCssVariables.scss';
 import { Colors } from './global/globalEnums';
 import { KeyManager } from './GlobalKeyHandler';
 import { InkTranscription } from './InkTranscription';
@@ -971,6 +972,7 @@ export class MainView extends React.Component {
                 <DictationOverlay />
                 <SharingManager />
                 <ServerStats />
+                <RTFMarkup />
                 <SettingsManager />
                 <ReportManager />
                 <CaptureManager />
diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx
index 55dee005d..32997a944 100644
--- a/src/client/views/MainViewModal.tsx
+++ b/src/client/views/MainViewModal.tsx
@@ -1,5 +1,5 @@
 import * as React from 'react';
-import "./MainViewModal.scss";
+import './MainViewModal.scss';
 import { observer } from 'mobx-react';
 
 export interface MainViewOverlayProps {
@@ -15,31 +15,38 @@ export interface MainViewOverlayProps {
 
 @observer
 export class MainViewModal extends React.Component<MainViewOverlayProps> {
-
     render() {
         const p = this.props;
         const dialogueOpacity = p.dialogueBoxDisplayedOpacity || 1;
         const overlayOpacity = p.overlayDisplayedOpacity || 0.4;
-        return !p.isDisplayed ? (null) : (
-            <div className="mainViewModal-cont" style={{
-                pointerEvents: p.isDisplayed && p.interactive ? "all" : "none"
-            }}>
-                <div className="dialogue-box" style={{
-                    borderColor: "black",
-                    ...(p.dialogueBoxStyle || {}),
-                    opacity: p.isDisplayed ? dialogueOpacity : 0
-                }} >
+        return !p.isDisplayed ? null : (
+            <div
+                className="mainViewModal-cont"
+                style={{
+                    pointerEvents: p.isDisplayed && p.interactive ? 'all' : 'none',
+                }}>
+                <div
+                    className="dialogue-box"
+                    style={{
+                        borderColor: 'black',
+                        height: 'max-content',
+                        overflow: 'auto',
+                        maxHeight: '80%',
+                        ...(p.dialogueBoxStyle || {}),
+                        opacity: p.isDisplayed ? dialogueOpacity : 0,
+                    }}>
                     {p.contents}
                 </div>
-                <div className="overlay"
+                <div
+                    className="overlay"
                     onClick={this.props?.closeOnExternalClick}
                     style={{
-                        backgroundColor: "black",
+                        backgroundColor: 'black',
                         ...(p.overlayStyle || {}),
-                        opacity: p.isDisplayed ? overlayOpacity : 0
+                        opacity: p.isDisplayed ? overlayOpacity : 0,
                     }}
                 />
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 38aa8db55..d58a4c199 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -767,6 +767,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const constantItems: ContextMenuProps[] = [];
 
         if (!Doc.IsSystem(this.rootDoc)) {
+            constantItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'layer-group' });
             constantItems.push({ description: 'Export as Zip file', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
             constantItems.push({ description: 'Import Zipped file', icon: 'upload', event: ({ x, y }) => this.importDocument() });
             (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
@@ -774,11 +775,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
                 constantItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' });
             }
-            cm.addItem({ description: 'General...', noexpand: false, subitems: constantItems, icon: 'question' });
+            cm.addItem({ description: 'General...', noexpand: true, subitems: constantItems, icon: 'question' });
         }
         const help = cm.findByDescription('Help...');
         const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : [];
-        helpItems.push({ description: 'Show Metadata', event: () => this.props.addDocTab(this.props.Document, (OpenWhere.addRight.toString() + 'KeyValue') as OpenWhere), icon: 'layer-group' });
         !Doc.noviceMode && helpItems.push({ description: 'Text Shortcuts Ctrl+/', event: () => this.props.addDocTab(Docs.Create.PdfDocument('/assets/cheat-sheet.pdf', { _width: 300, _height: 300 }), OpenWhere.addRight), icon: 'keyboard' });
         !Doc.noviceMode && helpItems.push({ description: 'Print Document in Console', event: () => console.log(this.props.Document), icon: 'hand-point-right' });
         !Doc.noviceMode && helpItems.push({ description: 'Print DataDoc in Console', event: () => console.log(this.props.Document[DataSym]), icon: 'hand-point-right' });
@@ -825,7 +825,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 icon: 'book',
             });
         }
-        if (!help) cm.addItem({ description: 'Help...', noexpand: true, subitems: helpItems, icon: 'question' });
+        if (!help) cm.addItem({ description: 'Help...', noexpand: !Doc.noviceMode, subitems: helpItems, icon: 'question' });
         else cm.moveAfter(help);
 
         e?.stopPropagation(); // DocumentViews should stop propagation of this event
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 399e6bfdf..0610d1e45 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -68,6 +68,7 @@ import { schema } from './schema_rts';
 import { SummaryView } from './SummaryView';
 import applyDevTools = require('prosemirror-dev-tools');
 import React = require('react');
+import { RTFMarkup } from '../../../util/RTFMarkup';
 const translateGoogleApi = require('translate-google-api');
 export const GoogleRef = 'googleDocId';
 type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
@@ -858,6 +859,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             icon: !this.Document._singleLine ? 'grip-lines' : 'bars',
         });
         optionItems.push({ description: `${this.Document._autoHeight ? 'Lock' : 'Auto'} Height`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: this.Document._autoHeight ? 'lock' : 'unlock' });
+        optionItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: 'text' });
         !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
         this._downX = this._downY = Number.NaN;
     };
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 599f96bca..20ce929ad 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -8,6 +8,7 @@ import { normalizeEmail } from '../../../../fields/util';
 import { Utils } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { Docs, DocUtils } from '../../../documents/Documents';
+import { RTFMarkup } from '../../../util/RTFMarkup';
 import { FormattedTextBox } from './FormattedTextBox';
 import { wrappingInputRule } from './prosemirrorPatches';
 import { RichTextMenu } from './RichTextMenu';
@@ -228,6 +229,12 @@ export class RichTextRules {
                 return null;
             }),
 
+            // show markup help
+            new InputRule(new RegExp(/%\?$/), (state, match, start, end) => {
+                setTimeout(RTFMarkup.Instance.open);
+                return state.tr.deleteRange(start, end);
+            }),
+
             // stop using active style
             new InputRule(new RegExp(/%alt$/), (state, match, start, end) => {
                 setTimeout(() => (this.Document[this.TextBox.props.fieldKey + '-usePath'] = this.Document[this.TextBox.props.fieldKey + '-usePath'] ? undefined : 'alternate'));
-- 
cgit v1.2.3-70-g09d2

