diff options
-rw-r--r-- | src/client/util/SelectionManager.ts | 3 | ||||
-rw-r--r-- | src/client/util/SharingManager.tsx | 102 | ||||
-rw-r--r-- | src/client/views/GlobalKeyHandler.ts | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionTreeView.tsx | 15 | ||||
-rw-r--r-- | src/client/views/collections/TreeView.tsx | 10 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 8 | ||||
-rw-r--r-- | src/fields/Doc.ts | 7 | ||||
-rw-r--r-- | src/fields/util.ts | 12 |
8 files changed, 62 insertions, 97 deletions
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 7795a4a59..008ce281c 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -37,7 +37,6 @@ export namespace SelectionManager { manager.SelectedDocuments.clear(); manager.SelectedDocuments.set(docView, true); } - Doc.UserDoc().activeSelection = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document)); } @action DeselectDoc(docView: DocumentView): void { @@ -45,7 +44,6 @@ export namespace SelectionManager { if (manager.SelectedDocuments.get(docView)) { manager.SelectedDocuments.delete(docView); docView.props.whenActiveChanged(false); - Doc.UserDoc().activeSelection = new List(SelectionManager.SelectedDocuments().map(dv => dv.props.Document)); } } @action @@ -54,7 +52,6 @@ export namespace SelectionManager { manager.SelectedSchemaDocument = undefined; Array.from(manager.SelectedDocuments.keys()).map(dv => dv.props.whenActiveChanged(false)); manager.SelectedDocuments.clear(); - Doc.UserDoc().activeSelection = new List<Doc>([]); } } diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 7991022d2..d3500970d 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, observable, runInAction } from "mobx"; +import { action, observable, runInAction, computed } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; import Select from "react-select"; @@ -7,7 +7,7 @@ import * as RequestPromise from "request-promise"; import { AclAdmin, AclPrivate, DataSym, Doc, DocListCast, Opt, AclSym } from "../../fields/Doc"; import { List } from "../../fields/List"; import { Cast, StrCast } from "../../fields/Types"; -import { distributeAcls, GetEffectiveAcl, SharingPermissions } from "../../fields/util"; +import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx } from "../../fields/util"; import { Utils } from "../../Utils"; import { DocServer } from "../DocServer"; import { CollectionView } from "../views/collections/CollectionView"; @@ -148,7 +148,7 @@ export class SharingManager extends React.Component<{}> { * @param group * @param permission */ - setInternalGroupSharing = (group: Doc, permission: string, targetDoc?: Doc) => { + setInternalGroupSharing = (group: Doc | { groupName: string }, permission: string, targetDoc?: Doc) => { const target = targetDoc || this.targetDoc!; const key = StrCast(group.groupName).replace(".", "_"); @@ -160,7 +160,7 @@ export class SharingManager extends React.Component<{}> { doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`acl-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, doc); GetEffectiveAcl(doc) === AclAdmin && distributeAcls(acl, permission as SharingPermissions, doc); - if (key !== "Public") { + if (group instanceof Doc) { const members: string[] = JSON.parse(StrCast(group.members)); const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); @@ -393,39 +393,22 @@ export class SharingManager extends React.Component<{}> { /** * @returns the main interface of the SharingManager. */ - private get sharingInterface() { - + @computed get sharingInterface() { + TraceMobx(); const groupList = GroupManager.Instance?.getAllGroups() || []; - const sortedUsers = this.users.slice().sort(this.sortUsers) - .map(({ user: { email } }) => ({ label: email, value: indType + email })); - const sortedGroups = groupList.slice().sort(this.sortGroups) - .map(({ groupName }) => ({ label: StrCast(groupName), value: groupType + StrCast(groupName) })); + const sortedUsers = this.users.slice().sort(this.sortUsers).map(({ user: { email } }) => ({ label: email, value: indType + email })); + const sortedGroups = groupList.slice().sort(this.sortGroups).map(({ groupName }) => ({ label: StrCast(groupName), value: groupType + StrCast(groupName) })); // the next block handles the users shown (individuals/groups/both) const options: GroupedOptions[] = []; if (GroupManager.Instance) { if ((this.showUserOptions && this.showGroupOptions) || (!this.showUserOptions && !this.showGroupOptions)) { - options.push({ - label: 'Individuals', - options: sortedUsers - }, - { - label: 'Groups', - options: sortedGroups - }); - } - else if (this.showUserOptions) { - options.push({ - label: 'Individuals', - options: sortedUsers - }); - } - else { - options.push({ - label: 'Groups', - options: sortedGroups - }); + options.push( + { label: 'Individuals', options: sortedUsers }, + { label: 'Groups', options: sortedGroups }); } + else if (this.showUserOptions) options.push({ label: 'Individuals', options: sortedUsers }); + else options.push({ label: 'Groups', options: sortedGroups }); } const users = this.individualSort === "ascending" ? this.users.slice().sort(this.sortUsers) : this.individualSort === "descending" ? this.users.slice().sort(this.sortUsers).reverse() : this.users; @@ -510,12 +493,10 @@ export class SharingManager extends React.Component<{}> { ) : null ); - // dummy doc for public acl - const publicDoc = new Doc; - publicDoc.groupName = "Public"; + // the list of groups shared with - const groupListMap = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`acl-${StrCast(groupName).replace('.', '_')}`) : true); - groupListMap.unshift(publicDoc); + const groupListMap: (Doc | { groupName: string })[] = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`acl-${StrCast(groupName).replace('.', '_')}`) : true); + groupListMap.unshift({ groupName: "Public" }); const groupListContents = groupListMap.map(group => { const groupKey = `acl-${StrCast(group.groupName)}`; const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]); @@ -527,7 +508,7 @@ export class SharingManager extends React.Component<{}> { className={"container"} > <div className={"padding"}>{group.groupName}</div> - {group.groupName !== "Public" ? + {group instanceof Doc ? (<div className="group-info" onClick={action(() => GroupManager.Instance.currentGroup = group)}> <FontAwesomeIcon icon={"info-circle"} color={"#e8e8e8"} size={"sm"} style={{ backgroundColor: "#1e89d7", borderRadius: "100%", border: "1px solid #1e89d7" }} /> </div>) @@ -559,36 +540,6 @@ export class SharingManager extends React.Component<{}> { onCloseButtonClick={action(() => GroupManager.Instance.currentGroup = undefined)} /> : null} - {/* <p className={"share-link"}>Manage the public link to {this.focusOn("this document...")}</p> - {!this.linkVisible ? (null) : - <div className={"link-container"}> - <div className={"link-box"} onClick={this.copy}>{this.sharingUrl}</div> - <div - title={"Copy link to clipboard"} - className={"copy"} - style={{ backgroundColor: this.copied ? "lawngreen" : "gainsboro" }} - onClick={this.copy} - > - <FontAwesomeIcon icon={"copy"} /> - </div> - </div> - } - <div className={"people-with-container"}> - {!this.linkVisible ? (null) : <p className={"people-with"}>People with this link</p>} - <select - className={"people-with-select"} - value={this.sharingDoc ? StrCast(this.sharingDoc[PublicKey], SharingPermissions.None) : SharingPermissions.None} - style={{ - marginLeft: this.linkVisible ? 10 : 0, - color: this.sharingDoc ? ColorMapping.get(StrCast(this.sharingDoc[PublicKey], SharingPermissions.None)) : DefaultColor, - borderColor: this.sharingDoc ? ColorMapping.get(StrCast(this.sharingDoc[PublicKey], SharingPermissions.None)) : DefaultColor - }} - onChange={e => this.setExternalSharing(e.currentTarget.value)} - > - {this.sharingOptions} - </select> - </div> - <div className={"hr-substitute"} /> */} <div className="sharing-contents"> <p className={"share-title"}><b>Share </b>{this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, "this document") : "-multiple-")}</p> <div className={"close-button"} onClick={this.close}> @@ -660,16 +611,13 @@ export class SharingManager extends React.Component<{}> { } render() { - return ( - <MainViewModal - contents={this.sharingInterface} - isDisplayed={this.isOpen} - interactive={true} - dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} - overlayDisplayedOpacity={this.overlayOpacity} - closeOnExternalClick={this.close} - /> - ); + return <MainViewModal + contents={this.sharingInterface} + isDisplayed={this.isOpen} + interactive={true} + dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} + overlayDisplayedOpacity={this.overlayOpacity} + closeOnExternalClick={this.close} + />; } - }
\ No newline at end of file diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index ba9334c40..0c05a1b69 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -206,10 +206,12 @@ export class KeyManager { preventDefault = false; break; case "y": + SelectionManager.DeselectAll(); UndoManager.Redo(); stopPropagation = false; break; case "z": + SelectionManager.DeselectAll(); UndoManager.Undo(); stopPropagation = false; break; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index e61b9624a..efbe5581b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -24,6 +24,8 @@ import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import { TreeView } from "./TreeView"; import React = require("react"); +import { DocumentManager } from '../../util/DocumentManager'; +import { FormattedTextBoxComment } from '../nodes/formattedText/FormattedTextBoxComment'; export type collectionTreeViewProps = { treeViewHideTitle?: boolean; @@ -61,19 +63,26 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll this.treedropDisposer?.(); } - @action - remove = (doc: Doc | Doc[]): boolean => { + @undoBatch + remove = action((doc: Doc | Doc[]): boolean => { const docs = doc instanceof Doc ? [doc] : doc; const targetDataDoc = this.doc[DataSym]; const value = DocListCast(targetDataDoc[this.props.fieldKey]); const result = value.filter(v => !docs.includes(v)); SelectionManager.DeselectAll(); if (result.length !== value.length) { + const ind = targetDataDoc[this.props.fieldKey].indexOf(doc); targetDataDoc[this.props.fieldKey] = new List<Doc>(result); + if (ind > 0) { + const prev = targetDataDoc[this.props.fieldKey][ind - 1]; + FormattedTextBox.SelectOnLoad = prev[Id]; + const prevView = DocumentManager.Instance.getDocumentView(prev, this.props.CollectionView); + prevView?.select(false); + } return true; } return false; - } + }) @action addDoc = (doc: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => { const doAddDoc = (doc: Doc | Doc[]) => diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index e509eb78d..edb703135 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -121,12 +121,16 @@ export class TreeView extends React.Component<TreeViewProps> { } @undoBatch @action remove = (doc: Doc | Doc[], key: string) => { this.props.treeView.props.select(false); - return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true); + const ind = this.dataDoc[key].indexOf(doc); + const res = (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true); + res && ind > 0 && DocumentManager.Instance.getDocumentView(this.dataDoc[key][ind - 1], this.props.treeView.props.CollectionView)?.select(false); + return res; } @undoBatch @action removeDoc = (doc: Doc | Doc[]) => this.remove(doc, Doc.LayoutFieldKey(this.doc)); constructor(props: any) { super(props); + console.log("Ttile = " + this.props.document.title); const titleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" }); const openScript = ScriptField.MakeScript(`openOnRight(self)`); const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`); @@ -218,7 +222,6 @@ export class TreeView extends React.Component<TreeViewProps> { })} onClick={() => { SelectionManager.DeselectAll(); - Doc.UserDoc().activeSelection = new List([this.doc]); return false; }} OnEmpty={undoBatch(() => this.props.treeView.doc.treeViewOutlineMode && this.props.removeDoc?.(this.doc))} @@ -552,8 +555,9 @@ export class TreeView extends React.Component<TreeViewProps> { : <div className={`treeView-container${selected ? "-active" : ""}`} ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()} onKeyDown={e => { e.stopPropagation(); + e.preventDefault(); switch (e.key) { - case "Backspace": return this.doc.text && !(this.doc.text as RichTextField)?.Text && UndoManager.RunInBatch(() => this.props.removeDoc?.(this.doc), "delete"); + case "Backspace": return !(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc); case "Enter": return UndoManager.RunInBatch(() => this.makeTextCollection(), "bullet"); case "Tab": setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150); return UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.() : this.props.indentDocument?.(), "tab"); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 771b6bbbe..41969fd3e 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1162,6 +1162,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp FormattedTextBox.SelectOnLoadChar = ""; } else if (curText?.Text) { selectAll(this._editorView!.state, this._editorView?.dispatch); + this.startUndoTypingBatch(); } } @@ -1411,7 +1412,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } public startUndoTypingBatch() { - this._undoTyping = UndoManager.StartBatch("undoTyping"); + !this._undoTyping && (this._undoTyping = UndoManager.StartBatch("undoTyping")); } public endUndoTypingBatch() { @@ -1425,6 +1426,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp public static LiveTextUndo: UndoManager.Batch | undefined; public static HadSelection: boolean = false; onBlur = (e: any) => { + console.log("blur ", document.activeElement?.textContent); FormattedTextBox.HadSelection = window.getSelection()?.toString() !== ""; this.endUndoTypingBatch(); this.doLinkOnDeselect(); @@ -1473,9 +1475,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(mark)); } - if (!this._undoTyping) { - this.startUndoTypingBatch(); - } + this.startUndoTypingBatch(); } ondrop = (eve: React.DragEvent) => { diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index ba7c9c7da..470f9d4be 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -24,6 +24,7 @@ import { LinkManager } from "../client/util/LinkManager"; import JSZip = require("jszip"); import { saveAs } from "file-saver"; import { CollectionDockingView } from "../client/views/collections/CollectionDockingView"; +import { SelectionManager } from "../client/util/SelectionManager"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -1295,8 +1296,8 @@ Scripting.addGlobal(function docList(field: any) { return DocListCast(field); }) Scripting.addGlobal(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); }); Scripting.addGlobal(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); }); Scripting.addGlobal(function deiconifyView(doc: any) { Doc.deiconifyView(doc); }); -Scripting.addGlobal(function undo() { return UndoManager.Undo(); }); -Scripting.addGlobal(function redo() { return UndoManager.Redo(); }); +Scripting.addGlobal(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); }); +Scripting.addGlobal(function redo() { SelectionManager.DeselectAll(); return UndoManager.Redo(); }); Scripting.addGlobal(function DOC(id: string) { console.log("Can't parse a document id in a script"); return "invalid"; }); Scripting.addGlobal(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); }); Scripting.addGlobal(function docCast(doc: FieldResult): any { return DocCastAsync(doc); }); @@ -1305,7 +1306,7 @@ Scripting.addGlobal(function activePresentationItem() { return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)]; }); Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) { - const docs = DocListCast(Doc.UserDoc().activeSelection). + const docs = SelectionManager.SelectedDocuments().map(dv => dv.props.Document). filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCHOLDER && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null))); return docs.length ? new List(docs) : prevValue; diff --git a/src/fields/util.ts b/src/fields/util.ts index 9db79ced1..9e5890aa8 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -10,6 +10,8 @@ import { DocServer } from "../client/DocServer"; import { ComputedField } from "./ScriptField"; import { ScriptCast, StrCast } from "./Types"; import { returnZero } from "../Utils"; +import CursorField from "./CursorField"; +import { List } from "@material-ui/core"; function _readOnlySetter(): never { @@ -360,10 +362,12 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any const oldValue = current; const newValue = ObjectField.MakeCopy(value); current = newValue; - UndoManager.AddEvent({ - redo() { receiver[prop] = newValue; }, - undo() { receiver[prop] = oldValue; } - }); + if (!(value instanceof CursorField) && !(value?.some((v: any) => v instanceof CursorField))) { + UndoManager.AddEvent({ + redo() { receiver[prop] = newValue; }, + undo() { receiver[prop] = oldValue; } + }); + } } target[Update](diff); }; |