From 94137cb3a771ec6afd803f3cff97da86a14dd54f Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Fri, 26 Jun 2020 01:46:52 +0530 Subject: background colour + comments --- .../collectionGrid/CollectionGridView.scss | 3 +- .../collectionGrid/CollectionGridView.tsx | 41 +++++++++++++++++++++- 2 files changed, 41 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss index 9c2d5cbff..4d8473be9 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.scss +++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss @@ -8,7 +8,6 @@ .collectionGridView-gridContainer { height: 100%; overflow-y: auto; - background-color: white; overflow-x: hidden; display: flex; @@ -22,7 +21,7 @@ } .react-grid-layout { - width : 100%; + width: 100%; } .react-grid-item>.react-resizable-handle { diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index 2015ca930..72577e921 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -47,6 +47,9 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { @computed get flexGrid() { return BoolCast(this.props.Document.gridFlex, true); } // is grid static/flexible i.e. whether nodes be moved around and resized @computed get compaction() { return StrCast(this.props.Document.gridStartCompaction, StrCast(this.props.Document.gridCompaction, "vertical")); } // is grid static/flexible i.e. whether nodes be moved around and resized + /** + * Sets up the listeners for the list of documents and the reset button. + */ componentDidMount() { this._changeListenerDisposer = reaction(() => this.childLayoutPairs, (pairs) => { const newLayouts: Layout[] = []; @@ -68,11 +71,18 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { }); } + /** + * Disposes the listeners. + */ componentWillUnmount() { this._changeListenerDisposer?.(); this._resetListenerDisposer?.(); } + /** + * @returns the default location of the grid node (i.e. when the grid is static) + * @param index + */ unflexedPosition(index: number): Omit { return { x: (index % Math.floor(this.numCols / this.defaultW)) * this.defaultW, @@ -83,6 +93,9 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { }; } + /** + * Maps the x- and y- coordinates of the event to a grid cell. + */ screenToCell(sx: number, sy: number) { const pt = this.props.ScreenToLocalTransform().transformPoint(sx, sy); const x = Math.floor(pt[0] / this.colWidthPlusGap); @@ -90,10 +103,16 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { return { x, y }; } + /** + * Creates a layout object for a grid item + */ makeLayoutItem = (doc: Doc, pos: { x: number, y: number }, Static: boolean = false, w: number = this.defaultW, h: number = this.defaultH) => { return ({ i: doc[Id], w, h, x: pos.x, y: pos.y, static: Static }); } + /** + * Adds a layout to the list of layouts. + */ addLayoutItem = (layouts: Layout[], layout: Layout) => { const f = layouts.findIndex(l => l.i === layout.i); f !== -1 && layouts.splice(f, 1); @@ -215,6 +234,9 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { this.savedLayoutList.map((layout, index) => Object.assign(layout, this.unflexedPosition(index))); } + /** + * Handles internal drop of Dash documents. + */ @action onInternalDrop = (e: Event, de: DragManager.DropEvent) => { const savedLayouts = this.savedLayoutList; @@ -227,6 +249,16 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { return false; } + /** + * Handles external drop of images/PDFs etc from outside Dash. + */ + @action + onExternalDrop = async (e: React.DragEvent): Promise => { + const where = this.screenToCell(e.clientX, e.clientY); + super.onExternalDrop(e, { x: where.x, y: where.y }); + + } + /** * Handles the change in the value of the rowHeight slider. */ @@ -234,6 +266,9 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { onSliderChange = (event: React.ChangeEvent) => { this._rowHeight = event.currentTarget.valueAsNumber; } + /** + * Handles the user clicking on the slider. + */ @action onSliderDown = (e: React.PointerEvent) => { this._rowHeight = this.rowHeight; // uses _rowHeight during dragging and sets doc's rowHeight when finished so that operation is undoable @@ -253,6 +288,9 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { ContextMenu.Instance.addItem({ description: "Display", subitems: displayOptionsMenu, icon: "tv" }); } + /** + * Handles text document creation on double click. + */ onPointerDown = (e: React.PointerEvent) => { if (this.props.active(true)) { setupMoveUpEvents(this, e, returnFalse, returnFalse, @@ -276,8 +314,9 @@ export class CollectionGridView extends CollectionSubView(GridSchema) {
this.onPointerDown(e)} > + onPointerDown={e => this.onPointerDown(e)}>
e.stopPropagation()} onScroll={action(e => { if (!this.props.isSelected()) e.currentTarget.scrollTop = this._scroll; -- cgit v1.2.3-70-g09d2 From 4590467260a783e7d83adb20631c3b575d7e4cd3 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Tue, 30 Jun 2020 15:25:08 +0530 Subject: beginning sharing interface ui rework --- src/client/util/SharingManager.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 127ee33ce..903f6e23e 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -296,7 +296,7 @@ export default class SharingManager extends React.Component<{}> { onCloseButtonClick={action(() => this.groupToView = undefined)} /> : null} -

Manage the public link to {this.focusOn("this document...")}

+ {/*

Manage the public link to {this.focusOn("this document...")}

{!this.linkVisible ? (null) :
{this.sharingUrl}
@@ -325,10 +325,10 @@ export default class SharingManager extends React.Component<{}> { {this.sharingOptions}
-
+
*/}
-

Privately share {this.focusOn("this document")} with an individual...

+

Privately share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))} with an individual...

{/*200*/} {!existOtherUsers ? "There are no other users in your database." : this.users.map(({ user, notificationDoc }) => { // can't use async here @@ -366,7 +366,7 @@ export default class SharingManager extends React.Component<{}> {
-

Privately share {this.focusOn("this document")} with a group...

+

Privately share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))} with a group...

{/*200*/} {!existGroups ? "There are no groups in your database." : this.groups.map(group => { -- cgit v1.2.3-70-g09d2 From 467afe6346bf3188461721fa78eed0f9ac214da7 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Wed, 1 Jul 2020 16:55:51 +0530 Subject: ui changes + related functionality changes --- package-lock.json | 44 +++++-- src/client/util/GroupManager.tsx | 91 ++++++------- src/client/util/GroupMemberView.tsx | 4 +- src/client/util/SharingManager.scss | 122 +++++++++++------ src/client/util/SharingManager.tsx | 252 +++++++++++++++++++++++------------- 5 files changed, 321 insertions(+), 192 deletions(-) (limited to 'src') diff --git a/package-lock.json b/package-lock.json index f03a39df0..c4c623994 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2966,7 +2966,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2984,11 +2985,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3001,15 +3004,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3112,7 +3118,8 @@ }, "inherits": { "version": "2.0.4", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3122,6 +3129,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3134,17 +3142,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "1.2.5", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3161,6 +3172,7 @@ "mkdirp": { "version": "0.5.3", "bundled": true, + "optional": true, "requires": { "minimist": "^1.2.5" } @@ -3216,7 +3228,8 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "npm-packlist": { "version": "1.4.8", @@ -3241,7 +3254,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3251,6 +3265,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3319,7 +3334,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3349,6 +3365,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3366,6 +3383,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3404,11 +3422,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.1.1", - "bundled": true + "bundled": true, + "optional": true } } } diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 7c68fc2a0..e352d46a8 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -89,7 +89,8 @@ export default class GroupManager extends React.Component<{}> { /** * @returns a list of all group documents. */ - private getAllGroups(): Doc[] { + // private ? + getAllGroups(): Doc[] { const groupDoc = this.GroupManagerDoc; return groupDoc ? DocListCast(groupDoc.data) : []; } @@ -98,7 +99,8 @@ export default class GroupManager extends React.Component<{}> { * @returns a group document based on the group name. * @param groupName */ - private getGroup(groupName: string): Doc | undefined { + // private? + getGroup(groupName: string): Doc | undefined { const groupDoc = this.getAllGroups().find(group => group.groupName === groupName); return groupDoc; } @@ -172,8 +174,9 @@ export default class GroupManager extends React.Component<{}> { deleteGroup(group: Doc): boolean { if (group) { if (this.GroupManagerDoc && this.hasEditAccess(group)) { + // TODO look at this later + // SharingManager.Instance.setInternalGroupSharing(group, "Not Shared"); Doc.RemoveDocFromList(this.GroupManagerDoc, "data", group); - SharingManager.Instance.setInternalGroupSharing(group, "Not Shared"); if (group === this.currentGroup) { runInAction(() => this.currentGroup = undefined); } @@ -248,48 +251,48 @@ export default class GroupManager extends React.Component<{}> { /** * A getter that @returns the interface rendered to view an individual group. */ - private get editingInterface() { - const members: string[] = this.currentGroup ? JSON.parse(StrCast(this.currentGroup.members)) : []; - const options: UserOptions[] = this.currentGroup ? this.options.filter(option => !(JSON.parse(StrCast(this.currentGroup!.members)) as string[]).includes(option.value)) : []; - return (!this.currentGroup ? null : -
-
- {this.currentGroup.groupName} -
this.currentGroup = undefined)}> - -
+ // private get editingInterface() { + // const members: string[] = this.currentGroup ? JSON.parse(StrCast(this.currentGroup.members)) : []; + // const options: UserOptions[] = this.currentGroup ? this.options.filter(option => !(JSON.parse(StrCast(this.currentGroup!.members)) as string[]).includes(option.value)) : []; + // return (!this.currentGroup ? null : + //
+ //
+ // {this.currentGroup.groupName} + //
this.currentGroup = undefined)}> + // + //
- {this.hasEditAccess(this.currentGroup) ? -
-
- this.addMemberToGroup(this.currentGroup!, (selectedOption as UserOptions).value)} + // placeholder={"Add members"} + // value={null} + // closeMenuOnSelect={true} + // /> + //
+ // + //
: + // null} + //
+ //
+ // {members.map(member => ( + //
+ //
+ // {member} + //
+ // {this.hasEditAccess(this.currentGroup!) ? : null} + //
+ // ))} + //
+ //
+ // ); - } + // } /** * A getter that @returns the main interface for the GroupManager. @@ -307,7 +310,7 @@ export default class GroupManager extends React.Component<{}> { {this.currentGroup ? this.currentGroup = undefined} + onCloseButtonClick={action(() => this.currentGroup = undefined)} /> : null}
diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index b2d75158e..c844892b1 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -8,7 +8,7 @@ import { action } from "mobx"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import * as fa from '@fortawesome/free-solid-svg-icons'; import Select from "react-select"; -import { Doc, Opt } from "../../fields/Doc"; +import { Doc } from "../../fields/Doc"; import "./GroupMemberView.scss"; library.add(fa.faWindowClose); @@ -44,7 +44,7 @@ export default class GroupMemberView extends React.Component
- +
: null}
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index fcbc05f8a..209c41651 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -1,9 +1,9 @@ @import "../views/globalCssVariables"; .sharing-interface { - display: flex; - flex-direction: column; - width: 730px; + // display: flex; + // flex-direction: column; + width: 600px; .dialogue-box { width: 450; @@ -16,6 +16,66 @@ .sharing-contents { display: flex; + flex-direction: column; + + .share-setup { + display: flex; + margin-bottom: 20px; + align-items: center; + height: 36; + + .user-search { + width: 90%; + } + + .permissions-select { + z-index: 1; + margin-left: -100; + border: none; + outline: none; + } + + .share-button { + height: 100%; + margin-left: 3%; + } + } + + .main-container { + display: flex; + + + .individual-container, + .group-container { + width: 50%; + + .share-title { + margin-top: 20px; + margin-bottom: 20px; + } + + .groups-list, + .users-list { + font-style: italic; + background: white; + border: 1px solid black; + padding-left: 10px; + padding-right: 10px; + overflow-y: scroll; + overflow-x: hidden; + text-align: left; + display: flex; + align-content: center; + align-items: center; + text-align: center; + justify-content: center; + // color: red; + color: black; + height: 150px; + margin: 0 2; + } + } + } button { background: $darker-alt-accent; @@ -31,37 +91,6 @@ transition: transform 0.2s; height: 25; } - - .individual-container, - .group-container { - width: 50%; - - .share-groups, - .share-individual { - margin-top: 20px; - margin-bottom: 20px; - } - - .groups-list, - .users-list { - font-style: italic; - background: white; - border: 1px solid black; - padding-left: 10px; - padding-right: 10px; - overflow-y: scroll; - overflow-x: hidden; - text-align: left; - display: flex; - align-content: center; - align-items: center; - text-align: center; - justify-content: center; - color: red; - height: 150px; - margin: 0 2; - } - } } .focus-span { @@ -69,11 +98,12 @@ } p { - font-size: 15px; + font-size: 20px; text-align: left; - font-style: italic; - padding: 0; + // font-style: italic; + // padding: 0; margin: 0 0 20px 0; + color: black; } .hr-substitute { @@ -108,9 +138,9 @@ -moz-user-select: none; -ms-user-select: none; user-select: none; - width: 700px; - min-width: 700px; - max-width: 700px; + width: 100%; + // min-width: 700px; + // max-width: 700px; text-align: left; font-style: normal; font-size: 14; @@ -118,20 +148,28 @@ padding: 0; align-items: baseline; + &:hover .padding { + white-space: unset; + } + .padding { padding: 0 10px 0 0; color: black; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + max-width: 48%; } .permissions-dropdown { - outline: none; + border: none; height: 25; } .edit-actions { display: flex; position: absolute; - right: 51.5%; + right: -10; } } diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 903f6e23e..491abe1dc 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -9,7 +9,6 @@ import { Utils } from "../../Utils"; import "./SharingManager.scss"; import { Id } from "../../fields/FieldSymbols"; import { observer } from "mobx-react"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { library } from '@fortawesome/fontawesome-svg-core'; import * as fa from '@fortawesome/free-solid-svg-icons'; import { DocumentView } from "../views/nodes/DocumentView"; @@ -17,8 +16,9 @@ import { SelectionManager } from "./SelectionManager"; import { DocumentManager } from "./DocumentManager"; import { CollectionView } from "../views/collections/CollectionView"; import { DictationOverlay } from "../views/DictationOverlay"; -import GroupManager from "./GroupManager"; +import GroupManager, { UserOptions } from "./GroupManager"; import GroupMemberView from "./GroupMemberView"; +import Select from "react-select"; library.add(fa.faCopy); @@ -28,18 +28,18 @@ export interface User { } export enum SharingPermissions { - None = "Not Shared", - View = "Can View", + Edit = "Can Edit", Add = "Can Add", - Edit = "Can Edit" + View = "Can View", + None = "Not Shared" } -const ColorMapping = new Map([ - [SharingPermissions.None, "red"], - [SharingPermissions.View, "maroon"], - [SharingPermissions.Add, "blue"], - [SharingPermissions.Edit, "green"] -]); +// const ColorMapping = new Map([ +// [SharingPermissions.None, "red"], +// [SharingPermissions.View, "maroon"], +// [SharingPermissions.Add, "blue"], +// [SharingPermissions.Edit, "green"] +// ]); const HierarchyMapping = new Map([ [SharingPermissions.None, "0"], @@ -54,10 +54,18 @@ const HierarchyMapping = new Map([ ]); +interface GroupOptions { + label: string; + options: UserOptions[]; +} + const SharingKey = "sharingPermissions"; const PublicKey = "publicLinkPermissions"; const DefaultColor = "black"; +const groupType = "!groupType/"; +const indType = "!indType/"; + interface ValidatedUser { user: User; notificationDoc: Doc; @@ -70,17 +78,18 @@ export default class SharingManager extends React.Component<{}> { public static Instance: SharingManager; @observable private isOpen = false; @observable private users: ValidatedUser[] = []; - @observable private groups: Doc[] = []; + // @observable private groups: Doc[] = []; @observable private targetDoc: Doc | undefined; @observable private targetDocView: DocumentView | undefined; @observable private copied = false; @observable private dialogueBoxOpacity = 1; @observable private overlayOpacity = 0.4; - @observable private groupToView: Opt; + @observable private selectedUsers: UserOptions[] | null = null; + @observable private permissions: SharingPermissions = SharingPermissions.None; - private get linkVisible() { - return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; - } + // private get linkVisible() { + // return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; + // } public open = (target: DocumentView) => { SelectionManager.DeselectAll(); @@ -94,7 +103,7 @@ export default class SharingManager extends React.Component<{}> { } })); - runInAction(() => this.groups = GroupManager.Instance.getAllGroupsCopy()); + // runInAction(() => this.groups = GroupManager.Instance.getAllGroups()); } public close = action(() => { @@ -145,6 +154,7 @@ export default class SharingManager extends React.Component<{}> { const users: ValidatedUser[] = this.users.filter(user => members.includes(user.user.email)); const sharingDoc = this.sharingDoc!; + console.log(sharingDoc) if (permission === SharingPermissions.None) { const metadata = sharingDoc[StrCast(group.groupName)]; if (metadata) sharingDoc[StrCast(group.groupName)] = undefined; @@ -158,7 +168,7 @@ export default class SharingManager extends React.Component<{}> { }); } - setInternalSharing = async (recipient: ValidatedUser, state: string, group: Opt) => { + setInternalSharing = async (recipient: ValidatedUser, state: string, group?: Doc) => { const { user, notificationDoc } = recipient; const target = this.targetDoc!; const manager = this.sharingDoc!; @@ -207,13 +217,13 @@ export default class SharingManager extends React.Component<{}> { if (metadata) metadata.maxPermission = HierarchyMapping.get(`${max}`); } - private setExternalSharing = (state: string) => { - const sharingDoc = this.sharingDoc; - if (!sharingDoc) { - return; - } - sharingDoc[PublicKey] = state; - } + // private setExternalSharing = (state: string) => { + // const sharingDoc = this.sharingDoc; + // if (!sharingDoc) { + // return; + // } + // sharingDoc[PublicKey] = state; + // } private get sharingUrl() { if (!this.targetDoc) { @@ -282,18 +292,57 @@ export default class SharingManager extends React.Component<{}> { return StrCast(metadata instanceof Doc ? metadata.maxPermission : metadata, SharingPermissions.None); } + @action + handleUsersChange = (selectedOptions: any) => { + this.selectedUsers = selectedOptions as UserOptions[]; + } + + @action + handlePermissionsChange = (event: React.ChangeEvent) => { + this.permissions = event.currentTarget.value as SharingPermissions; + } + + @action + share = () => { + this.selectedUsers?.forEach(user => { + if (user.value.includes(indType)) { + console.log(user); + console.log(this.users.find(u => u.user.email === user.label)); + this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions); + } + else { + this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions); + } + }); + + this.selectedUsers = null; + } + private get sharingInterface() { const existOtherUsers = this.users.length > 0; - const existGroups = this.groups.length > 0; + const existGroups = GroupManager.Instance?.getAllGroups().length > 0; // const manager = this.sharingDoc!; + const options: GroupOptions[] = GroupManager.Instance ? + [ + { + label: 'Individuals', + options: GroupManager.Instance.options.map(({ label, value }) => ({ label, value: "!indType/" + value })) + }, + { + label: 'Groups', + options: GroupManager.Instance.getAllGroups().map(({ groupName }) => ({ label: StrCast(groupName), value: "!groupType/" + StrCast(groupName) })) + } + ] + : []; + return (
- {this.groupToView ? + {GroupManager.Instance?.currentGroup ? this.groupToView = undefined)} + group={GroupManager.Instance.currentGroup} + onCloseButtonClick={action(() => GroupManager.Instance.currentGroup = undefined)} /> : null} {/*

Manage the public link to {this.focusOn("this document...")}

@@ -327,76 +376,95 @@ export default class SharingManager extends React.Component<{}> {
*/}
-
-

