diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | src/client/views/collections/CollectionMenu.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 72 | ||||
-rw-r--r-- | src/fields/Doc.ts | 2 |
5 files changed, 44 insertions, 39 deletions
diff --git a/.gitignore b/.gitignore index 7d3a4d214..e94da5580 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ src/scraping/buxton/final/source/ src/scraping/buxton/final/json/ src/scraping/buxton/source/ src/server/public/files/ +src/server/stats/userLoginStats.csv src/scraping/acm/package-lock.json src/server/session_manager/logs/**/*.log *.crt diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 5b631676e..2154016bd 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -1329,7 +1329,7 @@ export class Collection3DCarouselViewChrome extends React.Component<CollectionVi return ( <div className="collection3DCarouselViewChrome-cont"> <div className="collection3DCarouselViewChrome-scrollSpeed-cont"> - {FormattedTextBox.Focused ? <RichTextMenu /> : null} + {/* {FormattedTextBox.Focused ? <RichTextMenu /> : null} */} <div className="collectionStackingViewChrome-scrollSpeed-label">AUTOSCROLL SPEED:</div> <div className="collection3DCarouselViewChrome-scrollSpeed"> <EditableView GetValue={() => StrCast(this.scrollSpeed)} oneLine SetValue={this.setValue} contents={this.scrollSpeed ? this.scrollSpeed : 1000} /> diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 7f1e15c2f..0dfd119d7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -1,6 +1,6 @@ import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, Field } from '../../../../fields/Doc'; +import { CssSym, Doc, Field } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; import { Cast, NumCast, StrCast } from '../../../../fields/Types'; @@ -35,10 +35,10 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo () => [ this.props.A.props.ScreenToLocalTransform(), Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.scrollTop, - Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?._highlights, + Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.[CssSym], this.props.B.props.ScreenToLocalTransform(), Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.scrollTop, - Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?._highlights, + Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.[CssSym], ], action(() => { this._start = Date.now(); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 5719ea83c..f5826ef95 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1,7 +1,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { isEqual } from 'lodash'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { baseKeymap, selectAll } from 'prosemirror-commands'; import { history } from 'prosemirror-history'; @@ -11,13 +11,13 @@ import { Fragment, Mark, Node, Slice } from 'prosemirror-model'; import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state'; import { EditorView } from 'prosemirror-view'; import { DateField } from '../../../../fields/DateField'; -import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, Doc, DocListCast, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from '../../../../fields/Doc'; +import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, CssSym, Doc, DocListCast, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { PrefetchProxy } from '../../../../fields/Proxy'; import { RichTextField } from '../../../../fields/RichTextField'; import { RichTextUtils } from '../../../../fields/RichTextUtils'; -import { ComputedField, ScriptField } from '../../../../fields/ScriptField'; +import { ComputedField } from '../../../../fields/ScriptField'; import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils'; @@ -29,6 +29,7 @@ import { DictationManager } from '../../../util/DictationManager'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { MakeTemplate } from '../../../util/DropConverter'; +import { IsFollowLinkScript } from '../../../util/LinkFollower'; import { LinkManager } from '../../../util/LinkManager'; import { SelectionManager } from '../../../util/SelectionManager'; import { SnappingManager } from '../../../util/SnappingManager'; @@ -64,7 +65,6 @@ import { schema } from './schema_rts'; import { SummaryView } from './SummaryView'; import applyDevTools = require('prosemirror-dev-tools'); import React = require('react'); -import { IsFollowLinkScript } from '../../../util/LinkFollower'; const translateGoogleApi = require('translate-google-api'); export const GoogleRef = 'googleDocId'; @@ -79,7 +79,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps public static blankState = () => EditorState.create(FormattedTextBox.Instance.config); public static Instance: FormattedTextBox; public static LiveTextUndo: UndoManager.Batch | undefined; - static _globalHighlights: string[] = ['Audio Tags', 'Text from Others', 'Todo Items', 'Important Items', 'Disagree Items', 'Ignore Items']; + static _globalHighlightsCache: string = ''; + static _globalHighlights = new ObservableSet<string>(['Audio Tags', 'Text from Others', 'Todo Items', 'Important Items', 'Disagree Items', 'Ignore Items']); static _highlightStyleSheet: any = addStyleSheet(); static _bulletStyleSheet: any = addStyleSheet(); static _userStyleSheet: any = addStyleSheet(); @@ -193,7 +194,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps constructor(props: any) { super(props); FormattedTextBox.Instance = this; - this.updateHighlights(); this._recordingStart = Date.now(); } @@ -605,45 +605,46 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps return ret; } - updateHighlights = () => { - const highlights = FormattedTextBox._globalHighlights; + updateHighlights = (highlights: string[]) => { + if (Array.from(highlights).join('') === FormattedTextBox._globalHighlightsCache) return; + setTimeout(() => (FormattedTextBox._globalHighlightsCache = Array.from(highlights).join(''))); clearStyleSheetRules(FormattedTextBox._userStyleSheet); - if (highlights.indexOf('Audio Tags') === -1) { + if (highlights.includes('Audio Tags')) { addStyleSheetRule(FormattedTextBox._userStyleSheet, 'audiotag', { display: 'none' }, ''); } - if (highlights.indexOf('Text from Others') !== -1) { + if (highlights.includes('Text from Others')) { addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-remote', { background: 'yellow' }); } - if (highlights.indexOf('My Text') !== -1) { + if (highlights.includes('My Text')) { addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-' + Doc.CurrentUserEmail.replace('.', '').replace('@', ''), { background: 'moccasin' }); } - if (highlights.indexOf('Todo Items') !== -1) { + if (highlights.includes('Todo Items')) { addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UT-todo', { outline: 'black solid 1px' }); } - if (highlights.indexOf('Important Items') !== -1) { + if (highlights.includes('Important Items')) { addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UT-important', { 'font-size': 'larger' }); } - if (highlights.indexOf('Bold Text') !== -1) { - addStyleSheetRule(FormattedTextBox._userStyleSheet, '.formattedTextBox-inner-selected .ProseMirror strong > span', { 'font-size': 'large' }, ''); - addStyleSheetRule(FormattedTextBox._userStyleSheet, '.formattedTextBox-inner-selected .ProseMirror :not(strong > span)', { 'font-size': '0px' }, ''); + if (highlights.includes('Bold Text')) { + addStyleSheetRule(FormattedTextBox._userStyleSheet, '.formattedTextBox-inner .ProseMirror strong > span', { 'font-size': 'large' }, ''); + addStyleSheetRule(FormattedTextBox._userStyleSheet, '.formattedTextBox-inner .ProseMirror :not(strong > span)', { 'font-size': '0px' }, ''); } - if (highlights.indexOf('Disagree Items') !== -1) { + if (highlights.includes('Disagree Items')) { addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UT-disagree', { 'text-decoration': 'line-through' }); } - if (highlights.indexOf('Ignore Items') !== -1) { + if (highlights.includes('Ignore Items')) { addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UT-ignore', { 'font-size': '1' }); } - if (highlights.indexOf('By Recent Minute') !== -1) { + if (highlights.includes('By Recent Minute')) { addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-' + Doc.CurrentUserEmail.replace('.', '').replace('@', ''), { opacity: '0.1' }); const min = Math.round(Date.now() / 1000 / 60); numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-min-' + (min - i), { opacity: ((10 - i - 1) / 10).toString() })); - setTimeout(this.updateHighlights); } - if (highlights.indexOf('By Recent Hour') !== -1) { + if (highlights.includes('By Recent Hour')) { addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-' + Doc.CurrentUserEmail.replace('.', '').replace('@', ''), { opacity: '0.1' }); const hr = Math.round(Date.now() / 1000 / 60 / 60); numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-hr-' + (hr - i), { opacity: ((10 - i - 1) / 10).toString() })); } + this.layoutDoc[CssSym] = this.layoutDoc[CssSym] + 1; // css changes happen outside of react/mobx. so we need to set a flag that will notify anyone intereted in layout changes triggered by css changes (eg., CollectionLinkView) }; @observable _showSidebar = false; @@ -781,18 +782,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps const expertHighlighting = [...noviceHighlighting, 'Important Items', 'Ignore Items', 'Disagree Items', 'By Recent Minute', 'By Recent Hour']; (Doc.noviceMode ? noviceHighlighting : expertHighlighting).forEach(option => highlighting.push({ - description: (FormattedTextBox._globalHighlights.indexOf(option) === -1 ? 'Highlight ' : 'Unhighlight ') + option, - event: () => { + description: (!FormattedTextBox._globalHighlights.has(option) ? 'Highlight ' : 'Unhighlight ') + option, + event: action(() => { e.stopPropagation(); - if (FormattedTextBox._globalHighlights.indexOf(option) === -1) { - FormattedTextBox._globalHighlights.push(option); + if (!FormattedTextBox._globalHighlights.has(option)) { + FormattedTextBox._globalHighlights.add(option); } else { - FormattedTextBox._globalHighlights.splice(FormattedTextBox._globalHighlights.indexOf(option), 1); + FormattedTextBox._globalHighlights.delete(option); } - runInAction(() => (this.layoutDoc._highlights = FormattedTextBox._globalHighlights.join(''))); - this.updateHighlights(); - }, - icon: FormattedTextBox._globalHighlights.indexOf(option) === -1 ? 'highlighter' : 'remove-format', + }), + icon: !FormattedTextBox._globalHighlights.has(option) ? 'highlighter' : 'remove-format', }) ); @@ -1031,6 +1030,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps () => this.autoHeight, autoHeight => autoHeight && this.tryUpdateScrollHeight() ); + this._disposers.highlights = reaction( + () => Array.from(FormattedTextBox._globalHighlights).slice(), + highlights => this.updateHighlights(highlights), + { fireImmediately: true } + ); this._disposers.width = reaction( () => this.props.PanelWidth(), width => this.tryUpdateScrollHeight() @@ -1115,7 +1119,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps this._disposers.selected = reaction( () => this.props.isSelected(), action(selected => { - this.layoutDoc._highlights = selected ? FormattedTextBox._globalHighlights.join('') : ''; + if (FormattedTextBox._globalHighlights.has('Bold Text')) { + this.layoutDoc[CssSym] = this.layoutDoc[CssSym] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed + } if (RichTextMenu.Instance?.view === this._editorView && !selected) { RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined); } @@ -1431,7 +1437,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } componentWillUnmount() { - FormattedTextBox.Focused === this && (FormattedTextBox.Focused = undefined); Object.values(this._disposers).forEach(disposer => disposer?.()); this.endUndoTypingBatch(); this.unhighlightSearchTerms(); @@ -1532,11 +1537,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps @action onFocused = (e: React.FocusEvent): void => { //applyDevTools.applyDevTools(this._editorView); - FormattedTextBox.Focused = this; this.ProseRef?.children[0] === e.nativeEvent.target && this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props); this.startUndoTypingBatch(); }; - @observable public static Focused: FormattedTextBox | undefined; onClick = (e: React.MouseEvent): void => { if (!this.props.isContentActive()) return; @@ -1633,7 +1636,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps tr && this._editorView.dispatch(tr); } } - FormattedTextBox.Focused === this && (FormattedTextBox.Focused = undefined); if (RichTextMenu.Instance?.view === this._editorView && !this.props.isSelected(true)) { RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined); } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 662792ff0..6c808c145 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -96,6 +96,7 @@ export const HighlightSym = Symbol('Highlight'); export const DataSym = Symbol('Data'); export const LayoutSym = Symbol('Layout'); export const FieldsSym = Symbol('Fields'); +export const CssSym = Symbol('Css'); export const AclSym = Symbol('Acl'); export const DirectLinksSym = Symbol('DirectLinks'); export const AclUnset = Symbol('AclUnset'); @@ -341,6 +342,7 @@ export class Doc extends RefField { @observable private ___fieldKeys: any = {}; /// all of the raw acl's that have been set on this document. Use GetEffectiveAcl to determine the actual ACL of the doc for editing @observable public [AclSym]: { [key: string]: symbol } = {}; + @observable public [CssSym]: number = 0; // incrementer denoting a change to CSS layout @observable public [DirectLinksSym]: Set<Doc> = new Set(); @observable public [AnimationSym]: Opt<Doc>; @observable public [HighlightSym]: boolean = false; |