Privately share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))} with an individual...

-
{/*200*/} - {!existOtherUsers ? "There are no other users in your database." : - this.users.map(({ user, notificationDoc }) => { // can't use async here - const userKey = user.userDocumentId; - const permissions = this.computePermissions(userKey); - const color = ColorMapping.get(permissions); - - // console.log(manager); - // const metadata = manager[userKey] as Doc; - // const usersShared = StrCast(metadata?.usersShared, ""); - // console.log(usersShared) - - - return ( -
- {user.email} - {/*
{usersShared}
*/} -
- +

Share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))}

+
+ + {this.sharingOptions} + + +
+
+
+
{/*200*/} + {!existOtherUsers ? "There are no other users in your database." : + this.users.map(({ user, notificationDoc }) => { // can't use async here + const userKey = user.userDocumentId; + const permissions = this.computePermissions(userKey); + // const color = ColorMapping.get(permissions); + + // console.log(manager); + // const metadata = manager[userKey] as Doc; + // const usersShared = StrCast(metadata?.usersShared, ""); + // console.log(usersShared) + + + return permissions === SharingPermissions.None ? null : ( +
+ {user.email} + {/*
{usersShared}
*/} +
+ +
-
- ); - }) - } + ); + }) + } +
-
-
-

Privately share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))} with a group...

-
{/*200*/} - {!existGroups ? "There are no groups in your database." : - this.groups.map(group => { - const permissions = this.computePermissions(StrCast(group.groupName)); - const color = ColorMapping.get(permissions); - return ( -
- {group.groupName} -
- - +
+
{/*200*/} + {!existGroups ? "There are no groups in your database." : + GroupManager.Instance.getAllGroups().map(group => { + const permissions = this.computePermissions(StrCast(group.groupName)); + // const color = ColorMapping.get(permissions); + return permissions === SharingPermissions.None ? null : ( +
+ {group.groupName} +
+ + +
-
- ); - }) + ); + }) - } + } +
+
Done
-- cgit v1.2.3-70-g09d2 From d7f2f6994ce6f2a450dff67b3595a692be9cb977 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Thu, 2 Jul 2020 17:23:31 +0530 Subject: restructuring and simplifying group implementation --- src/client/util/GroupManager.tsx | 34 +++++- src/client/util/SharingManager.tsx | 199 +++++++++++++++++--------------- src/client/views/nodes/DocumentView.tsx | 3 +- src/fields/Doc.ts | 8 +- src/fields/util.ts | 2 +- 5 files changed, 141 insertions(+), 105 deletions(-) (limited to 'src') diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index e352d46a8..c8d3be49b 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -3,11 +3,11 @@ import { observable, action, runInAction, computed } from "mobx"; import { SelectionManager } from "./SelectionManager"; import MainViewModal from "../views/MainViewModal"; import { observer } from "mobx-react"; -import { Doc, DocListCast, Opt } from "../../fields/Doc"; +import { Doc, DocListCast, Opt, DocListCastAsync } from "../../fields/Doc"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import * as fa from '@fortawesome/free-solid-svg-icons'; import { library } from "@fortawesome/fontawesome-svg-core"; -import SharingManager, { User } from "./SharingManager"; +import { User } from "./SharingManager"; import { Utils } from "../../Utils"; import * as RequestPromise from "request-promise"; import Select from 'react-select'; @@ -33,6 +33,7 @@ export default class GroupManager extends React.Component<{}> { @observable private selectedUsers: UserOptions[] | null = null; // list of users selected in the "Select users" dropdown. @observable currentGroup: Opt; // the currently selected group. private inputRef: React.RefObject = React.createRef(); // the ref for the input box. + private currentUserGroups: Doc[] = []; constructor(props: Readonly<{}>) { super(props); @@ -42,6 +43,26 @@ export default class GroupManager extends React.Component<{}> { // sets up the list of users componentDidMount() { this.populateUsers().then(resolved => runInAction(() => this.users = resolved)); + + // this.getAllGroups().forEach(group => { + // const members: string[] = JSON.parse(StrCast(group.members)); + // if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(group); + // }); + DocListCastAsync(this.GroupManagerDoc?.data).then(groups => { + groups?.forEach(group => { + const members: string[] = JSON.parse(StrCast(group.members)); + if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(group); + }); + }) + .finally(() => console.log(this.currentUserGroups)); + + // (this.GroupManagerDoc?.data as List).forEach(group => { + // Promise.resolve(group).then(resolvedGroup => { + // const members: string[] = JSON.parse(StrCast(resolvedGroup.members)); + // if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(resolvedGroup); + // }); + // }); + } /** @@ -49,8 +70,8 @@ export default class GroupManager extends React.Component<{}> { */ populateUsers = async () => { const userList: User[] = JSON.parse(await RequestPromise.get(Utils.prepend("/getUsers"))); - const currentUserIndex = userList.findIndex(user => user.email === Doc.CurrentUserEmail); - currentUserIndex !== -1 && userList.splice(currentUserIndex, 1); + // const currentUserIndex = userList.findIndex(user => user.email === Doc.CurrentUserEmail); + // currentUserIndex !== -1 && userList.splice(currentUserIndex, 1); return userList.map(user => user.email); } @@ -61,6 +82,11 @@ export default class GroupManager extends React.Component<{}> { return this.users.map(user => ({ label: user, value: user })); } + + get groupMemberships() { + return this.currentUserGroups; + } + /** * Makes the GroupManager visible. */ diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 491abe1dc..b4977f8ea 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -85,7 +85,7 @@ export default class SharingManager extends React.Component<{}> { @observable private dialogueBoxOpacity = 1; @observable private overlayOpacity = 0.4; @observable private selectedUsers: UserOptions[] | null = null; - @observable private permissions: SharingPermissions = SharingPermissions.None; + @observable private permissions: SharingPermissions = SharingPermissions.Edit; // private get linkVisible() { // return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; @@ -98,9 +98,9 @@ export default class SharingManager extends React.Component<{}> { this.targetDoc = target.props.Document; DictationOverlay.Instance.hasActiveModal = true; this.isOpen = true; - if (!this.sharingDoc) { - this.sharingDoc = new Doc; - } + // if (!this.sharingDoc) { + // this.sharingDoc = new Doc; + // } })); // runInAction(() => this.groups = GroupManager.Instance.getAllGroups()); @@ -116,13 +116,13 @@ export default class SharingManager extends React.Component<{}> { }), 500); }); - private get sharingDoc() { - return this.targetDoc ? Cast(this.targetDoc[SharingKey], Doc) as Doc : undefined; - } + // private get sharingDoc() { + // return this.targetDoc ? Cast(this.targetDoc[SharingKey], Doc) as Doc : undefined; + // } - private set sharingDoc(value: Doc | undefined) { - this.targetDoc && (this.targetDoc[SharingKey] = value); - } + // private set sharingDoc(value: Doc | undefined) { + // this.targetDoc && (this.targetDoc[SharingKey] = value); + // } constructor(props: {}) { super(props); @@ -153,76 +153,79 @@ export default class SharingManager extends React.Component<{}> { const members: string[] = JSON.parse(StrCast(group.members)); const users: ValidatedUser[] = this.users.filter(user => members.includes(user.user.email)); - const sharingDoc = this.sharingDoc!; - console.log(sharingDoc) - if (permission === SharingPermissions.None) { - const metadata = sharingDoc[StrCast(group.groupName)]; - if (metadata) sharingDoc[StrCast(group.groupName)] = undefined; - } - else { - sharingDoc[StrCast(group.groupName)] = permission; - } + const target = this.targetDoc!; + const ACL = `ACL-${StrCast(group.groupName)}`; + + target[ACL] = permission; + // const sharingDoc = this.sharingDoc!; + // if (permission === SharingPermissions.None) { + // const metadata = sharingDoc[StrCast(group.groupName)]; + // if (metadata) sharingDoc[StrCast(group.groupName)] = undefined; + // } + // else { + // sharingDoc[StrCast(group.groupName)] = permission; + // } - users.forEach(user => { - this.setInternalSharing(user, permission, group); + users.forEach(({ notificationDoc }) => { + Doc.AddDocToList(notificationDoc, storage, target); }); } - setInternalSharing = async (recipient: ValidatedUser, state: string, group?: Doc) => { + setInternalSharing = (recipient: ValidatedUser, permission: string, group?: Doc) => { const { user, notificationDoc } = recipient; const target = this.targetDoc!; - const manager = this.sharingDoc!; + // const manager = this.sharingDoc!; const key = user.userDocumentId; - let metadata = await DocCastAsync(manager[key]); - const permissions: { [key: string]: number } = metadata?.permissions ? JSON.parse(StrCast(metadata.permissions)) : {}; - permissions[StrCast(group ? group.groupName : Doc.CurrentUserEmail)] = parseInt(HierarchyMapping.get(state)!); - const max = Math.max(...Object.values(permissions)); - - // let max = 0; - // const keys: string[] = []; - // for (const [key, value] of Object.entries(permissions)) { - // if (value === max && max !== 0) { - // keys.push(key); - // } - // else if (value > max) { - // keys.splice(0, keys.length); - // keys.push(key); - // max = value; - // } - // } + const ACL = `ACL-${key}`; - switch (max) { - case 0: - if (metadata) { - const sharedAlias = (await DocCastAsync(metadata.sharedAlias))!; - Doc.RemoveDocFromList(notificationDoc, storage, sharedAlias); - manager[key] = undefined; - } - break; - - case 1: case 2: case 3: - if (!metadata) { - metadata = new Doc; - const sharedAlias = Doc.MakeAlias(target); - Doc.AddDocToList(notificationDoc, storage, sharedAlias); - metadata.sharedAlias = sharedAlias; - manager[key] = metadata; - } - metadata.permissions = JSON.stringify(permissions); - // metadata.usersShared = JSON.stringify(keys); - break; - } + // const permissions: { [key: string]: number } = target[ACL] ? JSON.parse(StrCast(target[ACL])) : {}; + + target[ACL] = permission; + + Doc.AddDocToList(notificationDoc, storage, target); - if (metadata) metadata.maxPermission = HierarchyMapping.get(`${max}`); + + // let metadata = await DocCastAsync(manager[key]); + // const permissions: { [key: string]: number } = metadata?.permissions ? JSON.parse(StrCast(metadata.permissions)) : {}; + // permissions[StrCast(group ? group.groupName : Doc.CurrentUserEmail)] = parseInt(HierarchyMapping.get(permission)!); + // const max = Math.max(...Object.values(permissions)); + + // switch (max) { + // case 0: + // // if (metadata) { + // // const sharedAlias = (await DocCastAsync(metadata.sharedAlias))!; + // // Doc.RemoveDocFromList(notificationDoc, storage, sharedAlias); + // // manager[key] = undefined; + // // } + // Doc.RemoveDocFromList(notificationDoc, storage, target); + // break; + + // case 1: case 2: case 3: + + // Doc.AddDocToList(notificationDoc, storage, target); + + // if (!metadata) { + // metadata = new Doc; + // const sharedAlias = Doc.MakeAlias(target); + // Doc.AddDocToList(notificationDoc, storage, target); + // metadata.sharedAlias = sharedAlias; + // manager[key] = metadata; + // } + // metadata.permissions = JSON.stringify(permissions); + // // metadata.usersShared = JSON.stringify(keys); + // break; + // } + + // if (metadata) metadata.maxPermission = HierarchyMapping.get(`${max}`); } - // private setExternalSharing = (state: string) => { + // private setExternalSharing = (permission: string) => { // const sharingDoc = this.sharingDoc; // if (!sharingDoc) { // return; // } - // sharingDoc[PublicKey] = state; + // sharingDoc[PublicKey] = permission; // } private get sharingUrl() { @@ -281,15 +284,21 @@ export default class SharingManager extends React.Component<{}> { } private computePermissions = (userKey: string) => { - const sharingDoc = this.sharingDoc; - if (!sharingDoc) { - return SharingPermissions.None; - } - const metadata = sharingDoc[userKey] as Doc | string; - if (!metadata) { - return SharingPermissions.None; - } - return StrCast(metadata instanceof Doc ? metadata.maxPermission : metadata, SharingPermissions.None); + // const sharingDoc = this.sharingDoc; + // if (!sharingDoc) { + // return SharingPermissions.None; + // } + // const metadata = sharingDoc[userKey] as Doc | string; + + if (!this.targetDoc) return SharingPermissions.None; + + const ACL = `ACL-${userKey}`; + const permission = StrCast(this.targetDoc[ACL]); + + // if (!metadata) { + // return SharingPermissions.None; + // } + return StrCast(this.targetDoc[ACL], SharingPermissions.None); } @action @@ -314,7 +323,6 @@ export default class SharingManager extends React.Component<{}> { this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions); } }); - this.selectedUsers = null; } @@ -377,27 +385,30 @@ export default class SharingManager extends React.Component<{}> {
*/}

Share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))}

-
- - {this.sharingOptions} - - -
+ {this.targetDoc?.author !== Doc.CurrentUserEmail ? null + : +
+ + {this.sharingOptions} + + +
+ }
{/*200*/} - {!existOtherUsers ? "There are no other users in your database." : + { this.users.map(({ user, notificationDoc }) => { // can't use async here const userKey = user.userDocumentId; const permissions = this.computePermissions(userKey); @@ -434,8 +445,8 @@ export default class SharingManager extends React.Component<{}> {
{/*200*/} - {!existGroups ? "There are no groups in your database." : - GroupManager.Instance.getAllGroups().map(group => { + { + GroupManager.Instance?.getAllGroups().map(group => { const permissions = this.computePermissions(StrCast(group.groupName)); // const color = ColorMapping.get(permissions); return permissions === SharingPermissions.None ? null : ( diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 09eeaee36..21b6d8310 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1169,9 +1169,8 @@ export class DocumentView extends DocComponent(Docu } render() { - if (!(this.props.Document instanceof Doc)) return (null); if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null); - if (this.props.Document.hidden) return (null); + if (!(this.props.Document instanceof Doc)) return (null); const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document); const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null))); const finalOpacity = this.props.opacity ? this.props.opacity() : opacity; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index e4d11dd4d..7b1db1042 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -96,13 +96,13 @@ export const AclSym = Symbol("Acl"); export const AclPrivate = Symbol("AclOwnerOnly"); export const AclReadonly = Symbol("AclReadOnly"); export const AclAddonly = Symbol("AclAddonly"); -export const AclReadWrite = Symbol("AclReadWrite"); +export const AclEdit = Symbol("AclEdit"); export const UpdatingFromServer = Symbol("UpdatingFromServer"); const CachedUpdates = Symbol("Cached updates"); export function fetchProto(doc: Doc) { - if (doc.author !== Doc.CurrentUserEmail) { + if (doc.author !== Doc.CurrentUserEmail) { // storing acls for groups needs to be extended here - AclSym should store a datastructure that stores information about permissions const acl = Doc.Get(doc, "ACL", true); switch (acl) { case "ownerOnly": @@ -114,8 +114,8 @@ export function fetchProto(doc: Doc) { case "addOnly": doc[AclSym] = AclAddonly; break; - case "write": - doc[AclSym] = AclReadWrite; + case "edit": + doc[AclSym] = AclEdit; } } diff --git a/src/fields/util.ts b/src/fields/util.ts index 2dc21c987..7bb090a93 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -111,7 +111,7 @@ const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHe "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"]; export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean { let prop = in_prop; - if (target[AclSym] && !_overrideAcl && !DocServer.PlaygroundFields.includes(in_prop.toString())) return true; + if (target[AclSym] && !_overrideAcl && !DocServer.PlaygroundFields.includes(in_prop.toString())) return true; // generalise to a testpermission function if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) { if (!prop.startsWith("_")) { console.log(prop + " is deprecated - switch to _" + prop); -- cgit v1.2.3-70-g09d2 From 293c62ea3eccbeb2565960abb5ab02fabb5e20a0 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Fri, 3 Jul 2020 16:16:36 +0530 Subject: many changes related to adding and removing document from the notificationDoc of a user --- src/client/DocServer.ts | 2 +- src/client/util/GroupManager.tsx | 11 +- src/client/util/SharingManager.scss | 62 ++++++--- src/client/util/SharingManager.tsx | 258 +++++++++++++++++++++++------------- src/client/views/MainViewModal.scss | 5 +- src/client/views/MainViewModal.tsx | 1 - 6 files changed, 218 insertions(+), 121 deletions(-) (limited to 'src') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 2a7a7c59a..eac53bb02 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -31,7 +31,7 @@ export namespace DocServer { export enum WriteMode { Default = 0, //Anything goes - Playground = 1, //Playground (write own/no read) + 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) } diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index c8d3be49b..220916ba7 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -7,7 +7,7 @@ import { Doc, DocListCast, Opt, DocListCastAsync } from "../../fields/Doc"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import * as fa from '@fortawesome/free-solid-svg-icons'; import { library } from "@fortawesome/fontawesome-svg-core"; -import { User } from "./SharingManager"; +import SharingManager, { User } from "./SharingManager"; import { Utils } from "../../Utils"; import * as RequestPromise from "request-promise"; import Select from 'react-select'; @@ -203,6 +203,7 @@ export default class GroupManager extends React.Component<{}> { // TODO look at this later // SharingManager.Instance.setInternalGroupSharing(group, "Not Shared"); Doc.RemoveDocFromList(this.GroupManagerDoc, "data", group); + SharingManager.Instance.removeGroup(group); if (group === this.currentGroup) { runInAction(() => this.currentGroup = undefined); } @@ -222,6 +223,7 @@ export default class GroupManager extends React.Component<{}> { const memberList: string[] = JSON.parse(StrCast(groupDoc.members)); !memberList.includes(email) && memberList.push(email); groupDoc.members = JSON.stringify(memberList); + SharingManager.Instance.shareWithAddedMember(groupDoc, email); } } @@ -234,8 +236,11 @@ export default class GroupManager extends React.Component<{}> { if (this.hasEditAccess(groupDoc)) { const memberList: string[] = JSON.parse(StrCast(groupDoc.members)); const index = memberList.indexOf(email); - index !== -1 && memberList.splice(index, 1); - groupDoc.members = JSON.stringify(memberList); + if (index !== -1) { + const user = memberList.splice(index, 1)[0]; + groupDoc.members = JSON.stringify(memberList); + SharingManager.Instance.removeMember(groupDoc, email); + } } } diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index 209c41651..2708876a3 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -4,6 +4,7 @@ // display: flex; // flex-direction: column; width: 600px; + height: 360px; .dialogue-box { width: 450; @@ -14,10 +15,23 @@ transform: translate(-20px, -20px); } + select { + text-align: justify; + text-align-last: end + } + .sharing-contents { display: flex; flex-direction: column; + .close-button { + position: absolute; + right: 1em; + top: 1em; + cursor: pointer; + z-index: 999; + } + .share-setup { display: flex; margin-bottom: 20px; @@ -33,11 +47,14 @@ margin-left: -100; border: none; outline: none; + text-align: justify; // for Edge + text-align-last: end; } .share-button { - height: 100%; - margin-left: 3%; + height: 105%; + margin-left: 2%; + background-color: #979797; } } @@ -57,8 +74,8 @@ .groups-list, .users-list { font-style: italic; - background: white; - border: 1px solid black; + background: gainsboro; + // border: 1px solid black; padding-left: 10px; padding-right: 10px; overflow-y: scroll; @@ -71,8 +88,14 @@ justify-content: center; // color: red; color: black; - height: 150px; + height: 255px; margin: 0 2; + + + .none { + font-style: italic; + + } } } } @@ -146,7 +169,7 @@ font-size: 14; font-weight: normal; padding: 0; - align-items: baseline; + align-items: center; &:hover .padding { white-space: unset; @@ -164,6 +187,7 @@ .permissions-dropdown { border: none; height: 25; + background: gainsboro; } .edit-actions { @@ -210,17 +234,17 @@ } } - .close-button { - border-radius: 5px; - margin-top: 20px; - padding: 10px 0; - background: aliceblue; - transition: 0.5s ease all; - border: 1px solid; - border-color: aliceblue; - } - - .close-button:hover { - border-color: black; - } + // .close-button { + // border-radius: 5px; + // margin-top: 20px; + // padding: 10px 0; + // background: aliceblue; + // transition: 0.5s ease all; + // border: 1px solid; + // border-color: aliceblue; + // } + + // .close-button:hover { + // border-color: black; + // } } \ No newline at end of file diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index b4977f8ea..372b6172d 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -1,7 +1,7 @@ import { observable, runInAction, action } from "mobx"; import * as React from "react"; import MainViewModal from "../views/MainViewModal"; -import { Doc, Opt, DocCastAsync } from "../../fields/Doc"; +import { Doc, Opt, DocCastAsync, DocListCast } from "../../fields/Doc"; import { DocServer } from "../DocServer"; import { Cast, StrCast } from "../../fields/Types"; import * as RequestPromise from "request-promise"; @@ -19,6 +19,8 @@ import { DictationOverlay } from "../views/DictationOverlay"; import GroupManager, { UserOptions } from "./GroupManager"; import GroupMemberView from "./GroupMemberView"; import Select from "react-select"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { List } from "../../fields/List"; library.add(fa.faCopy); @@ -86,6 +88,8 @@ export default class SharingManager extends React.Component<{}> { @observable private overlayOpacity = 0.4; @observable private selectedUsers: UserOptions[] | null = null; @observable private permissions: SharingPermissions = SharingPermissions.Edit; + @observable private sharedUsers: ValidatedUser[] = []; + @observable private sharedGroups: Doc[] = []; // private get linkVisible() { // return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; @@ -166,16 +170,52 @@ export default class SharingManager extends React.Component<{}> { // sharingDoc[StrCast(group.groupName)] = permission; // } + group.docsShared ? Doc.IndexOf(target, DocListCast(group.docsShared)) === -1 && (group.docsShared as List).push(target) : group.docsShared = new List([target]); + users.forEach(({ notificationDoc }) => { - Doc.AddDocToList(notificationDoc, storage, target); + + if (permission !== SharingPermissions.None) Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target); + else Doc.IndexOf(target, DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); + }); } - setInternalSharing = (recipient: ValidatedUser, permission: string, group?: Doc) => { + shareWithAddedMember = (group: Doc, email: string) => { + const user: ValidatedUser = this.users.find(user => user.user.email === email)!; + + if (group.docsShared) { + DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc)); + } + } + + removeMember = (group: Doc, email: string) => { + const user: ValidatedUser = this.users.find(user => user.user.email === email)!; + + if (group.docsShared) { + DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(user.notificationDoc, storage, doc)); + } + } + + removeGroup = (group: Doc) => { + if (group.docsShared) { + DocListCast(group.docsShared).forEach(doc => { + const ACL = `ACL-${StrCast(group.groupName)}`; + doc[ACL] = "Not Shared"; + + const members: string[] = JSON.parse(StrCast(group.members)); + const users: ValidatedUser[] = this.users.filter(user => members.includes(user.user.email)); + + users.forEach(user => Doc.RemoveDocFromList(user.notificationDoc, storage, doc)); + }) + } + } + + setInternalSharing = (recipient: ValidatedUser, permission: string) => { const { user, notificationDoc } = recipient; const target = this.targetDoc!; // const manager = this.sharingDoc!; - const key = user.userDocumentId; + const key = user.email.replace('.', '_'); + // const key = user.userDocumentId; const ACL = `ACL-${key}`; @@ -183,43 +223,47 @@ export default class SharingManager extends React.Component<{}> { target[ACL] = permission; - Doc.AddDocToList(notificationDoc, storage, target); - - - // let metadata = await DocCastAsync(manager[key]); - // const permissions: { [key: string]: number } = metadata?.permissions ? JSON.parse(StrCast(metadata.permissions)) : {}; - // permissions[StrCast(group ? group.groupName : Doc.CurrentUserEmail)] = parseInt(HierarchyMapping.get(permission)!); - // const max = Math.max(...Object.values(permissions)); - - // switch (max) { - // case 0: - // // if (metadata) { - // // const sharedAlias = (await DocCastAsync(metadata.sharedAlias))!; - // // Doc.RemoveDocFromList(notificationDoc, storage, sharedAlias); - // // manager[key] = undefined; - // // } - // Doc.RemoveDocFromList(notificationDoc, storage, target); - // break; - - // case 1: case 2: case 3: - - // Doc.AddDocToList(notificationDoc, storage, target); - - // if (!metadata) { - // metadata = new Doc; - // const sharedAlias = Doc.MakeAlias(target); - // Doc.AddDocToList(notificationDoc, storage, target); - // metadata.sharedAlias = sharedAlias; - // manager[key] = metadata; - // } - // metadata.permissions = JSON.stringify(permissions); - // // metadata.usersShared = JSON.stringify(keys); - // break; - // } - // if (metadata) metadata.maxPermission = HierarchyMapping.get(`${max}`); + if (permission !== SharingPermissions.None) Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target); + else Doc.IndexOf(target, DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); + } + + // let metadata = await DocCastAsync(manager[key]); + // const permissions: { [key: string]: number } = metadata?.permissions ? JSON.parse(StrCast(metadata.permissions)) : {}; + // permissions[StrCast(group ? group.groupName : Doc.CurrentUserEmail)] = parseInt(HierarchyMapping.get(permission)!); + // const max = Math.max(...Object.values(permissions)); + + // switch (max) { + // case 0: + // // if (metadata) { + // // const sharedAlias = (await DocCastAsync(metadata.sharedAlias))!; + // // Doc.RemoveDocFromList(notificationDoc, storage, sharedAlias); + // // manager[key] = undefined; + // // } + // Doc.RemoveDocFromList(notificationDoc, storage, target); + // break; + + // case 1: case 2: case 3: + + // Doc.AddDocToList(notificationDoc, storage, target); + + // if (!metadata) { + // metadata = new Doc; + // const sharedAlias = Doc.MakeAlias(target); + // Doc.AddDocToList(notificationDoc, storage, target); + // metadata.sharedAlias = sharedAlias; + // manager[key] = metadata; + // } + // metadata.permissions = JSON.stringify(permissions); + // // metadata.usersShared = JSON.stringify(keys); + // break; + // } + + // if (metadata) metadata.maxPermission = HierarchyMapping.get(`${max}`); + + // private setExternalSharing = (permission: string) => { // const sharingDoc = this.sharingDoc; // if (!sharingDoc) { @@ -345,6 +389,67 @@ export default class SharingManager extends React.Component<{}> { ] : []; + const userListContents: (JSX.Element | null)[] = this.users.map(({ user, notificationDoc }) => { // can't use async here + const userKey = user.email.replace('.', '_'); + // const userKey = user.userDocumentId; + const permissions = this.computePermissions(userKey); + // const color = ColorMapping.get(permissions); + + // console.log(manager); + // const metadata = manager[userKey] as Doc; + // const usersShared = StrCast(metadata?.usersShared, ""); + // console.log(usersShared) + + return permissions === SharingPermissions.None ? null : ( +
+ {user.email} + {/*
{usersShared}
*/} +
+ +
+
+ ); + }); + + + const groupListContents = GroupManager.Instance?.getAllGroups().map(group => { + const permissions = this.computePermissions(StrCast(group.groupName)); + // const color = ColorMapping.get(permissions); + + return permissions === SharingPermissions.None ? null : ( +
+ {group.groupName} +
+ + +
+
+ ); + }); + + const displayUserList = userListContents?.every(user => user === null); + const displayGroupList = groupListContents?.every(group => group === null); + return (
{GroupManager.Instance?.currentGroup ? @@ -385,6 +490,9 @@ export default class SharingManager extends React.Component<{}> {
*/}

Share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))}

+
+ +
{this.targetDoc?.author !== Doc.CurrentUserEmail ? null :
@@ -407,69 +515,30 @@ export default class SharingManager extends React.Component<{}> { }
-
{/*200*/} +
{/*200*/} { - this.users.map(({ user, notificationDoc }) => { // can't use async here - const userKey = user.userDocumentId; - const permissions = this.computePermissions(userKey); - // const color = ColorMapping.get(permissions); - - // console.log(manager); - // const metadata = manager[userKey] as Doc; - // const usersShared = StrCast(metadata?.usersShared, ""); - // console.log(usersShared) - - - return permissions === SharingPermissions.None ? null : ( -
- {user.email} - {/*
{usersShared}
*/} -
- -
-
- ); - }) + displayUserList ? +
+ There are no users this document has been shared with. +
+ : + userListContents }
-
{/*200*/} +
{/*200*/} { - GroupManager.Instance?.getAllGroups().map(group => { - const permissions = this.computePermissions(StrCast(group.groupName)); - // const color = ColorMapping.get(permissions); - return permissions === SharingPermissions.None ? null : ( -
- {group.groupName} -
- - -
+ displayGroupList ? +
+ There are no groups this document has been shared with.
- ); - }) - + : + groupListContents }
@@ -477,7 +546,6 @@ export default class SharingManager extends React.Component<{}> {
-
Done
); } diff --git a/src/client/views/MainViewModal.scss b/src/client/views/MainViewModal.scss index f5a9ee76c..812fe540b 100644 --- a/src/client/views/MainViewModal.scss +++ b/src/client/views/MainViewModal.scss @@ -6,9 +6,10 @@ align-self: center; align-content: center; padding: 20px; - background: gainsboro; + // background: gainsboro; + background: white; border-radius: 10px; - border: 3px solid black; + border: 0.5px solid black; box-shadow: #00000044 5px 5px 10px; transform: translate(-50%, -50%); top: 50%; diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx index a7bd5882d..c6b3532e8 100644 --- a/src/client/views/MainViewModal.tsx +++ b/src/client/views/MainViewModal.tsx @@ -22,7 +22,6 @@ export default class MainViewModal extends React.Component
Date: Fri, 3 Jul 2020 20:22:52 +0900 Subject: pin and icon --- src/client/views/AntimodeMenu.tsx | 7 +- src/client/views/GestureOverlay.tsx | 10 +- src/client/views/MainView.tsx | 11 +- .../collectionFreeForm/InkOptionsMenu.scss | 5 + .../collectionFreeForm/InkOptionsMenu.tsx | 267 ++++++++++++++------- 5 files changed, 202 insertions(+), 98 deletions(-) (limited to 'src') diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx index 3e4d20fea..d83ac434c 100644 --- a/src/client/views/AntimodeMenu.tsx +++ b/src/client/views/AntimodeMenu.tsx @@ -137,13 +137,18 @@ export default abstract class AntimodeMenu extends React.Component { protected getElement(buttons: JSX.Element[]) { return (
+ style={{ + left: this._left, top: this._top, opacity: this._opacity, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, + position: this.Pinned ? "unset" : undefined + }}>
{buttons}
); } + + protected getElementWithRows(rows: JSX.Element[], numRows: number, hasDragger: boolean = true) { return (
{ @@ -885,7 +891,9 @@ export default class GestureOverlay extends Touchable { render() { return ( -
+ +
{this.showMobileInkOverlay ? : <>} {this.elements} diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 8f5a31b6c..afdff4ed8 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -453,10 +453,12 @@ export class MainView extends React.Component { @computed get mainContent() { const sidebar = this.userDoc?.["tabs-panelContainer"]; + console.log(InkOptionsMenu.Instance?.Pinned); return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
@@ -593,7 +595,10 @@ export class MainView extends React.Component { - + + + + {this.mainContent} @@ -606,7 +611,7 @@ export class MainView extends React.Component { - + {this.snapLines} diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss index a9fab4c1e..7329f4fc1 100644 --- a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss +++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss @@ -26,6 +26,11 @@ /* Make the buttons appear below each other */ } +.btn-draw { + display: inline; + /* Make the buttons appear below each other */ +} + .btn2-group { display: block; background: #323232; diff --git a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx index f1032adaa..7de70b8cd 100644 --- a/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx +++ b/src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx @@ -17,6 +17,7 @@ import { DocumentType } from "../../../documents/DocumentTypes"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; import { faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSubscript, faSuperscript, faIndent, faEyeDropper, faCaretDown, faPalette, faArrowsAlt, faHighlighter, faLink, faPaintRoller, faSleigh, faBars, faFillDrip, faBrush, faPenNib, faShapes, faArrowLeft, faEllipsisH, faBezierCurve, } from "@fortawesome/free-solid-svg-icons"; +import { Cast, StrCast, BoolCast } from "../../../../fields/Types"; library.add(faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, faCaretDown, faPalette, faArrowsAlt, faHighlighter, faLink, faPaintRoller, faBars, faFillDrip, faBrush, faPenNib, faShapes, faArrowLeft, faEllipsisH, faBezierCurve); @@ -28,36 +29,67 @@ export default class InkOptionsMenu extends AntimodeMenu { private _width = ["1", "5", "10", "100"]; // private _buttons = ["circle", "triangle", "rectangle", "arrow", "line"]; // private _icons = ["O", "∆", "ロ", "➜", "-"]; - private _buttons = ["circle", "triangle", "rectangle", "line", "noRec", "",]; - private _icons = ["O", "∆", "ロ", "⎯", "✖︎", " "]; + // private _buttons = ["circle", "triangle", "rectangle", "line", "noRec", "",]; + // private _icons = ["O", "∆", "ロ", "⎯⎯⎯", "✖︎", " "]; //arrowStart and arrowEnd must match and defs must exist in Inking Stroke - private _arrowStart = ["arrowHead", "arrowHead", "dot", "dot", "none"]; - private _arrowEnd = ["none", "arrowEnd", "none", "dot", "none"]; - private _arrowIcons = ["→", "↔︎", "•", "••", " "]; + // private _arrowStart = ["arrowHead", "arrowHead", "dot", "dot", "none"]; + // private _arrowEnd = ["none", "arrowEnd", "none", "dot", "none"]; + // private _arrowIcons = ["→", "↔︎", "•", "••", " "]; + private _draw = ["⎯", "→", "↔︎", "∿", "↝", "↭", "ロ", "O", "∆"]; + private _head = ["none", "arrowHead", "arrowHead", "none", "arrowHead", "arrowHead", "none", "none", "none"]; + private _end = ["none", "none", "arrowEnd", "none", "none", "arrowEnd", "none", "none", "none"]; + private _shape = ["", "", "", "", "", "", "rectangle", "circle", "triangle"]; + + @observable _shapesNum = this._shape.length; + @observable _selected = this._shapesNum; + + @observable private collapsed: boolean = false; @observable _colorBtn = false; @observable _widthBtn = false; @observable _fillBtn = false; - @observable _arrowBtn = false; - @observable _dashBtn = false; - @observable _shapeBtn = false; + // @observable _arrowBtn = false; + // @observable _dashBtn = false; + // @observable _shapeBtn = false; constructor(props: Readonly<{}>) { super(props); InkOptionsMenu.Instance = this; this._canFade = false; // don't let the inking menu fade away + this.Pinned = BoolCast(Doc.UserDoc()["inkOptionsMenu-pinned"]); + } - getColors = () => { - return this._palette; + @action + toggleMenuPin = (e: React.MouseEvent) => { + Doc.UserDoc()["inkOptionsMenu-pinned"] = this.Pinned = !this.Pinned; + if (!this.Pinned) { + // this.fadeOut(true); + } } @action - changeArrow = (arrowStart: string, arrowEnd: string) => { - SetActiveArrowStart(arrowStart); - SetActiveArrowEnd(arrowEnd); + protected toggleCollapse = (e: React.MouseEvent) => { + this.collapsed = !this.collapsed; + setTimeout(() => { + const x = Math.min(this._left, window.innerWidth - InkOptionsMenu.Instance.width); + InkOptionsMenu.Instance.jumpTo(x, this._top, true); + }, 0); + } + + + + + getColors = () => { + return this._palette; } + // @action + // changeArrow = (arrowStart: string, arrowEnd: string) => { + // SetActiveArrowStart(arrowStart); + // SetActiveArrowEnd(arrowEnd); + // } + @action changeColor = (color: string, type: string) => { const col: ColorState = { @@ -116,40 +148,77 @@ export default class InkOptionsMenu extends AntimodeMenu { this.editProperties(ActiveDash(), "dash"); } - @computed get arrowPicker() { - var currIcon; - for (var i = 0; i < this._arrowStart.length; i++) { - if (this._arrowStart[i] === ActiveArrowStart() && this._arrowEnd[i] === ActiveArrowEnd()) { - currIcon = this._arrowIcons[i]; - if (this._arrowIcons[i] === " ") { - currIcon = "➤"; - } - } - } - var arrowPicker = ; - if (this._arrowBtn) { - arrowPicker =
- {arrowPicker} - {this._arrowStart.map((arrowStart, i) => { - return ; - })} -
; - } - return arrowPicker; + @computed get drawButtons() { + const drawButtons =
+ {this._draw.map((icon, i) => { + return ; + })}
; + return drawButtons; } + // @computed get arrowPicker() { + // var currIcon; + // for (var i = 0; i < this._arrowStart.length; i++) { + // if (this._arrowStart[i] === ActiveArrowStart() && this._arrowEnd[i] === ActiveArrowEnd()) { + // currIcon = this._arrowIcons[i]; + // if (this._arrowIcons[i] === " ") { + // currIcon = "➤"; + // } + // } + // } + // var arrowPicker = ; + // if (this._arrowBtn) { + // arrowPicker =
+ // {arrowPicker} + // {this._arrowStart.map((arrowStart, i) => { + // return ; + // })} + //
; + // } + // return arrowPicker; + // } + @computed get widthPicker() { var widthPicker = ; - if (this._shapeBtn) { - shapePicker =
- {shapePicker} - {this._buttons.map((btn, i) => { - var ttl = btn; - if (btn === "") { - ttl = "no shape"; - } - if (btn === "noRec") { - ttl = "disable shape recognition"; - } - return ; - })} -
; - } - return shapePicker; - } + // @computed get shapePicker() { + // var currIcon; + // if (GestureOverlay.Instance.InkShape === "") { + // currIcon = ; + // } else { + // for (var i = 0; i < this._icons.length; i++) { + // if (GestureOverlay.Instance.InkShape === this._buttons[i]) { + // currIcon = this._icons[i]; + // } + // } + // } + // var shapePicker = ; + // if (this._shapeBtn) { + // shapePicker =
+ // {shapePicker} + // {this._buttons.map((btn, i) => { + // var ttl = btn; + // if (btn === "") { + // ttl = "no shape"; + // } + // if (btn === "noRec") { + // ttl = "disable shape recognition"; + // } + // return ; + // })} + //
; + // } + // return shapePicker; + // } @computed get bezierButton() { return , - this.shapePicker, - this.bezierButton, + // this.shapePicker, + // this.bezierButton, this.widthPicker, this.colorPicker, this.fillPicker, - this.arrowPicker, - this.dashButton, + this.drawButtons, + // this.arrowPicker, + // this.dashButton, + + ]; + + // return this.getElement(buttons); return this.getElement(buttons); } } Scripting.addGlobal(function activatePen(penBtn: any) { if (penBtn) { - Doc.SetSelectedTool(InkTool.Pen); + //no longer changes to inkmode + // Doc.SetSelectedTool(InkTool.Pen); InkOptionsMenu.Instance.jumpTo(300, 300); + InkOptionsMenu.Instance.Pinned = true; + } else { - Doc.SetSelectedTool(InkTool.None); + // Doc.SetSelectedTool(InkTool.None); + InkOptionsMenu.Instance.Pinned = false; InkOptionsMenu.Instance.fadeOut(true); + } }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 1410392575b411f49601ce039787b6052542b711 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Fri, 3 Jul 2020 21:50:56 +0530 Subject: can now exit modal by clicking outside it --- src/client/apis/GoogleAuthenticationManager.tsx | 1 + src/client/util/GroupManager.tsx | 1 + src/client/util/GroupMemberView.tsx | 1 + src/client/util/SettingsManager.tsx | 1 + src/client/util/SharingManager.tsx | 1 + src/client/views/DictationOverlay.tsx | 1 + src/client/views/MainViewModal.tsx | 35 +++++++++++++++++++++++++ src/mobile/AudioUpload.tsx | 1 + src/mobile/ImageUpload.tsx | 1 + 9 files changed, 43 insertions(+) (limited to 'src') diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx index bf4469aeb..5a2bdb13b 100644 --- a/src/client/apis/GoogleAuthenticationManager.tsx +++ b/src/client/apis/GoogleAuthenticationManager.tsx @@ -157,6 +157,7 @@ export default class GoogleAuthenticationManager extends React.Component<{}> { contents={this.renderPrompt} overlayDisplayedOpacity={0.9} dialogueBoxStyle={this.dialogueBoxStyle} + closeOnExternalClick={() => this.isOpen = false} /> ); } diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 220916ba7..0e710457e 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -387,6 +387,7 @@ export default class GroupManager extends React.Component<{}> { interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} + closeOnExternalClick={this.close} /> ); } diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index c844892b1..4377a1428 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -68,6 +68,7 @@ export default class GroupMemberView extends React.Component; } diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 9888cce48..fa2b20095 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -134,6 +134,7 @@ export default class SettingsManager extends React.Component<{}> { interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} + closeOnExternalClick={this.close} /> ); } diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 372b6172d..e74824581 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -559,6 +559,7 @@ export default class SharingManager extends React.Component<{}> { interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} + closeOnExternalClick={this.close} /> ); } diff --git a/src/client/views/DictationOverlay.tsx b/src/client/views/DictationOverlay.tsx index 65770c0bb..9ed14509f 100644 --- a/src/client/views/DictationOverlay.tsx +++ b/src/client/views/DictationOverlay.tsx @@ -66,6 +66,7 @@ export class DictationOverlay extends React.Component { interactive={false} dialogueBoxStyle={dialogueBoxStyle} overlayStyle={overlayStyle} + closeOnExternalClick={this.initiateDictationFade} />); } } \ No newline at end of file diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx index c6b3532e8..6041593b8 100644 --- a/src/client/views/MainViewModal.tsx +++ b/src/client/views/MainViewModal.tsx @@ -1,5 +1,8 @@ import * as React from 'react'; import "./MainViewModal.scss"; +import { Opt } from '../../fields/Doc'; +import { Lambda, reaction } from 'mobx'; +import { observer } from 'mobx-react'; export interface MainViewOverlayProps { isDisplayed: boolean; @@ -9,10 +12,41 @@ export interface MainViewOverlayProps { overlayStyle?: React.CSSProperties; dialogueBoxDisplayedOpacity?: number; overlayDisplayedOpacity?: number; + closeOnExternalClick?: () => void; } +@observer export default class MainViewModal extends React.Component { + private ref: React.RefObject = React.createRef(); + private displayedListenerDisposer: Opt; + + componentDidMount() { + + document.removeEventListener("click", this.close); + + this.displayedListenerDisposer = reaction(() => this.props.isDisplayed, (isDisplayed) => { + if (isDisplayed) document.addEventListener("click", this.close); + else document.removeEventListener("click", this.close); + }); + } + + componentWillUnmount() { + this.displayedListenerDisposer?.(); + document.removeEventListener("click", this.close); + } + + close = (e: MouseEvent) => { + + const { left, right, top, bottom } = this.ref.current!.getBoundingClientRect(); + + if (e.clientX === 0 && e.clientY === 0) return; // why does this happen? + if (e.clientX < left || e.clientX > right || e.clientY > bottom || e.clientY < top) { + this.props.closeOnExternalClick?.(); + } + + } + render() { const p = this.props; const dialogueOpacity = p.dialogueBoxDisplayedOpacity || 1; @@ -26,6 +60,7 @@ export default class MainViewModal extends React.Component ...(p.dialogueBoxStyle || {}), opacity: p.isDisplayed ? dialogueOpacity : 0 }} + ref={this.ref} >{p.contents}
); } diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index 5ea626d52..6a5834f52 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -172,6 +172,7 @@ export class Uploader extends React.Component { interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} + closeOnExternalClick={this.closeUpload} /> ); } -- cgit v1.2.3-70-g09d2 From fae27af1a57e88be628a77f9d0a8b1c9e5fe32e2 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Sat, 4 Jul 2020 00:00:55 +0530 Subject: changed click to pointerdown --- src/client/views/MainViewModal.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx index 6041593b8..2bdd159c9 100644 --- a/src/client/views/MainViewModal.tsx +++ b/src/client/views/MainViewModal.tsx @@ -23,20 +23,20 @@ export default class MainViewModal extends React.Component componentDidMount() { - document.removeEventListener("click", this.close); + document.removeEventListener("pointerdown", this.close); this.displayedListenerDisposer = reaction(() => this.props.isDisplayed, (isDisplayed) => { - if (isDisplayed) document.addEventListener("click", this.close); - else document.removeEventListener("click", this.close); + if (isDisplayed) document.addEventListener("pointerdown", this.close); + else document.removeEventListener("pointerdown", this.close); }); } componentWillUnmount() { this.displayedListenerDisposer?.(); - document.removeEventListener("click", this.close); + document.removeEventListener("pointerdown", this.close); } - close = (e: MouseEvent) => { + close = (e: PointerEvent) => { const { left, right, top, bottom } = this.ref.current!.getBoundingClientRect(); -- cgit v1.2.3-70-g09d2 From 9cc98267457b0d69d4699b27d881430c169c2c2a Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Sun, 5 Jul 2020 23:35:00 +0530 Subject: simplified exit on outside click + rotating arrow in dropdown menu --- src/client/util/GroupManager.tsx | 7 +++++++ src/client/util/GroupMemberView.tsx | 7 +++++++ src/client/views/MainViewModal.tsx | 33 +-------------------------------- 3 files changed, 15 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 0e710457e..b14dcf55b 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -362,6 +362,13 @@ export default class GroupManager extends React.Component<{}> { placeholder={"Select users"} value={this.selectedUsers} closeMenuOnSelect={false} + styles={{ + dropdownIndicator: (base, state) => ({ + ...base, + transition: '0.5s all ease', + transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : undefined + }) + }} />
diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index 4377a1428..cc279b6b2 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -42,6 +42,13 @@ export default class GroupMemberView extends React.Component ({ + ...base, + transition: '0.5s all ease', + transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : undefined + }) + }} />
diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx index 2bdd159c9..0b73a6ad7 100644 --- a/src/client/views/MainViewModal.tsx +++ b/src/client/views/MainViewModal.tsx @@ -1,7 +1,5 @@ import * as React from 'react'; import "./MainViewModal.scss"; -import { Opt } from '../../fields/Doc'; -import { Lambda, reaction } from 'mobx'; import { observer } from 'mobx-react'; export interface MainViewOverlayProps { @@ -18,35 +16,6 @@ export interface MainViewOverlayProps { @observer export default class MainViewModal extends React.Component { - private ref: React.RefObject = React.createRef(); - private displayedListenerDisposer: Opt; - - componentDidMount() { - - document.removeEventListener("pointerdown", this.close); - - this.displayedListenerDisposer = reaction(() => this.props.isDisplayed, (isDisplayed) => { - if (isDisplayed) document.addEventListener("pointerdown", this.close); - else document.removeEventListener("pointerdown", this.close); - }); - } - - componentWillUnmount() { - this.displayedListenerDisposer?.(); - document.removeEventListener("pointerdown", this.close); - } - - close = (e: PointerEvent) => { - - const { left, right, top, bottom } = this.ref.current!.getBoundingClientRect(); - - if (e.clientX === 0 && e.clientY === 0) return; // why does this happen? - if (e.clientX < left || e.clientX > right || e.clientY > bottom || e.clientY < top) { - this.props.closeOnExternalClick?.(); - } - - } - render() { const p = this.props; const dialogueOpacity = p.dialogueBoxDisplayedOpacity || 1; @@ -60,10 +29,10 @@ export default class MainViewModal extends React.Component ...(p.dialogueBoxStyle || {}), opacity: p.isDisplayed ? dialogueOpacity : 0 }} - ref={this.ref} >{p.contents}
Date: Mon, 6 Jul 2020 18:18:17 +0530 Subject: trying first implementation of storing acls --- src/client/DocServer.ts | 7 ++- src/client/util/GroupManager.tsx | 60 +++---------------- src/client/util/SharingManager.tsx | 42 +++++++------- src/client/views/DocComponent.tsx | 7 ++- src/client/views/collections/CollectionView.tsx | 8 ++- src/client/views/nodes/DocumentContentsView.tsx | 6 +- src/client/views/nodes/DocumentView.tsx | 5 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 8 ++- src/fields/Doc.ts | 67 ++++++++++++++-------- src/fields/util.ts | 60 +++++++++++++++++-- 10 files changed, 151 insertions(+), 119 deletions(-) (limited to 'src') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index eac53bb02..860a8fd92 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -39,9 +39,10 @@ export namespace DocServer { const docsWithUpdates: { [field: string]: Set } = {}; export var PlaygroundFields: string[]; - export function setPlaygroundFields(livePlayougroundFields: string[]) { - DocServer.PlaygroundFields = livePlayougroundFields; - livePlayougroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground)); + export function setPlaygroundFields(livePlaygroundFields: string[]) { + console.log("here"); + DocServer.PlaygroundFields = livePlaygroundFields; + livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground)); } export function setFieldWriteMode(field: string, writeMode: WriteMode) { diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index b14dcf55b..83b206f94 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -33,7 +33,7 @@ export default class GroupManager extends React.Component<{}> { @observable private selectedUsers: UserOptions[] | null = null; // list of users selected in the "Select users" dropdown. @observable currentGroup: Opt; // the currently selected group. private inputRef: React.RefObject = React.createRef(); // the ref for the input box. - private currentUserGroups: Doc[] = []; + currentUserGroups: string[] = []; constructor(props: Readonly<{}>) { super(props); @@ -51,7 +51,7 @@ export default class GroupManager extends React.Component<{}> { DocListCastAsync(this.GroupManagerDoc?.data).then(groups => { groups?.forEach(group => { const members: string[] = JSON.parse(StrCast(group.members)); - if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(group); + if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(StrCast(group.groupName)); }); }) .finally(() => console.log(this.currentUserGroups)); @@ -82,11 +82,6 @@ export default class GroupManager extends React.Component<{}> { return this.users.map(user => ({ label: user, value: user })); } - - get groupMemberships() { - return this.currentUserGroups; - } - /** * Makes the GroupManager visible. */ @@ -151,6 +146,11 @@ export default class GroupManager extends React.Component<{}> { ); } + getGroupMembers(group: string | Doc): string[] { + if (group instanceof Doc) return JSON.parse(StrCast(group.members)) as string[]; + else return JSON.parse(StrCast(this.getGroup(group)!.members)) as string[]; + } + /** * @returns the members of the admin group. */ @@ -279,52 +279,6 @@ export default class GroupManager extends React.Component<{}> { this.inputRef.current.value = ""; } - /** - * A getter that @returns the interface rendered to view an individual group. - */ - // private get editingInterface() { - // const members: string[] = this.currentGroup ? JSON.parse(StrCast(this.currentGroup.members)) : []; - // const options: UserOptions[] = this.currentGroup ? this.options.filter(option => !(JSON.parse(StrCast(this.currentGroup!.members)) as string[]).includes(option.value)) : []; - // return (!this.currentGroup ? null : - //
- //
- // {this.currentGroup.groupName} - //
this.currentGroup = undefined)}> - // - //
- - // {this.hasEditAccess(this.currentGroup) ? - //
- //
- // +
+
Add
+
; } @action @@ -346,7 +375,7 @@ export class LinkEditor extends React.Component { {this.followBehavior} + size={"lg"} />
@@ -367,6 +396,11 @@ export class LinkEditor extends React.Component {
; } + @action + changeInfo = () => { + this.showInfo = !this.showInfo; + } + render() { const destination = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); @@ -382,11 +416,17 @@ export class LinkEditor extends React.Component { style={{ display: this.props.hideback ? "none" : "" }} onClick={this.props.showLinks}> -

editing link to: { +

Editing Link to: { destination.proto?.title ?? destination.title ?? "untitled"}

- + {/* */} +
+ {this.showInfo ?
+
{this.props.linkDoc.author ?
Author: {this.props.linkDoc.author}
: null}
+
{this.props.linkDoc.creationDate ?
Creation Date: + {DateCast(this.props.linkDoc.creationDate).toString()}
: null}
+
: null}
{this.editDescription}
{this.followingDropdown}
diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss index 10a24c5ca..422ab0430 100644 --- a/src/client/views/linking/LinkMenu.scss +++ b/src/client/views/linking/LinkMenu.scss @@ -3,7 +3,8 @@ .linkMenu { width: 100%; height: auto; - //border: 1px solid black; + + border: 1px solid black; // &:hover { // width: calc(auto + 26px); @@ -11,9 +12,13 @@ } .linkMenu-list { + border: 1px solid black; + + box-shadow: 3px 3px 1.5px grey; + max-height: 170px; - //overflow-y: scroll; + overflow-y: scroll; position: absolute; z-index: 10; background: white; @@ -34,6 +39,10 @@ border-bottom: 0.5px solid lightgray; //@extend: 5px 0; + &:last-child { + border-bottom: none; + } + .linkMenu-group-name { display: flex; diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index 064c24f7a..478ac3fe6 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -1,4 +1,4 @@ -import { action, observable } from "mobx"; +import { action, observable, computed } from "mobx"; import { observer } from "mobx-react"; import { DocumentView } from "../nodes/DocumentView"; import { LinkEditor } from "./LinkEditor"; @@ -29,7 +29,14 @@ export class LinkMenu extends React.Component { @observable private _linkMenuRef = React.createRef(); private _editorRef = React.createRef(); - @observable private _numLinks: number = 0; + //@observable private _numLinks: number = 0; + + // @computed get overflow() { + // if (this._numLinks) { + // return "scroll"; + // } + // return "auto"; + // } @action onClick = (e: PointerEvent) => { @@ -71,9 +78,6 @@ export class LinkMenu extends React.Component { showEditor={action((linkDoc: Doc) => this._editingLink = linkDoc)} addDocTab={this.props.addDocTab} /> ); - group.forEach((item) => { - this._numLinks++; - }); }); // if source doc has no links push message @@ -82,7 +86,6 @@ export class LinkMenu extends React.Component { return linkItems; } - @action render() { const sourceDoc = this.props.docView.props.Document; const groups: Map = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc); @@ -90,7 +93,7 @@ export class LinkMenu extends React.Component {
4 ? "scroll" : "auto" + //overflowY: "scroll", }}> {!this._editingLink ? this.renderAllGroups(groups) : diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss index df8a3cadb..8578d0b75 100644 --- a/src/client/views/linking/LinkMenuItem.scss +++ b/src/client/views/linking/LinkMenuItem.scss @@ -103,7 +103,7 @@ width: 20px; height: 20px; margin: 0; - //margin-right: 6px; + margin-right: 4px; padding-right: 6px; border-radius: 50%; pointer-events: auto; diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 6af474513..59a88a1d9 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -78,7 +78,10 @@ export class LinkMenuItem extends React.Component { @action toggleShowMore(e: React.PointerEvent) { e.stopPropagation(); this._showMore = !this._showMore; } onEdit = (e: React.PointerEvent): void => { + + console.log("Edit"); LinkManager.currentLink = this.props.linkDoc; + console.log(this.props.linkDoc); setupMoveUpEvents(this, e, this.editMoved, emptyFunction, () => this.props.showEditor(this.props.linkDoc)); } -- cgit v1.2.3-70-g09d2 From 2b1af3fab4cc51f8cbb577ed51842cec1774a355 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Wed, 8 Jul 2020 13:22:49 -0500 Subject: restructured textbox comment, cleaned menu UI --- src/client/views/linking/LinkEditor.scss | 15 ++-- src/client/views/linking/LinkEditor.tsx | 3 +- src/client/views/linking/LinkMenu.scss | 71 ++++++++++------ src/client/views/linking/LinkMenu.tsx | 18 ++-- src/client/views/linking/LinkMenuItem.scss | 29 ++++--- src/client/views/linking/LinkMenuItem.tsx | 10 ++- .../formattedText/FormattedTextBoxComment.scss | 99 +++++++++------------- .../formattedText/FormattedTextBoxComment.tsx | 53 +++--------- 8 files changed, 143 insertions(+), 155 deletions(-) (limited to 'src') diff --git a/src/client/views/linking/LinkEditor.scss b/src/client/views/linking/LinkEditor.scss index 937427e46..87afc99eb 100644 --- a/src/client/views/linking/LinkEditor.scss +++ b/src/client/views/linking/LinkEditor.scss @@ -40,6 +40,10 @@ font-size: 9px; //font-style: italic; text-decoration-color: grey; + + .button { + color: black; + } } .linkEditor-description { @@ -61,7 +65,7 @@ //border-radius: 4px; padding-left: 2px; padding-right: 2px; - margin-right: 4px; + //margin-right: 4px; color: black; text-decoration-color: grey; } @@ -69,14 +73,14 @@ .linkEditor-description-add-button { display: inline; /* float: right; */ - border-radius: 9px; + border-radius: 7px; font-size: 9px; background-color: black; /* padding: 3px; */ padding-top: 4px; - padding-left: 3px; + padding-left: 7px; padding-bottom: 4px; - padding-right: 5px; + padding-right: 8px; height: 80%; color: white; } @@ -102,10 +106,11 @@ padding-left: 2px; padding-right: 2px; text-decoration-color: black; - color: black; + color: rgb(94, 94, 94); .linkEditor-followingDropdown-icon { float: right; + color: black; } } diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 7af11bca1..a26685318 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -339,6 +339,7 @@ export class LinkEditor extends React.Component {
{ destination.proto?.title ?? destination.title ?? "untitled"}

{/* */} - +
{this.showInfo ?
{this.props.linkDoc.author ?
Author: {this.props.linkDoc.author}
: null}
diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss index 422ab0430..4f7ac3275 100644 --- a/src/client/views/linking/LinkMenu.scss +++ b/src/client/views/linking/LinkMenu.scss @@ -1,37 +1,59 @@ @import "../globalCssVariables"; .linkMenu { - width: 100%; + width: auto; height: auto; - border: 1px solid black; - // &:hover { - // width: calc(auto + 26px); - // } -} + .linkMenu-list { -.linkMenu-list { + display: inline-block; - border: 1px solid black; + border: 1px solid black; - box-shadow: 3px 3px 1.5px grey; + box-shadow: 3px 3px 1.5px grey; - max-height: 170px; - overflow-y: scroll; - position: absolute; - z-index: 10; - background: white; - min-width: 150px; - //border-radius: 5px; - //padding-top: 6.5px; - //padding-bottom: 6.5px; - //padding-left: 6.5px; - //padding-right: 2px; - width: calc(auto + 50px); + max-height: 170px; + overflow-y: scroll; + position: relative; + z-index: 10; + background: white; + min-width: 170px; + //border-radius: 5px; + //padding-top: 6.5px; + //padding-bottom: 6.5px; + //padding-left: 6.5px; + //padding-right: 2px; + //width: calc(auto + 50px); - &:last-child { - border-bottom: none; + white-space: nowrap; + + overflow-x: hidden; + + &:last-child { + border-bottom: none; + } + + &:hover { + padding-right: 65px; + display: inline-block; + } + } + + .linkMenu-listEditor { + + display: inline-block; + + border: 1px solid black; + + box-shadow: 3px 3px 1.5px grey; + + max-height: 170px; + overflow-y: scroll; + position: relative; + z-index: 10; + background: white; + min-width: 170px; } } @@ -39,6 +61,7 @@ border-bottom: 0.5px solid lightgray; //@extend: 5px 0; + &:last-child { border-bottom: none; } @@ -54,7 +77,7 @@ } p.expand-one { - width: calc(100% + 26px); + width: calc(100% + 20px); } .linkEditor-tableButton { diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index 478ac3fe6..234cd5e07 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -90,16 +90,16 @@ export class LinkMenu extends React.Component { const sourceDoc = this.props.docView.props.Document; const groups: Map = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc); return
-
- {!this._editingLink ? - this.renderAllGroups(groups) : + {!this._editingLink ?
+ {this.renderAllGroups(groups)} +
:
this._editingLink = undefined)} /> - } -
; +
+ } + +
; } } \ No newline at end of file diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss index 8578d0b75..9f1f82ce2 100644 --- a/src/client/views/linking/LinkMenuItem.scss +++ b/src/client/views/linking/LinkMenuItem.scss @@ -26,6 +26,8 @@ color: rgb(85, 120, 196); font-size: 14px; padding-bottom: 2px; + padding-right: 4px; + margin-right: 4px; } .linkMenu-description { @@ -36,12 +38,16 @@ } p { - //padding: 4px 2px; + padding-right: 4px; line-height: 12px; border-radius: 5px; - overflow-wrap: break-word; + //overflow-wrap: break-word; user-select: none; } + + &:hover { + padding-right: 8px; + } } } @@ -60,6 +66,7 @@ &:hover { + width: calc(100% + 58px); .linkMenu-item-buttons { display: flex; @@ -74,19 +81,13 @@ text-overflow: break; } - &.expand-two p { - width: calc(100% - 52px); - //text-decoration: underline; - //color: rgb(15, 57, 148); - //background-color: lightgray; - } + // &.expand-two p { + // width: calc(100% - 63px); + // } - &.expand-three p { - width: calc(100% - 84px); - //text-decoration: underline; - //color: rgb(15, 57, 148); - //background-color: lightgray; - } + // &.expand-three p { + // width: calc(100% - 93px); + // } } } } diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 59a88a1d9..57993d240 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes, faPencilAlt } from '@fortawesome/free-solid-svg-icons'; +import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes, faPencilAlt, faEyeSlash } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, observable } from 'mobx'; import { observer } from "mobx-react"; @@ -15,7 +15,7 @@ import { setupMoveUpEvents, emptyFunction } from '../../../Utils'; import { DocumentView } from '../nodes/DocumentView'; import { DocumentLinksButton } from '../nodes/DocumentLinksButton'; import { LinkDocPreview } from '../nodes/LinkDocPreview'; -library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp, faPencilAlt); +library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp, faPencilAlt, faEyeSlash); interface LinkMenuItemProps { @@ -179,6 +179,8 @@ export class LinkMenuItem extends React.Component { const keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType); const canExpand = keys ? keys.length > 0 : false; + const eyeIcon = this.props.linkDoc.shown ? "eye-slash" : "eye"; + return (
@@ -203,6 +205,10 @@ export class LinkMenuItem extends React.Component { {canExpand ?
this.toggleShowMore(e)}>
: <>} +
+
+ +
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss index 9089e7039..286ccf22d 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss @@ -8,6 +8,45 @@ margin-bottom: 7px; -webkit-transform: translateX(-50%); transform: translateX(-50%); + box-shadow: 3px 3px 1.5px grey; + + .FormattedTextBoxComment-title { + background-color: white; + border: 8px solid white; + + .FormattedTextBoxComment-button { + display: inline; + padding-left: 6px; + padding-right: 6px; + padding-top: 2.5px; + padding-bottom: 2.5px; + width: 20px; + height: 20px; + margin: 0; + margin-right: 6px; + border-radius: 50%; + pointer-events: auto; + background-color: rgb(0, 0, 0); + color: rgb(255, 255, 255); + transition: transform 0.2s; + text-align: center; + position: relative; + font: 10px; + + &:hover { + background-color: rgb(77, 77, 77); + cursor: grab; + } + } + + .FormattedTextBoxComment-preview-wrapper { + max-width: 180px; + max-height: 168px; + overflow: hidden; + overflow-y: hidden; + padding-top: 5px; + } + } } .FormattedTextBox-tooltip:before { @@ -42,64 +81,4 @@ top: 50%; right: 0; transform: translateY(-50%); - - .FormattedTextBoxComment-button { - width: 20px; - height: 20px; - margin: 0; - margin-right: 6px; - border-radius: 50%; - pointer-events: auto; - background-color: rgb(38, 40, 41); - color: rgb(178, 181, 184); - font-size: 65%; - transition: transform 0.2s; - text-align: center; - position: relative; - - // margin-top: "auto"; - // margin-bottom: "auto"; - // background: black; - // color: white; - // display: inline-block; - // border-radius: 18px; - // font-size: 12.5px; - // width: 18px; - // height: 18px; - // margin-top: auto; - // margin-bottom: auto; - // margin-right: 3px; - // cursor: pointer; - // transition: transform 0.2s; - - .FormattedTextBoxComment-fa-icon { - margin-top: "auto"; - margin-bottom: "auto"; - background: black; - color: white; - display: inline-block; - border-radius: 18px; - font-size: 12.5px; - width: 18px; - height: 18px; - margin-top: auto; - margin-bottom: auto; - margin-right: 3px; - cursor: pointer; - transition: transform 0.2s; - // position: absolute; - // top: 50%; - // left: 50%; - // transform: translate(-50%, -50%); - } - - &:last-child { - margin-right: 0; - } - - &:hover { - background: rgb(53, 146, 199); - ; - } - } } \ No newline at end of file diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index 56826e5c7..79b09b374 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -241,52 +241,22 @@ export class FormattedTextBoxComment { } if (target?.author) { FormattedTextBoxComment.showCommentbox("", view, nbef); - const docPreview =
+ + + const docPreview =
{target.title}
-
this._deleteRef = r}> - this._deleteRef = r}> +
-
this._followRef = r}> - this._followRef = r}> +
-
@@ -318,6 +288,9 @@ export class FormattedTextBoxComment { />
; + + + FormattedTextBoxComment.showCommentbox("", view, nbef); ReactDOM.render(docPreview, FormattedTextBoxComment.tooltipText); -- cgit v1.2.3-70-g09d2 From 3beca8e7d268dc7b67b20b2c8760ea5e4b6fdb88 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Wed, 8 Jul 2020 18:59:37 -0500 Subject: added dashDragging event --- src/client/util/DragManager.ts | 48 ++++++++++++++++++++++ .../collectionFreeForm/CollectionFreeFormView.tsx | 15 +++++++ src/client/views/nodes/DocumentLinksButton.tsx | 2 +- 3 files changed, 64 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 2ceafff30..64c3d8458 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -427,6 +427,54 @@ export namespace DragManager { }, dragData.droppedDocuments); } + const target = document.elementFromPoint(e.x, e.y); + + const complete = new DragCompleteEvent(false, dragData); + + if (target) { + target.dispatchEvent( + new CustomEvent("dashDragging", { + bubbles: true, + detail: { + shiftKey: e.shiftKey, + altKey: e.altKey, + metaKey: e.metaKey, + ctrlKey: e.ctrlKey, + clientX: e.clientX, + clientY: e.clientY, + dataTransfer: new DataTransfer, + button: e.button, + buttons: e.buttons, + getModifierState: e.getModifierState, + movementX: e.movementX, + movementY: e.movementY, + pageX: e.pageX, + pageY: e.pageY, + relatedTarget: e.relatedTarget, + screenX: e.screenX, + screenY: e.screenY, + detail: e.detail, + view: e.view ? e.view : new Window, + nativeEvent: new DragEvent("dashDragging"), + currentTarget: target, + target: target, + bubbles: true, + cancelable: true, + defaultPrevented: true, + eventPhase: e.eventPhase, + isTrusted: true, + preventDefault: e.preventDefault, + isDefaultPrevented: () => true, + stopPropagation: e.stopPropagation, + isPropagationStopped: () => true, + persist: emptyFunction, + timeStamp: e.timeStamp, + type: "dashDragging" + } + }) + ); + } + const { thisX, thisY } = snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom); const moveX = thisX - lastX; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9bf425db2..5a762a85c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1143,10 +1143,19 @@ export class CollectionFreeFormView extends CollectionSubView this.doLayoutComputation, (elements) => this._layoutElements = elements || [], { fireImmediately: true, name: "doLayout" }); + + const handler = (e: Event) => this.handleDragging(e, (e as CustomEvent).detail); + + document.addEventListener("dashDragging", handler); } + componentWillUnmount() { this._layoutComputeReaction?.(); + + const handler = (e: Event) => this.handleDragging(e, (e as CustomEvent).detail); + document.removeEventListener("dashDragging", handler); } + @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); } elementFunc = () => this._layoutElements; @@ -1155,6 +1164,12 @@ export class CollectionFreeFormView extends CollectionSubView { + console.log(de.clientX); + console.log(de.clientX); + } + promoteCollection = undoBatch(action(() => { const childDocs = this.childDocs.slice(); childDocs.forEach(doc => { diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 7fb447cab..44e72215d 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -152,7 +152,7 @@ export class DocumentLinksButton extends React.Component
Date: Thu, 9 Jul 2020 01:54:29 -0500 Subject: started dragging pans screen (needs cleaning) --- src/client/util/LinkManager.ts | 2 + .../collectionFreeForm/CollectionFreeFormView.tsx | 105 ++++++++++++++++++--- 2 files changed, 96 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 6da581f35..50f3fc1d6 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -3,6 +3,7 @@ import { List } from "../../fields/List"; import { listSpec } from "../../fields/Schema"; import { Cast, StrCast } from "../../fields/Types"; import { Scripting } from "./Scripting"; +import { undoBatch } from "./UndoManager"; /* * link doc: @@ -52,6 +53,7 @@ export class LinkManager { return false; } + @undoBatch public deleteLink(linkDoc: Doc): boolean { if (LinkManager.Instance.LinkManagerDoc && linkDoc instanceof Doc) { Doc.RemoveDocFromList(LinkManager.Instance.LinkManagerDoc, "data", linkDoc); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5a762a85c..a3d3a210d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -101,6 +101,10 @@ export class CollectionFreeFormView extends CollectionSubView(); + @observable _marqueeRef = React.createRef(); + @observable canPanX: boolean = true; + @observable canPanY: boolean = true; + @computed get fitToContentScaling() { return this.fitToContent ? NumCast(this.layoutDoc.fitToContentScaling, 1) : 1; } @computed get fitToContent() { return (this.props.fitToBox || this.Document._fitToBox) && !this.isAnnotationOverlay; } @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; } @@ -1164,10 +1168,87 @@ export class CollectionFreeFormView extends CollectionSubView + @action handleDragging = (e: Event, de: DragEvent) => { - console.log(de.clientX); - console.log(de.clientX); + + const nw = NumCast(this.Document._nativeWidth, this.props.NativeWidth()); + const nh = NumCast(this.Document._nativeHeight, this.props.NativeHeight()); + const hscale = nh ? this.props.PanelHeight() / nh : 1; + const wscale = nw ? this.props.PanelWidth() / nw : 1; + + if (this._marqueeRef) { + if (this._marqueeRef.current) { + + // console.log("left: " + this._marqueeRef.current.clientLeft); + // console.log("width: " + this._marqueeRef.current.clientWidth); + // console.log("client x: " + de.clientX); + + // console.log("top: " + this._marqueeRef.current.clientTop); + // console.log("height: " + this._marqueeRef.current.clientHeight); + // console.log("client y: " + de.clientY); + + + if (this._marqueeRef.current.clientWidth > 0) { + if (de.clientX - 315 - this._marqueeRef.current.clientLeft < 25) { + console.log("PAN left "); + if (this.canPanX) { + this.Document._panX = de.clientX - 20 - this._marqueeRef.current.clientLeft; + setTimeout(action(() => { + this.canPanX = true; + this.panX(); + }), 2500); + this.canPanX = false; + } + } else if (de.clientX - 315 - this._marqueeRef.current.clientLeft - + this._marqueeRef.current.clientWidth < 25) { + console.log("PAN right "); + if (this.canPanX) { + this.Document._panX = de.clientX - 315 - this._marqueeRef.current.clientLeft - + this._marqueeRef.current.clientWidth; + + setTimeout(action(() => { + this.panX(); + this.canPanX = true; + }), 2500); + this.canPanX = false; + } + + } + } + + if (this._marqueeRef.current.clientHeight > 0) { + if (de.clientY - 120 - this._marqueeRef.current.clientTop < 25) { + console.log("PAN top "); + if (this.canPanY) { + this.Document._panY = de.clientY - 20 - this._marqueeRef.current.clientTop; + setTimeout(action(() => { + this.canPanY = true; + this.panY(); + }), 2500); + this.canPanY = false; + } + } else if (de.clientY - 120 - this._marqueeRef.current.clientTop - + this._marqueeRef.current.clientHeight < 25) { + console.log("PAN bottom "); + if (this.canPanY) { + this.Document._panY = de.clientY - 120 - this._marqueeRef.current.clientTop - + this._marqueeRef.current.clientHeight; + + setTimeout(action(() => { + this.panY(); + this.canPanY = true; + }), 2500); + this.canPanY = false; + } + + } + + } + } + } } promoteCollection = undoBatch(action(() => { @@ -1351,7 +1432,8 @@ export class CollectionFreeFormView extends CollectionSubView - - {this.children} - +
+ + {this.children} +
{this.showTimeline ? : (null)} ; } -- cgit v1.2.3-70-g09d2 From 12285b8c0aff514a2345874508c35b6de1026ef4 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Thu, 9 Jul 2020 12:25:46 +0530 Subject: acls now work I think + some cleanup --- src/client/DocServer.ts | 1 - src/client/util/GroupManager.tsx | 13 +++- src/client/util/GroupMemberView.tsx | 2 +- src/client/util/SharingManager.tsx | 2 - src/client/views/DocComponent.tsx | 1 - src/client/views/GlobalKeyHandler.ts | 2 + src/client/views/collections/CollectionView.tsx | 21 +++--- src/client/views/nodes/DocumentContentsView.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 22 +++--- .../views/nodes/formattedText/FormattedTextBox.tsx | 1 - src/fields/Doc.ts | 24 +------ src/fields/util.ts | 84 +++++++++++----------- 12 files changed, 84 insertions(+), 90 deletions(-) (limited to 'src') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 860a8fd92..bac324c77 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -40,7 +40,6 @@ export namespace DocServer { export var PlaygroundFields: string[]; export function setPlaygroundFields(livePlaygroundFields: string[]) { - console.log("here"); DocServer.PlaygroundFields = livePlaygroundFields; livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground)); } diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 83b206f94..23bdd248b 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -14,6 +14,7 @@ import Select from 'react-select'; import "./GroupManager.scss"; import { StrCast } from "../../fields/Types"; import GroupMemberView from "./GroupMemberView"; +import { setGroups } from "../../fields/util"; library.add(fa.faWindowClose); @@ -54,7 +55,7 @@ export default class GroupManager extends React.Component<{}> { if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(StrCast(group.groupName)); }); }) - .finally(() => console.log(this.currentUserGroups)); + .finally(() => setGroups(this.currentUserGroups)); // (this.GroupManagerDoc?.data as List).forEach(group => { // Promise.resolve(group).then(resolvedGroup => { @@ -178,6 +179,10 @@ export default class GroupManager extends React.Component<{}> { groupDoc.groupName = groupName; groupDoc.owners = JSON.stringify([Doc.CurrentUserEmail]); groupDoc.members = JSON.stringify(memberEmails); + if (memberEmails.includes(Doc.CurrentUserEmail)) { + this.currentUserGroups.push(groupName); + setGroups(this.currentUserGroups); + } this.addGroup(groupDoc); } @@ -204,6 +209,12 @@ export default class GroupManager extends React.Component<{}> { // SharingManager.Instance.setInternalGroupSharing(group, "Not Shared"); Doc.RemoveDocFromList(this.GroupManagerDoc, "data", group); SharingManager.Instance.removeGroup(group); + const members: string[] = JSON.parse(StrCast(group.members)); + if (members.includes(Doc.CurrentUserEmail)) { + const index = this.currentUserGroups.findIndex(groupName => groupName === group.groupName); + index !== -1 && this.currentUserGroups.splice(index, 1); + setGroups(this.currentUserGroups); + } if (group === this.currentGroup) { runInAction(() => this.currentGroup = undefined); } diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index cc279b6b2..742caa676 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -51,7 +51,7 @@ export default class GroupMemberView extends React.Component
- +
: null}
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index bec6b973b..d64302456 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -359,8 +359,6 @@ export default class SharingManager extends React.Component<{}> { share = () => { this.selectedUsers?.forEach(user => { if (user.value.includes(indType)) { - console.log(user); - console.log(this.users.find(u => u.user.email === user.label)); this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions); } else { diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index e8c34d931..781673e59 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -138,7 +138,6 @@ export function ViewBoxAnnotatableComponent

!docList.includes(d)); - console.log("here"); const effectiveAcl = getEffectiveAcl(this.dataDoc); if (added.length) { if (effectiveAcl === AclReadonly) { diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index a3a023164..45d53a5f5 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -22,6 +22,7 @@ import { DocumentView } from "./nodes/DocumentView"; import { DocumentLinksButton } from "./nodes/DocumentLinksButton"; import PDFMenu from "./pdf/PDFMenu"; import { ContextMenu } from "./ContextMenu"; +import GroupManager from "../util/GroupManager"; const modifiers = ["control", "meta", "shift", "alt"]; type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise; @@ -107,6 +108,7 @@ export default class KeyManager { GoogleAuthenticationManager.Instance.cancel(); HypothesisAuthenticationManager.Instance.cancel(); SharingManager.Instance.close(); + GroupManager.Instance.close(); break; case "delete": case "backspace": diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 31f0c1df3..6a6a475c8 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -8,7 +8,7 @@ import * as React from 'react'; import Lightbox from 'react-image-lightbox-with-rotate'; import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app import { DateField } from '../../../fields/DateField'; -import { AclAddonly, AclReadonly, AclSym, DataSym, Doc, DocListCast, Field, Opt } from '../../../fields/Doc'; +import { AclAddonly, AclReadonly, AclSym, DataSym, Doc, DocListCast, Field, Opt, AclEdit } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; @@ -132,8 +132,7 @@ export class CollectionView extends Touchable !docList.includes(d)); - console.log("here"); - const effectiveAcl = getEffectiveAcl(this.dataDoc); + const effectiveAcl = getEffectiveAcl(this.props.Document); if (added.length) { if (effectiveAcl === AclReadonly) { return false; @@ -167,13 +166,15 @@ export class CollectionView extends Touchable { - const docs = doc instanceof Doc ? [doc] : doc as Doc[]; - const targetDataDoc = this.props.Document[DataSym]; - const value = DocListCast(targetDataDoc[this.props.fieldKey]); - const result = value.filter(v => !docs.includes(v)); - if (result.length !== value.length) { - targetDataDoc[this.props.fieldKey] = new List(result); - return true; + if (getEffectiveAcl(this.props.Document) === AclEdit) { + const docs = doc instanceof Doc ? [doc] : doc as Doc[]; + const targetDataDoc = this.props.Document[DataSym]; + const value = DocListCast(targetDataDoc[this.props.fieldKey]); + const result = value.filter(v => !docs.includes(v)); + if (result.length !== value.length) { + targetDataDoc[this.props.fieldKey] = new List(result); + return true; + } } return false; } diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index d480c76d0..e34ceb994 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -184,7 +184,6 @@ export class DocumentContentsView extends React.Component 1 ? splits[0] + splits[1].replace(/{([^{}]|(?R))*}/, replacer4) : ""; // might have been more elegant if javascript supported recursive patterns - console.log("here"); return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc || getEffectiveAcl(this.layoutDoc) === AclPrivate) ? (null) : (Docu @undoBatch @action - setAcl = (acl: "readOnly" | "addOnly" | "ownerOnly" | "write") => { + setAcl = (acl: SharingPermissions) => { this.dataDoc.ACL = this.props.Document.ACL = acl; DocListCast(this.dataDoc[Doc.LayoutFieldKey(this.dataDoc)]).map(d => { if (d.author === Doc.CurrentUserEmail) d.ACL = acl; @@ -735,7 +735,7 @@ export class DocumentView extends DocComponent(Docu } @undoBatch @action - testAcl = (acl: "readOnly" | "addOnly" | "ownerOnly" | "write") => { + testAcl = (acl: SharingPermissions) => { this.dataDoc.author = this.props.Document.author = "ADMIN"; this.dataDoc.ACL = this.props.Document.ACL = acl; DocListCast(this.dataDoc[Doc.LayoutFieldKey(this.dataDoc)]).map(d => { @@ -845,12 +845,12 @@ export class DocumentView extends DocComponent(Docu const existingAcls = cm.findByDescription("Privacy..."); const aclItems: ContextMenuProps[] = existingAcls && "subitems" in existingAcls ? existingAcls.subitems : []; - aclItems.push({ description: "Make Add Only", event: () => this.setAcl("addOnly"), icon: "concierge-bell" }); - aclItems.push({ description: "Make Read Only", event: () => this.setAcl("readOnly"), icon: "concierge-bell" }); - aclItems.push({ description: "Make Private", event: () => this.setAcl("ownerOnly"), icon: "concierge-bell" }); - aclItems.push({ description: "Make Editable", event: () => this.setAcl("write"), icon: "concierge-bell" }); - aclItems.push({ description: "Test Private", event: () => this.testAcl("ownerOnly"), icon: "concierge-bell" }); - aclItems.push({ description: "Test Readonly", event: () => this.testAcl("readOnly"), icon: "concierge-bell" }); + aclItems.push({ description: "Make Add Only", event: () => this.setAcl(SharingPermissions.Add), icon: "concierge-bell" }); + aclItems.push({ description: "Make Read Only", event: () => this.setAcl(SharingPermissions.View), icon: "concierge-bell" }); + aclItems.push({ description: "Make Private", event: () => this.setAcl(SharingPermissions.None), icon: "concierge-bell" }); + aclItems.push({ description: "Make Editable", event: () => this.setAcl(SharingPermissions.Edit), icon: "concierge-bell" }); + aclItems.push({ description: "Test Private", event: () => this.testAcl(SharingPermissions.None), icon: "concierge-bell" }); + aclItems.push({ description: "Test Readonly", event: () => this.testAcl(SharingPermissions.View), icon: "concierge-bell" }); !existingAcls && cm.addItem({ description: "Privacy...", subitems: aclItems, icon: "question" }); // const recommender_subitems: ContextMenuProps[] = []; @@ -1206,9 +1206,9 @@ export class DocumentView extends DocComponent(Docu } render() { - console.log("here"); - if (getEffectiveAcl(this.props.Document) === AclPrivate) return (null); if (!(this.props.Document instanceof Doc)) return (null); + if (getEffectiveAcl(this.props.Document) === AclPrivate) return (null); + if (this.props.Document.hidden) return (null); const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document); const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null))); const finalOpacity = this.props.opacity ? this.props.opacity() : opacity; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index a0dbcd980..ccf83cbf9 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -227,7 +227,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template const json = JSON.stringify(state.toJSON()); // if (!this.dataDoc[AclSym]) { // what? - console.log("here"); if (getEffectiveAcl(this.dataDoc) === AclEdit) { if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) { this._applyingChange = true; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index c965dc282..27eabf451 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -109,7 +109,6 @@ const AclMap = new Map([ ]); export function fetchProto(doc: Doc) { - console.log("in fetchproto"); if (doc.author !== Doc.CurrentUserEmail) { // storing acls for groups needs to be extended here - AclSym should store a datastructure that stores information about permissions const permissions: { [key: string]: symbol } = {}; @@ -118,22 +117,8 @@ export function fetchProto(doc: Doc) { if (key.startsWith("ACL")) permissions[key] = AclMap.get(StrCast(doc[key]))!; }); - doc[AclSym] = permissions; - // const acl = Doc.Get(doc, "ACL", true); - // switch (acl) { - // case "ownerOnly": - // doc[AclSym] = AclPrivate; - // return undefined; - // case "readOnly": - // doc[AclSym] = AclReadonly; - // break; - // case "addOnly": - // doc[AclSym] = AclAddonly; - // break; - // // case "edit": - // // doc[AclSym] = AclEdit; - // } + if (Object.keys(permissions).length) doc[AclSym] = permissions; } if (doc.proto instanceof Promise) { @@ -208,7 +193,7 @@ export class Doc extends RefField { private [Self] = this; private [SelfProxy]: any; - public [AclSym]: any; + public [AclSym]: { [key: string]: symbol }; public [WidthSym] = () => NumCast(this[SelfProxy]._width); public [HeightSym] = () => NumCast(this[SelfProxy]._height); public [ToScriptString]() { return `DOC-"${this[Self][Id]}"-`; } @@ -232,8 +217,8 @@ export class Doc extends RefField { return Cast(this[SelfProxy][renderFieldKey + "-layout[" + templateLayoutDoc[Id] + "]"], Doc, null) || templateLayoutDoc; } return undefined; - } + } private [CachedUpdates]: { [key: string]: () => void | Promise } = {}; public static CurrentUserEmail: string = ""; @@ -840,7 +825,6 @@ export namespace Doc { } // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message) export function IsBrushedDegreeUnmemoized(doc: Doc) { - console.log("here"); if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return 0; return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? 1 : 0; } @@ -850,7 +834,6 @@ export namespace Doc { })(doc); } export function BrushDoc(doc: Doc) { - console.log("here"); if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc; brushManager.BrushedDoc.set(doc, true); brushManager.BrushedDoc.set(Doc.GetProto(doc), true); @@ -888,7 +871,6 @@ export namespace Doc { } const highlightManager = new HighlightBrush(); export function IsHighlighted(doc: Doc) { - console.log("here"); if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return false; return highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetProto(doc)); } diff --git a/src/fields/util.ts b/src/fields/util.ts index a3e7a36f8..a1af1d3c5 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -1,5 +1,5 @@ import { UndoManager } from "../client/util/UndoManager"; -import { Doc, Field, FieldResult, UpdatingFromServer, LayoutSym, AclSym, AclPrivate, AclEdit, AclReadonly, AclAddonly } from "./Doc"; +import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, fetchProto } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; @@ -9,7 +9,6 @@ import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols"; import { DocServer } from "../client/DocServer"; import { ComputedField } from "./ScriptField"; import { ScriptCast } from "./Types"; -import GroupManager from "../client/util/GroupManager"; function _readOnlySetter(): never { @@ -108,57 +107,65 @@ export function OVERRIDE_ACL(val: boolean) { _overrideAcl = val; } -const HierarchyMapping = new Map([ - [AclPrivate, 0], - [AclReadonly, 1], - [AclAddonly, 2], - [AclEdit, 3] -]); +let currentUserGroups: string[] = []; +let currentUserEmail: string;// = Doc.CurrentUserEmail; -export function getEffectiveAcl(target: any): symbol { +export function setGroups(groups: string[]) { + currentUserGroups = groups; + currentUserEmail = Doc.CurrentUserEmail; +} - console.log("in getEffectiveAcl"); - if (target[AclSym].ACL) return target[AclSym].ACL; +export function getEffectiveAcl(target: any, in_prop?: string | symbol | number): symbol { - let effectiveAcl = AclEdit; + const HierarchyMapping = new Map([ + [AclPrivate, 0], + [AclReadonly, 1], + [AclAddonly, 2], + [AclEdit, 3] + ]); - for (const [key, value] of Object.entries(target[AclSym])) { - if (key.startsWith("ACL-")) { - if (GroupManager.Instance.currentUserGroups.includes(key.substring(4)) || Doc.CurrentUserEmail === key.substring(4).replace("_", ".")) { - if (HierarchyMapping.get(value as symbol)! > HierarchyMapping.get(effectiveAcl)!) { - effectiveAcl = value as symbol; - if (effectiveAcl === AclEdit) break; - } - } - } + if (!target[AclSym] && target instanceof Doc) { + fetchProto(target); } - return effectiveAcl; -} + if (target[AclSym] && Object.keys(target[AclSym]).length) { + + if (target.author === currentUserEmail) return AclEdit; + + if (_overrideAcl || (in_prop && DocServer.PlaygroundFields?.includes(in_prop.toString()))) return AclEdit; -function testPermission(target: any, in_prop: string | symbol | number): boolean { + if (target[AclSym].ACL) return target[AclSym].ACL; - console.log("here"); - // if (target[AclSym].ACL !== AclEdit && !_overrideAcl && !DocServer.PlaygroundFields.includes(in_prop.toString())) return false; - if (target[AclSym].ACL === AclEdit) return true; - for (const [key, value] of Object.entries(target[AclSym])) { - if (key.startsWith("ACL-")) { - if (GroupManager.Instance.currentUserGroups.includes(key.substring(4))) { - if (value === AclEdit) return true; + let effectiveAcl = AclPrivate; + let aclPresent = false; + + for (const [key, value] of Object.entries(target[AclSym])) { + if (key.startsWith("ACL-")) { + if (currentUserGroups.includes(key.substring(4)) || currentUserEmail === key.substring(4).replace("_", ".")) { + if (HierarchyMapping.get(value as symbol)! >= HierarchyMapping.get(effectiveAcl)!) { + aclPresent = true; + effectiveAcl = value as symbol; + if (effectiveAcl === AclEdit) break; + } + } } } + return aclPresent ? effectiveAcl : AclEdit; + } + else { + return AclEdit; } - return _overrideAcl || DocServer.PlaygroundFields.includes(in_prop.toString()); } + const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"]; export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean { - console.log("in setter") let prop = in_prop; - // if (target[AclSym] && !_overrideAcl && !DocServer.PlaygroundFields.includes(in_prop.toString())) return true; // generalise to a testpermission function - if (!testPermission(target, in_prop)) return true; + if (getEffectiveAcl(target, in_prop) !== AclEdit) { + return true; + } if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) { if (!prop.startsWith("_")) { console.log(prop + " is deprecated - switch to _" + prop); @@ -176,11 +183,9 @@ export function setter(target: any, in_prop: string | symbol | number, value: an } export function getter(target: any, in_prop: string | symbol | number, receiver: any): any { - console.log("in getter") let prop = in_prop; - const effectiveAcl = getEffectiveAcl(target); - if (in_prop === AclSym) return _overrideAcl ? undefined : effectiveAcl; - if (effectiveAcl === AclPrivate && !_overrideAcl) return undefined; + if (in_prop === AclSym) return _overrideAcl ? undefined : target[AclSym]; + if (getEffectiveAcl(target) === AclPrivate && !_overrideAcl) return undefined; if (prop === LayoutSym) { return target.__LAYOUT__; } @@ -217,7 +222,6 @@ function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreP } if (field === undefined && !ignoreProto && prop !== "proto") { const proto = getFieldImpl(target, "proto", receiver, true);//TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters - console.log("here"); if (proto instanceof Doc && getEffectiveAcl(proto) !== AclPrivate) { return getFieldImpl(proto[Self], prop, receiver, ignoreProto); } -- cgit v1.2.3-70-g09d2 From d70c9004215aea00514030be4137ccc2247b541a Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Thu, 9 Jul 2020 12:42:20 +0530 Subject: removed some unnecessary imports --- src/client/util/SharingManager.tsx | 2 +- src/client/views/DocComponent.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 3 +-- 6 files changed, 6 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index d64302456..fd3b2dd04 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -1,7 +1,7 @@ import { observable, runInAction, action } from "mobx"; import * as React from "react"; import MainViewModal from "../views/MainViewModal"; -import { Doc, Opt, DocCastAsync, DocListCast } from "../../fields/Doc"; +import { Doc, Opt, DocListCast } from "../../fields/Doc"; import { DocServer } from "../DocServer"; import { Cast, StrCast } from "../../fields/Types"; import * as RequestPromise from "request-promise"; diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 781673e59..43ffe225f 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -1,4 +1,4 @@ -import { Doc, Opt, DataSym, DocListCast, AclSym, AclReadonly, AclAddonly } from '../../fields/Doc'; +import { Doc, Opt, DataSym, DocListCast, AclReadonly, AclAddonly } from '../../fields/Doc'; import { Touchable } from './Touchable'; import { computed, action, observable } from 'mobx'; import { Cast, BoolCast, ScriptCast } from '../../fields/Types'; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 6a6a475c8..032012b2d 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -8,7 +8,7 @@ import * as React from 'react'; import Lightbox from 'react-image-lightbox-with-rotate'; import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app import { DateField } from '../../../fields/DateField'; -import { AclAddonly, AclReadonly, AclSym, DataSym, Doc, DocListCast, Field, Opt, AclEdit } from '../../../fields/Doc'; +import { AclAddonly, AclReadonly, DataSym, Doc, DocListCast, Field, Opt, AclEdit } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index e34ceb994..1e6966c05 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -1,6 +1,6 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc, Opt, Field, AclSym, AclPrivate } from "../../../fields/Doc"; +import { Doc, Opt, Field, AclPrivate } from "../../../fields/Doc"; import { Cast, StrCast, NumCast } from "../../../fields/Types"; import { OmitKeys, Without, emptyPath } from "../../../Utils"; import { DirectoryImportBox } from "../../util/Import & Export/DirectoryImportBox"; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b9ae8b444..9dc30c683 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -4,7 +4,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as rp from "request-promise"; -import { Doc, DocListCast, HeightSym, Opt, WidthSym, DataSym, AclSym, AclReadonly, AclPrivate } from "../../../fields/Doc"; +import { Doc, DocListCast, HeightSym, Opt, WidthSym, DataSym, AclPrivate } from "../../../fields/Doc"; import { Document } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index ccf83cbf9..aa89b4a10 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -13,7 +13,7 @@ import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from " import { ReplaceStep } from 'prosemirror-transform'; import { EditorView } from "prosemirror-view"; import { DateField } from '../../../../fields/DateField'; -import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, AclSym, AclEdit } from "../../../../fields/Doc"; +import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, AclEdit } from "../../../../fields/Doc"; import { documentSchema } from '../../../../fields/documentSchemas'; import applyDevTools = require("prosemirror-dev-tools"); import { removeMarkWithAttrs } from "./prosemirrorPatches"; @@ -226,7 +226,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template const json = JSON.stringify(state.toJSON()); - // if (!this.dataDoc[AclSym]) { // what? if (getEffectiveAcl(this.dataDoc) === AclEdit) { if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) { this._applyingChange = true; -- cgit v1.2.3-70-g09d2 From bc0d6410ac42af595cea1fb242e10e464da321ae Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Thu, 9 Jul 2020 20:41:28 +0530 Subject: change DocListCast to async in SharingManager + prevented textbox from showing up on click --- src/client/util/SharingManager.tsx | 71 +++++++++++----------- src/client/views/DocComponent.tsx | 4 +- src/client/views/collections/CollectionView.tsx | 6 +- .../collections/collectionFreeForm/MarqueeView.tsx | 6 +- src/client/views/nodes/DocumentContentsView.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 4 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 +- src/fields/Doc.ts | 16 ++--- src/fields/util.ts | 24 +++----- 9 files changed, 70 insertions(+), 69 deletions(-) (limited to 'src') diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index fd3b2dd04..af68edab6 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -1,7 +1,7 @@ import { observable, runInAction, action } from "mobx"; import * as React from "react"; import MainViewModal from "../views/MainViewModal"; -import { Doc, Opt, DocListCast } from "../../fields/Doc"; +import { Doc, Opt, DocListCastAsync } from "../../fields/Doc"; import { DocServer } from "../DocServer"; import { Cast, StrCast } from "../../fields/Types"; import * as RequestPromise from "request-promise"; @@ -43,19 +43,6 @@ export enum SharingPermissions { // [SharingPermissions.Edit, "green"] // ]); -// export const HierarchyMapping = new Map([ -// [SharingPermissions.None, 0], -// [SharingPermissions.View, 1], -// [SharingPermissions.Add, 2], -// [SharingPermissions.Edit, 3] - -// // ["0", SharingPermissions.None], -// // ["1", SharingPermissions.View], -// // ["2", SharingPermissions.Add], -// // ["3", SharingPermissions.Edit] - -// ]); - interface GroupOptions { label: string; options: UserOptions[]; @@ -88,8 +75,6 @@ export default class SharingManager extends React.Component<{}> { @observable private overlayOpacity = 0.4; @observable private selectedUsers: UserOptions[] | null = null; @observable private permissions: SharingPermissions = SharingPermissions.Edit; - @observable private sharedUsers: ValidatedUser[] = []; - @observable private sharedGroups: Doc[] = []; // private get linkVisible() { // return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; @@ -162,13 +147,16 @@ export default class SharingManager extends React.Component<{}> { target[ACL] = permission; - group.docsShared ? Doc.IndexOf(target, DocListCast(group.docsShared)) === -1 && (group.docsShared as List).push(target) : group.docsShared = new List([target]); + group.docsShared ? DocListCastAsync(group.docsShared).then(resolved => Doc.IndexOf(target, resolved!) === -1 && (group.docsShared as List).push(target)) : group.docsShared = new List([target]); + // group.docsShared ? Doc.IndexOf(target, DocListCast(group.docsShared)) === -1 && (group.docsShared as List).push(target) : group.docsShared = new List([target]); users.forEach(({ notificationDoc }) => { - if (permission !== SharingPermissions.None) Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target); - else Doc.IndexOf(target, DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); - + DocListCastAsync(notificationDoc[storage]).then(res => console.log(res)); + DocListCastAsync(notificationDoc[storage]).then(resolved => { + if (permission !== SharingPermissions.None) Doc.IndexOf(target, resolved!) === -1 && Doc.AddDocToList(notificationDoc, storage, target); + else Doc.IndexOf(target, resolved!) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); + }); }); } @@ -176,7 +164,12 @@ export default class SharingManager extends React.Component<{}> { const user: ValidatedUser = this.users.find(user => user.user.email === email)!; if (group.docsShared) { - DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc)); + DocListCastAsync(group.docsShared).then(docsShared => { + docsShared?.forEach(doc => { + DocListCastAsync(user.notificationDoc[storage]).then(resolved => Doc.IndexOf(doc, resolved!) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc)); + }); + }); + // DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc)); } } @@ -184,20 +177,28 @@ export default class SharingManager extends React.Component<{}> { const user: ValidatedUser = this.users.find(user => user.user.email === email)!; if (group.docsShared) { - DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(user.notificationDoc, storage, doc)); + DocListCastAsync(group.docsShared).then(docsShared => { + docsShared?.forEach(doc => { + DocListCastAsync(user.notificationDoc[storage]).then(resolved => Doc.IndexOf(doc, resolved!) !== -1 && Doc.RemoveDocFromList(user.notificationDoc, storage, doc)); + }); + }); + // DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc)); } } removeGroup = (group: Doc) => { if (group.docsShared) { - DocListCast(group.docsShared).forEach(doc => { - const ACL = `ACL-${StrCast(group.groupName)}`; - doc[ACL] = "Not Shared"; + DocListCastAsync(group.docsShared).then(resolved => { + resolved?.forEach(doc => { + const ACL = `ACL-${StrCast(group.groupName)}`; + doc[ACL] = "Not Shared"; + + const members: string[] = JSON.parse(StrCast(group.members)); + const users: ValidatedUser[] = this.users.filter(user => members.includes(user.user.email)); - const members: string[] = JSON.parse(StrCast(group.members)); - const users: ValidatedUser[] = this.users.filter(user => members.includes(user.user.email)); + users.forEach(user => Doc.RemoveDocFromList(user.notificationDoc, storage, doc)); + }); - users.forEach(user => Doc.RemoveDocFromList(user.notificationDoc, storage, doc)); }); } } @@ -217,14 +218,16 @@ export default class SharingManager extends React.Component<{}> { if (permission !== SharingPermissions.None) { - !this.sharedUsers.includes(recipient) && this.sharedUsers.push(recipient); - - Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target); + console.log(target); + console.log(notificationDoc); + DocListCastAsync(notificationDoc[storage]).then(resolved => { + Doc.IndexOf(target, resolved!) === -1 && Doc.AddDocToList(notificationDoc, storage, target); + }); } else { - const index = this.sharedUsers.findIndex(user => user === recipient); - index !== -1 && this.sharedUsers.splice(index, 1); - Doc.IndexOf(target, DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); + DocListCastAsync(notificationDoc[storage]).then(resolved => { + Doc.IndexOf(target, resolved!) === -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); + }); } } diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 43ffe225f..eb58d8a3e 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -7,7 +7,7 @@ import { InteractionUtils } from '../util/InteractionUtils'; import { List } from '../../fields/List'; import { DateField } from '../../fields/DateField'; import { ScriptField } from '../../fields/ScriptField'; -import { getEffectiveAcl } from '../../fields/util'; +import { GetEffectiveAcl } from '../../fields/util'; /// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView) @@ -138,7 +138,7 @@ export function ViewBoxAnnotatableComponent

!docList.includes(d)); - const effectiveAcl = getEffectiveAcl(this.dataDoc); + const effectiveAcl = GetEffectiveAcl(this.dataDoc); if (added.length) { if (effectiveAcl === AclReadonly) { return false; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 032012b2d..7448ae002 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -17,7 +17,7 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; -import { TraceMobx, getEffectiveAcl } from '../../../fields/util'; +import { TraceMobx, GetEffectiveAcl } from '../../../fields/util'; import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; @@ -132,7 +132,7 @@ export class CollectionView extends Touchable !docList.includes(d)); - const effectiveAcl = getEffectiveAcl(this.props.Document); + const effectiveAcl = GetEffectiveAcl(this.props.Document); if (added.length) { if (effectiveAcl === AclReadonly) { return false; @@ -166,7 +166,7 @@ export class CollectionView extends Touchable { - if (getEffectiveAcl(this.props.Document) === AclEdit) { + if (GetEffectiveAcl(this.props.Document) === AclEdit) { const docs = doc instanceof Doc ? [doc] : doc as Doc[]; const targetDataDoc = this.props.Document[DataSym]; const value = DocListCast(targetDataDoc[this.props.fieldKey]); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index b47236bea..971c501ca 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -1,6 +1,7 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, Opt, DocListCast, DataSym } from "../../../../fields/Doc"; +import { Doc, Opt, DocListCast, DataSym, AclEdit, AclAddonly } from "../../../../fields/Doc"; +import { GetEffectiveAcl } from "../../../../fields/util"; import { InkData, InkField, InkTool } from "../../../../fields/InkField"; import { List } from "../../../../fields/List"; import { RichTextField } from "../../../../fields/RichTextField"; @@ -276,7 +277,8 @@ export class MarqueeView extends React.Component 1 ? splits[0] + splits[1].replace(/{([^{}]|(?R))*}/, replacer4) : ""; // might have been more elegant if javascript supported recursive patterns - return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc || getEffectiveAcl(this.layoutDoc) === AclPrivate) ? (null) : + return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc || GetEffectiveAcl(this.layoutDoc) === AclPrivate) ? (null) : (Docu render() { if (!(this.props.Document instanceof Doc)) return (null); - if (getEffectiveAcl(this.props.Document) === AclPrivate) return (null); + if (GetEffectiveAcl(this.props.Document) === AclPrivate) return (null); if (this.props.Document.hidden) return (null); const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document); const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null))); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index aa89b4a10..0583c7f39 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -24,7 +24,7 @@ import { RichTextField } from "../../../../fields/RichTextField"; import { RichTextUtils } from '../../../../fields/RichTextUtils'; import { createSchema, makeInterface } from "../../../../fields/Schema"; import { Cast, DateCast, NumCast, StrCast, ScriptCast } from "../../../../fields/Types"; -import { TraceMobx, OVERRIDE_ACL, getEffectiveAcl } from '../../../../fields/util'; +import { TraceMobx, OVERRIDE_ACL, GetEffectiveAcl } from '../../../../fields/util'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents } from '../../../../Utils'; import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils'; import { DocServer } from "../../../DocServer"; @@ -226,7 +226,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template const json = JSON.stringify(state.toJSON()); - if (getEffectiveAcl(this.dataDoc) === AclEdit) { + if (GetEffectiveAcl(this.dataDoc) === AclEdit) { if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) { this._applyingChange = true; (curText !== Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text) && (this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()))); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 27eabf451..e8dca5fb6 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -17,7 +17,7 @@ import { RichTextField } from "./RichTextField"; import { listSpec } from "./Schema"; import { ComputedField } from "./ScriptField"; import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types"; -import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction, getEffectiveAcl } from "./util"; +import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction, GetEffectiveAcl } from "./util"; import { LinkManager } from "../client/util/LinkManager"; import { SharingPermissions } from "../client/util/SharingManager"; @@ -136,10 +136,10 @@ export class Doc extends RefField { set: setter, get: getter, // getPrototypeOf: (target) => Cast(target[SelfProxy].proto, Doc) || null, // TODO this might be able to replace the proto logic in getter - has: (target, key) => getEffectiveAcl(target) !== AclPrivate && key in target.__fields, + has: (target, key) => GetEffectiveAcl(target) !== AclPrivate && key in target.__fields, ownKeys: target => { const obj = {} as any; - if (getEffectiveAcl(target) !== AclPrivate) Object.assign(obj, target.___fields); + if (GetEffectiveAcl(target) !== AclPrivate) Object.assign(obj, target.___fields); runInAction(() => obj.__LAYOUT__ = target.__LAYOUT__); return Object.keys(obj); }, @@ -197,7 +197,7 @@ export class Doc extends RefField { public [WidthSym] = () => NumCast(this[SelfProxy]._width); public [HeightSym] = () => NumCast(this[SelfProxy]._height); public [ToScriptString]() { return `DOC-"${this[Self][Id]}"-`; } - public [ToString]() { return `Doc(${getEffectiveAcl(this) === AclPrivate ? "-inaccessible-" : this.title})`; } + public [ToString]() { return `Doc(${GetEffectiveAcl(this) === AclPrivate ? "-inaccessible-" : this.title})`; } public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; } public get [DataSym]() { const self = this[SelfProxy]; @@ -825,7 +825,7 @@ export namespace Doc { } // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message) export function IsBrushedDegreeUnmemoized(doc: Doc) { - if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return 0; + if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return 0; return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? 1 : 0; } export function IsBrushedDegree(doc: Doc) { @@ -834,14 +834,14 @@ export namespace Doc { })(doc); } export function BrushDoc(doc: Doc) { - if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc; + if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc; brushManager.BrushedDoc.set(doc, true); brushManager.BrushedDoc.set(Doc.GetProto(doc), true); return doc; } export function UnBrushDoc(doc: Doc) { - if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc; + if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc; brushManager.BrushedDoc.delete(doc); brushManager.BrushedDoc.delete(Doc.GetProto(doc)); return doc; @@ -871,7 +871,7 @@ export namespace Doc { } const highlightManager = new HighlightBrush(); export function IsHighlighted(doc: Doc) { - if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return false; + if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return false; return highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetProto(doc)); } export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true) { diff --git a/src/fields/util.ts b/src/fields/util.ts index a1af1d3c5..f82ea26e0 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -108,15 +108,13 @@ export function OVERRIDE_ACL(val: boolean) { } let currentUserGroups: string[] = []; -let currentUserEmail: string;// = Doc.CurrentUserEmail; export function setGroups(groups: string[]) { currentUserGroups = groups; - currentUserEmail = Doc.CurrentUserEmail; } -export function getEffectiveAcl(target: any, in_prop?: string | symbol | number): symbol { +export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number): symbol { const HierarchyMapping = new Map([ [AclPrivate, 0], @@ -131,7 +129,7 @@ export function getEffectiveAcl(target: any, in_prop?: string | symbol | number) if (target[AclSym] && Object.keys(target[AclSym]).length) { - if (target.author === currentUserEmail) return AclEdit; + if (target.author === Doc.CurrentUserEmail || currentUserGroups.includes("admin")) return AclEdit; if (_overrideAcl || (in_prop && DocServer.PlaygroundFields?.includes(in_prop.toString()))) return AclEdit; @@ -141,13 +139,11 @@ export function getEffectiveAcl(target: any, in_prop?: string | symbol | number) let aclPresent = false; for (const [key, value] of Object.entries(target[AclSym])) { - if (key.startsWith("ACL-")) { - if (currentUserGroups.includes(key.substring(4)) || currentUserEmail === key.substring(4).replace("_", ".")) { - if (HierarchyMapping.get(value as symbol)! >= HierarchyMapping.get(effectiveAcl)!) { - aclPresent = true; - effectiveAcl = value as symbol; - if (effectiveAcl === AclEdit) break; - } + if (currentUserGroups.includes(key.substring(4)) || Doc.CurrentUserEmail === key.substring(4).replace("_", ".")) { + if (HierarchyMapping.get(value as symbol)! >= HierarchyMapping.get(effectiveAcl)!) { + aclPresent = true; + effectiveAcl = value as symbol; + if (effectiveAcl === AclEdit) break; } } } @@ -163,7 +159,7 @@ const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHe "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"]; export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean { let prop = in_prop; - if (getEffectiveAcl(target, in_prop) !== AclEdit) { + if (GetEffectiveAcl(target, in_prop) !== AclEdit) { return true; } if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) { @@ -185,7 +181,7 @@ export function setter(target: any, in_prop: string | symbol | number, value: an export function getter(target: any, in_prop: string | symbol | number, receiver: any): any { let prop = in_prop; if (in_prop === AclSym) return _overrideAcl ? undefined : target[AclSym]; - if (getEffectiveAcl(target) === AclPrivate && !_overrideAcl) return undefined; + if (GetEffectiveAcl(target) === AclPrivate && !_overrideAcl) return undefined; if (prop === LayoutSym) { return target.__LAYOUT__; } @@ -222,7 +218,7 @@ function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreP } if (field === undefined && !ignoreProto && prop !== "proto") { const proto = getFieldImpl(target, "proto", receiver, true);//TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters - if (proto instanceof Doc && getEffectiveAcl(proto) !== AclPrivate) { + if (proto instanceof Doc && GetEffectiveAcl(proto) !== AclPrivate) { return getFieldImpl(proto[Self], prop, receiver, ignoreProto); } return undefined; -- cgit v1.2.3-70-g09d2 From 58865aa5f27fa4a23af3a589bfc45868c7a77151 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 9 Jul 2020 11:31:04 -0400 Subject: fixed issues with frame based animations for annotations on images,etc. --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 6 +++--- src/client/views/presentationview/PresElementBox.tsx | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9bf425db2..60c9d7f4a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1065,7 +1065,7 @@ export class CollectionFreeFormView extends CollectionSubView) { const layoutDocs = this.childLayoutPairs.map(pair => pair.layout); - const initResult = this.Document.arrangeInit && this.Document.arrangeInit.script.run({ docs: layoutDocs, collection: this.Document }, console.log); + const initResult = this.Document.arrangeInit?.script.run({ docs: layoutDocs, collection: this.Document }, console.log); const state = initResult?.success ? initResult.result.scriptState : undefined; const elements = initResult?.success ? this.viewDefsToJSX(initResult.result.views) : []; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index a3020f912..d79e2c9ff 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -73,9 +73,9 @@ export class CollectionFreeFormDocumentView extends DocComponent (i <= timecode && x !== undefined) || p === undefined ? x : p, undefined as any as number), - y: Cast(doc["y-indexed"], listSpec("number"), []).reduce((p, y, i) => (i <= timecode && y !== undefined) || p === undefined ? y : p, undefined as any as number), - opacity: Cast(doc["opacity-indexed"], listSpec("number"), []).reduce((p, o, i) => i <= timecode || p === undefined ? o : p, undefined as any as number), + x: Cast(doc["x-indexed"], listSpec("number"), [NumCast(doc.x)]).reduce((p, x, i) => (i <= timecode && x !== undefined) || p === undefined ? x : p, undefined as any as number), + y: Cast(doc["y-indexed"], listSpec("number"), [NumCast(doc.y)]).reduce((p, y, i) => (i <= timecode && y !== undefined) || p === undefined ? y : p, undefined as any as number), + opacity: Cast(doc["opacity-indexed"], listSpec("number"), [NumCast(doc.opacity, 1)]).reduce((p, o, i) => i <= timecode || p === undefined ? o : p, undefined as any as number), }); } diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index 6fd3455b6..4c0972736 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -15,6 +15,7 @@ import { FieldView, FieldViewProps } from '../nodes/FieldView'; import "./PresElementBox.scss"; import React = require("react"); import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; +import { DocumentType } from "../../documents/DocumentTypes"; export const presSchema = createSchema({ presentationTargetDoc: Doc, @@ -100,7 +101,8 @@ export class PresElementBox extends ViewBoxBaseComponent Date: Thu, 9 Jul 2020 11:36:48 -0400 Subject: from last --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 60c9d7f4a..d9aabd7c2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -205,7 +205,7 @@ export class CollectionFreeFormView extends CollectionSubView Date: Thu, 9 Jul 2020 11:52:05 -0400 Subject: fixed observing docking view golden layout window size changes. --- src/client/views/collections/CollectionDockingView.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 79c577b6d..f408e24a8 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -390,6 +390,12 @@ export class CollectionDockingView extends React.Component void = () => { if (this._containerRef.current) { + const observer = new _global.ResizeObserver(action((entries: any) => { + for (const entry of entries) { + this.onResize(null as any); + } + })); + observer.observe(this._containerRef.current); this.reactionDisposer = reaction( () => this.props.Document.dockingConfig, () => { @@ -646,16 +652,8 @@ export class CollectionDockingView extends React.Component 0) { return

Nested workspaces can't be rendered
; } - return ( - - {({ measureRef }) => -
- - } - - ); + return