From a4959a79b002a25617a84e6edff1148fff666c68 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Fri, 19 Jun 2020 11:37:39 +0530 Subject: initial commit: added group document type + started setting up GroupManager --- src/client/documents/DocumentTypes.ts | 1 + src/client/documents/Documents.ts | 12 ++++++ src/client/util/CurrentUserUtils.ts | 1 + src/client/util/GroupManager.tsx | 43 ++++++++++++++++++++++ src/client/util/SharingManager.tsx | 4 +- src/client/views/MainView.tsx | 5 +++ .../CollectionStackingViewFieldColumn.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 5 ++- src/fields/Doc.ts | 3 ++ 9 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 src/client/util/GroupManager.tsx (limited to 'src') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 7ba21b2f6..5697a5bc0 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -36,4 +36,5 @@ export enum DocumentType { LINKDB = "linkdb", // database of links ??? why do we have this SCRIPTDB = "scriptdb", // database of scripts RECOMMENDATION = "recommendation", // view of a recommendation + GROUPDB = "groupdb" // database of groups } \ No newline at end of file diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index dac3a1aaa..980c52dd0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -310,6 +310,11 @@ export namespace Docs { [DocumentType.COMPARISON, { layout: { view: ComparisonBox, dataField: defaultDataKey }, }], + [DocumentType.GROUPDB, { + data: new List(), + layout: { view: EmptyBox, dataField: defaultDataKey }, + options: { childDropAction: "alias", title: "Global Group Database" } + }], ]); // All document prototypes are initialized with at least these values @@ -372,6 +377,13 @@ export namespace Docs { return Prototypes.get(DocumentType.SCRIPTDB); } + /** + * A collection of all groups in the database + */ + export function MainGroupDocument() { + return Prototypes.get(DocumentType.GROUPDB); + } + /** * This is a convenience method that is used to initialize * prototype documents for the first time. diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 37eaa5f5e..423b1fb90 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -766,6 +766,7 @@ export class CurrentUserUtils { await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels doc.globalLinkDatabase = Docs.Prototypes.MainLinkDocument(); doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument(); + // doc.globalGroupDatabse = Docs.Prototypes.MainGroupDocument(); // setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx new file mode 100644 index 000000000..d9a20e1a7 --- /dev/null +++ b/src/client/util/GroupManager.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { observable, action } from "mobx"; +import { SelectionManager } from "./SelectionManager"; +import MainViewModal from "../views/MainViewModal"; + + +export default class GroupManager extends React.Component<{}>{ + + static Instance: GroupManager; + @observable private isOpen: boolean = false; // whether the menu is open or not + @observable private dialogueBoxOpacity = 1; + @observable private overlayOpacity = 0.4; + + constructor(props: Readonly<{}>) { + super(props); + GroupManager.Instance = this; + } + + + open = action(() => { + SelectionManager.DeselectAll(); + this.isOpen = true; + }); + + close = action(() => { + this.isOpen = false; + }); + + private get groupInterface() { + return
TESTING
; + } + + render() { + return ; + } + +} \ No newline at end of file diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index dc67145fc..2e660e819 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -28,14 +28,14 @@ export interface User { export enum SharingPermissions { None = "Not Shared", View = "Can View", - Comment = "Can Comment", + Add = "Can Add and Comment", Edit = "Can Edit" } const ColorMapping = new Map([ [SharingPermissions.None, "red"], [SharingPermissions.View, "maroon"], - [SharingPermissions.Comment, "blue"], + [SharingPermissions.Add, "blue"], [SharingPermissions.Edit, "green"] ]); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 4a09f9ff3..830d5cf51 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -29,6 +29,7 @@ import { HistoryUtil } from '../util/History'; import RichTextMenu from './nodes/formattedText/RichTextMenu'; import { Scripting } from '../util/Scripting'; import SettingsManager from '../util/SettingsManager'; +import GroupManager from '../util/GroupManager'; import SharingManager from '../util/SharingManager'; import { Transform } from '../util/Transform'; import { CollectionDockingView } from './collections/CollectionDockingView'; @@ -446,6 +447,9 @@ export class MainView extends React.Component { + {/* */} {this.docButtons} ; @@ -562,6 +566,7 @@ export class MainView extends React.Component { + diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index bcd55f0fe..d128c59ec 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -362,7 +362,7 @@ export class CollectionStackingViewFieldColumn extends React.Component +
(Docu @undoBatch @action - setAcl = (acl: "readOnly" | "addOnly" | "ownerOnly") => { + setAcl = (acl: "readOnly" | "addOnly" | "ownerOnly" | "write") => { 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; @@ -684,7 +684,7 @@ export class DocumentView extends DocComponent(Docu } @undoBatch @action - testAcl = (acl: "readOnly" | "addOnly" | "ownerOnly") => { + testAcl = (acl: "readOnly" | "addOnly" | "ownerOnly" | "write") => { 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 => { @@ -798,6 +798,7 @@ export class DocumentView extends DocComponent(Docu 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" }); !existingAcls && cm.addItem({ description: "Privacy...", subitems: aclItems, icon: "question" }); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 8c8720179..8ca85cae9 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -96,6 +96,7 @@ 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 UpdatingFromServer = Symbol("UpdatingFromServer"); const CachedUpdates = Symbol("Cached updates"); @@ -113,6 +114,8 @@ export function fetchProto(doc: Doc) { case "addOnly": doc[AclSym] = AclAddonly; break; + case "write": + doc[AclSym] = AclReadWrite; } } -- cgit v1.2.3-70-g09d2 From 8e76c6267d502ab75b5d9e3fdbf5839c0967cdb1 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Fri, 19 Jun 2020 13:19:44 +0530 Subject: set up ui --- src/client/util/GroupManager.scss | 0 src/client/util/GroupManager.tsx | 49 +++++++++++++++++++++++++------------- src/client/views/MainView.scss | 50 ++++++++++++++++++++++++++------------- src/client/views/MainView.tsx | 14 ++++++----- 4 files changed, 75 insertions(+), 38 deletions(-) create mode 100644 src/client/util/GroupManager.scss (limited to 'src') diff --git a/src/client/util/GroupManager.scss b/src/client/util/GroupManager.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index d9a20e1a7..65ea97e6c 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -1,43 +1,60 @@ -import React from "react"; +import * as React from "react"; import { observable, action } from "mobx"; import { SelectionManager } from "./SelectionManager"; import MainViewModal from "../views/MainViewModal"; +import { observer } from "mobx-react"; - -export default class GroupManager extends React.Component<{}>{ +@observer +export default class GroupManager extends React.Component<{}> { static Instance: GroupManager; @observable private isOpen: boolean = false; // whether the menu is open or not - @observable private dialogueBoxOpacity = 1; - @observable private overlayOpacity = 0.4; + @observable private dialogueBoxOpacity: number = 1; + @observable private overlayOpacity: number = 0.4; constructor(props: Readonly<{}>) { super(props); GroupManager.Instance = this; } - - open = action(() => { + public open = action(() => { SelectionManager.DeselectAll(); this.isOpen = true; }); - close = action(() => { + public close = action(() => { this.isOpen = false; }); private get groupInterface() { - return
TESTING
; + return ( +
+
+

settings

+
+ OI +
+
+
+
+ + +
+
+
+ ); } render() { - return ; + return ( + + ); } } \ No newline at end of file diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index e84969565..5b142ffda 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -28,10 +28,11 @@ left: 0; width: 100%; height: 100%; - pointer-events:none; + pointer-events: none; } -.mainView-container, .mainView-container-dark { +.mainView-container, +.mainView-container-dark { width: 100%; height: 100%; position: absolute; @@ -40,40 +41,50 @@ left: 0; z-index: 1; touch-action: none; + .searchBox-container { background: lightgray; } } .mainView-container { - color:dimgray; + color: dimgray; + .lm_title { background: #cacaca; - color:black; + color: black; } } .mainView-container-dark { color: lightgray; + .lm_goldenlayout { background: dimgray; } + .lm_title { background: black; - color:unset; + color: unset; } + .marquee { border-color: white; } + #search-input { background: lightgray; } - .searchBox-container { - background: rgb(45,45,45); + + .searchBox-container { + background: rgb(45, 45, 45); } - .contextMenu-cont, .contextMenu-item { + + .contextMenu-cont, + .contextMenu-item { background: dimGray; } + .contextMenu-item:hover { background: gray; } @@ -108,20 +119,27 @@ overflow: hidden; } +.buttonContainer { -.mainView-settings { position: absolute; - left: 0; bottom: 0; - border-radius: 25%; - margin-left: -5px; - background: darkblue; -} -.mainView-settings:hover { - transform: none !important; + .mainView-settings { + // position: absolute; + // left: 0; + // bottom: 0; + border-radius: 25%; + margin-left: -5px; + background: darkblue; + } + + .mainView-settings:hover { + transform: none !important; + } } + + .mainView-logout { position: absolute; right: 0; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 830d5cf51..76c2cccae 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -444,12 +444,14 @@ export class MainView extends React.Component { docFilters={returnEmptyFilter} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} /> - - {/* */} +
+ + +
{this.docButtons}
; -- cgit v1.2.3-70-g09d2 From 9951e35273e9e14f48298ca25c84749346dac118 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Sun, 21 Jun 2020 19:18:01 +0530 Subject: more setup of groupmanager etc --- src/client/documents/DocumentTypes.ts | 1 + src/client/documents/Documents.ts | 3 +++ src/client/util/CurrentUserUtils.ts | 2 +- src/client/util/GroupManager.tsx | 47 ++++++++++++++++++++++++++++++++--- src/client/util/SettingsManager.scss | 1 + src/server/ApiManagers/UserManager.ts | 11 ++++++++ 6 files changed, 60 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 5697a5bc0..7578b7df0 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -32,6 +32,7 @@ export enum DocumentType { YOUTUBE = "youtube", // youtube directory (view of you tube search results) DOCHOLDER = "docholder", // nested document (view of a document) COMPARISON = "comparison", // before/after view with slider (view of 2 images) + GROUP = "group", // group of users LINKDB = "linkdb", // database of links ??? why do we have this SCRIPTDB = "scriptdb", // database of scripts diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 980c52dd0..d3710e858 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -315,6 +315,9 @@ export namespace Docs { layout: { view: EmptyBox, dataField: defaultDataKey }, options: { childDropAction: "alias", title: "Global Group Database" } }], + [DocumentType.GROUP, { + layout: { view: EmptyBox, dataField: defaultDataKey } + }] ]); // All document prototypes are initialized with at least these values diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 423b1fb90..84e69eebe 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -766,7 +766,7 @@ export class CurrentUserUtils { await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels doc.globalLinkDatabase = Docs.Prototypes.MainLinkDocument(); doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument(); - // doc.globalGroupDatabse = Docs.Prototypes.MainGroupDocument(); + doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument(); // setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 65ea97e6c..c4798e6a9 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -3,6 +3,13 @@ import { observable, action } from "mobx"; import { SelectionManager } from "./SelectionManager"; import MainViewModal from "../views/MainViewModal"; import { observer } from "mobx-react"; +import { Doc, DocListCast } from "../../fields/Doc"; +import { List } from "../../fields/List"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import * as fa from '@fortawesome/free-solid-svg-icons'; +import { library } from "@fortawesome/fontawesome-svg-core"; + +library.add(fa.faWindowClose); @observer export default class GroupManager extends React.Component<{}> { @@ -17,22 +24,54 @@ export default class GroupManager extends React.Component<{}> { GroupManager.Instance = this; } - public open = action(() => { + open = action(() => { SelectionManager.DeselectAll(); this.isOpen = true; }); - public close = action(() => { + close = action(() => { this.isOpen = false; }); + get GroupManagerDoc(): Doc | undefined { + return Doc.UserDoc().globalGroupDatabase as Doc; + } + + getAllGroups(): Doc[] { + const groupDoc = GroupManager.Instance.GroupManagerDoc; + return groupDoc ? DocListCast(groupDoc.data) : []; + } + + addGroup(groupDoc: Doc): boolean { + const groupList = GroupManager.Instance.getAllGroups(); + groupList.push(groupDoc); + if (GroupManager.Instance.GroupManagerDoc) { + GroupManager.Instance.GroupManagerDoc.data = new List(groupList); + return true; + } + return false; + } + + deleteGroup(groupDoc: Doc): boolean { + const groupList = GroupManager.Instance.getAllGroups(); + const index = groupList.indexOf(groupDoc); + if (index !== -1) { + groupList.splice(index, 1); + if (GroupManager.Instance.GroupManagerDoc) { + GroupManager.Instance.GroupManagerDoc.data = new List(groupList); + return true; + } + } + return false; + } + private get groupInterface() { return (
-

settings

+

Groups

- OI +
diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss index 6513cb223..fa2609ca2 100644 --- a/src/client/util/SettingsManager.scss +++ b/src/client/util/SettingsManager.scss @@ -41,6 +41,7 @@ position: absolute; right: 1em; top: 1em; + cursor: pointer; } .settings-heading { diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts index 0d1d8f218..5e7bb6855 100644 --- a/src/server/ApiManagers/UserManager.ts +++ b/src/server/ApiManagers/UserManager.ts @@ -25,6 +25,17 @@ export default class UserManager extends ApiManager { } }); + register({ + method: Method.GET, + subscription: "/getGroups", + secureHandler: async ({ res }) => { + console.log(Database.Instance.getCollectionNames()); + const cursor = await Database.Instance.query({}, { name: 1, owner: 1, members: 1 }, "groups"); + const results = await cursor.toArray(); + res.send(results.map(group => ({ name: group.name, owner: group.owner, members: group.members }))); + } + }); + register({ method: Method.GET, subscription: "/getUserDocumentId", -- cgit v1.2.3-70-g09d2 From fd8bcfb19f0c45757c60a9d63bf56753e9dcb12f Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Mon, 22 Jun 2020 18:10:16 +0530 Subject: cleaned up addLink and deleteLink in LinkManager + setting up GroupManager ui + installed multiselect dropdown package + a lot of initial group functionality (incomplete) --- package-lock.json | 70 +++++++++++++++++++ package.json | 2 + src/client/util/GroupManager.tsx | 126 +++++++++++++++++++++++++++++----- src/client/util/LinkManager.ts | 15 ++-- src/server/ApiManagers/UserManager.ts | 11 --- 5 files changed, 185 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/package-lock.json b/package-lock.json index c601e68fb..1d4d39826 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1005,6 +1005,17 @@ "@types/react": "*" } }, + "@types/react-select": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@types/react-select/-/react-select-3.0.13.tgz", + "integrity": "sha512-JxmSArGgzAOtb37+Jz2+3av8rVmp/3s3DGwlcP+g59/a3owkiuuU4/Jajd+qA32beDPHy4gJR2kkxagPY3j9kg==", + "dev": true, + "requires": { + "@types/react": "*", + "@types/react-dom": "*", + "@types/react-transition-group": "*" + } + }, "@types/react-table": { "version": "6.8.7", "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-6.8.7.tgz", @@ -1013,6 +1024,15 @@ "@types/react": "*" } }, + "@types/react-transition-group": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", + "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/request": { "version": "2.48.4", "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.4.tgz", @@ -8357,6 +8377,11 @@ "p-is-promise": "^2.0.0" } }, + "memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==" + }, "memory-fs": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", @@ -14280,6 +14305,14 @@ "react-modal": "^3.4.4" } }, + "react-input-autosize": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.2.tgz", + "integrity": "sha512-jQJgYCA3S0j+cuOwzuCd1OjmBmnZLdqQdiLKRYrsMMzbjUrVDS5RvJUDwJqA7sKuksDuzFtm6hZGKFu7Mjk5aw==", + "requires": { + "prop-types": "^15.5.8" + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -14357,6 +14390,43 @@ "react-draggable": "^4.0.3" } }, + "react-select": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-3.1.0.tgz", + "integrity": "sha512-wBFVblBH1iuCBprtpyGtd1dGMadsG36W5/t2Aj8OE6WbByDg5jIFyT7X5gT+l0qmT5TqWhxX+VsKJvCEl2uL9g==", + "requires": { + "@babel/runtime": "^7.4.4", + "@emotion/cache": "^10.0.9", + "@emotion/core": "^10.0.9", + "@emotion/css": "^10.0.9", + "memoize-one": "^5.0.0", + "prop-types": "^15.6.0", + "react-input-autosize": "^2.2.2", + "react-transition-group": "^4.3.0" + }, + "dependencies": { + "dom-helpers": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.4.tgz", + "integrity": "sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A==", + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^2.6.7" + } + }, + "react-transition-group": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + } + } + }, "react-table": { "version": "6.11.5", "resolved": "https://registry.npmjs.org/react-table/-/react-table-6.11.5.tgz", diff --git a/package.json b/package.json index 096c26cd0..0e6906fc8 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "@types/react-dom": "^16.9.5", "@types/react-grid-layout": "^0.17.1", "@types/react-measure": "^2.0.6", + "@types/react-select": "^3.0.13", "@types/react-table": "^6.8.6", "@types/request": "^2.48.4", "@types/request-promise": "^4.1.45", @@ -214,6 +215,7 @@ "react-jsx-parser": "^1.21.0", "react-measure": "^2.2.4", "react-resizable": "^1.10.1", + "react-select": "^3.1.0", "react-table": "^6.11.5", "readline": "^1.3.0", "request": "^2.88.0", diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index c4798e6a9..881583d37 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { observable, action } from "mobx"; +import { observable, action, runInAction, computed } from "mobx"; import { SelectionManager } from "./SelectionManager"; import MainViewModal from "../views/MainViewModal"; import { observer } from "mobx-react"; @@ -8,6 +8,10 @@ import { List } from "../../fields/List"; 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 { Utils } from "../../Utils"; +import * as RequestPromise from "request-promise"; +import Select from 'react-select'; library.add(fa.faWindowClose); @@ -18,15 +22,33 @@ export default class GroupManager extends React.Component<{}> { @observable private isOpen: boolean = false; // whether the menu is open or not @observable private dialogueBoxOpacity: number = 1; @observable private overlayOpacity: number = 0.4; + @observable private users: string[] = []; + @observable private selectedUsers: string[] | null = null; constructor(props: Readonly<{}>) { super(props); GroupManager.Instance = this; } + componentDidMount() { + console.log("mounted"); + } + + 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); + return userList.map(user => user.email); + } + + @computed get options() { + return this.users.map(user => ({ label: user, value: user })); + } + open = action(() => { SelectionManager.DeselectAll(); this.isOpen = true; + this.populateUsers().then(resolved => runInAction(() => this.users = resolved)); }); close = action(() => { @@ -42,29 +64,91 @@ export default class GroupManager extends React.Component<{}> { return groupDoc ? DocListCast(groupDoc.data) : []; } + getGroup(groupName: string): Doc | undefined { + const groupDoc = GroupManager.Instance.getAllGroups().find(group => group.name === groupName); + return groupDoc; + } + + get adminGroupMembers(): string[] { + return JSON.parse(GroupManager.Instance.getGroup("admin")!.members as string); + } + + hasEditAccess(groupDoc: Doc): boolean { + const accessList: string[] = JSON.parse(groupDoc.owners as string); + return accessList.includes(Doc.CurrentUserEmail) || GroupManager.Instance.adminGroupMembers.includes(Doc.CurrentUserEmail); + } + + createGroupDoc(groupName: string, memberEmails: string[]) { + const groupDoc = new Doc; + groupDoc.groupName = groupName; + groupDoc.owners = JSON.stringify([Doc.CurrentUserEmail]); + groupDoc.members = JSON.stringify(memberEmails); + this.addGroup(groupDoc); + } + addGroup(groupDoc: Doc): boolean { - const groupList = GroupManager.Instance.getAllGroups(); - groupList.push(groupDoc); + // const groupList = GroupManager.Instance.getAllGroups(); + // groupList.push(groupDoc); if (GroupManager.Instance.GroupManagerDoc) { - GroupManager.Instance.GroupManagerDoc.data = new List(groupList); + Doc.AddDocToList(GroupManager.Instance.GroupManagerDoc, "data", groupDoc); + // GroupManager.Instance.GroupManagerDoc.data = new List(groupList); return true; } return false; } - deleteGroup(groupDoc: Doc): boolean { - const groupList = GroupManager.Instance.getAllGroups(); - const index = groupList.indexOf(groupDoc); - if (index !== -1) { - groupList.splice(index, 1); - if (GroupManager.Instance.GroupManagerDoc) { - GroupManager.Instance.GroupManagerDoc.data = new List(groupList); + deleteGroup(groupName: string): boolean { + // const groupList = GroupManager.Instance.getAllGroups(); + // const index = groupList.indexOf(groupDoc); + // if (index !== -1) { + // groupList.splice(index, 1); + const groupDoc = GroupManager.Instance.getGroup(groupName); + if (groupDoc) { + if (GroupManager.Instance.GroupManagerDoc && GroupManager.Instance.hasEditAccess(groupDoc)) { + // GroupManager.Instance.GroupManagerDoc.data = new List(groupList); + Doc.RemoveDocFromList(GroupManager.Instance.GroupManagerDoc, "data", groupDoc); return true; } } + + return false; } + addMemberToGroup(groupDoc: Doc, email: string) { + if (GroupManager.Instance.hasEditAccess(groupDoc)) { + const memberList: string[] = JSON.parse(groupDoc.members as string); + !memberList.includes(email) && memberList.push(email); + groupDoc.members = JSON.stringify(memberList); + } + } + + removeMemberFromGroup(groupDoc: Doc, email: string) { + if (GroupManager.Instance.hasEditAccess(groupDoc)) { + const memberList: string[] = JSON.parse(groupDoc.members as string); + const index = memberList.indexOf(email); + index !== -1 && memberList.splice(index, 1); + groupDoc.members = JSON.stringify(memberList); + } + } + + @action + handleChange = (selectedOptions: any) => { + const castOptions = selectedOptions as { label: string, value: string }[]; + console.log(castOptions); + this.selectedUsers = castOptions.map(option => option.value); + } + + @action + resetSelection = () => { + console.log(this.selectedUsers?.[0]); + this.selectedUsers = null; + } + + createGroup = () => { + this.selectedUsers = null; + } + private get groupInterface() { return (
@@ -73,13 +157,21 @@ export default class GroupManager extends React.Component<{}> {
+
-
-
- - -
-
+ + +
); } diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 47b2541bd..94a0da985 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -41,24 +41,17 @@ export class LinkManager { } public addLink(linkDoc: Doc): boolean { - const linkList = LinkManager.Instance.getAllLinks(); - linkList.push(linkDoc); if (LinkManager.Instance.LinkManagerDoc) { - LinkManager.Instance.LinkManagerDoc.data = new List(linkList); + Doc.AddDocToList(LinkManager.Instance.LinkManagerDoc, "data", linkDoc); return true; } return false; } public deleteLink(linkDoc: Doc): boolean { - const linkList = LinkManager.Instance.getAllLinks(); - const index = LinkManager.Instance.getAllLinks().indexOf(linkDoc); - if (index > -1) { - linkList.splice(index, 1); - if (LinkManager.Instance.LinkManagerDoc) { - LinkManager.Instance.LinkManagerDoc.data = new List(linkList); - return true; - } + if (LinkManager.Instance.LinkManagerDoc) { + Doc.RemoveDocFromList(LinkManager.Instance.LinkManagerDoc, "data", linkDoc); + return true; } return false; } diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts index 5e7bb6855..0d1d8f218 100644 --- a/src/server/ApiManagers/UserManager.ts +++ b/src/server/ApiManagers/UserManager.ts @@ -25,17 +25,6 @@ export default class UserManager extends ApiManager { } }); - register({ - method: Method.GET, - subscription: "/getGroups", - secureHandler: async ({ res }) => { - console.log(Database.Instance.getCollectionNames()); - const cursor = await Database.Instance.query({}, { name: 1, owner: 1, members: 1 }, "groups"); - const results = await cursor.toArray(); - res.send(results.map(group => ({ name: group.name, owner: group.owner, members: group.members }))); - } - }); - register({ method: Method.GET, subscription: "/getUserDocumentId", -- cgit v1.2.3-70-g09d2 From 6f4b35d24b2e4cbceaa6dffa46a909c3e92db0b5 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Wed, 24 Jun 2020 18:20:30 +0530 Subject: a lot of ui stuff --- src/client/util/GroupManager.scss | 167 ++++++++++++++++++++++++++++++++++++++ src/client/util/GroupManager.tsx | 87 ++++++++++++-------- 2 files changed, 220 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/client/util/GroupManager.scss b/src/client/util/GroupManager.scss index e69de29bb..973a988d7 100644 --- a/src/client/util/GroupManager.scss +++ b/src/client/util/GroupManager.scss @@ -0,0 +1,167 @@ +@import "../views/globalCssVariables"; + +.group-interface { + background-color: whitesmoke !important; + color: grey; + width: 450px; + height: 300px; + + button { + background: $lighter-alt-accent; + outline: none; + border-radius: 5px; + border: 0px; + color: #fcfbf7; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 75%; + padding: 10px; + margin: 10px; + transition: transform 0.2s; + margin: 2px; + } +} + +.group-interface { + display: flex; + flex-direction: column; + + button { + width: 100%; + align-self: center; + background: $darker-alt-accent; + margin-top: 4px; + } + + .delete-button { + background: rgb(227, 86, 86); + } + + .close-button { + position: absolute; + right: 1em; + top: 1em; + cursor: pointer; + } + + .group-heading { + letter-spacing: .5em; + } + + + .group-body { + display: flex; + justify-content: space-between; + max-height: 80%; + + .group-create { + display: flex; + flex-direction: column; + flex-basis: 30%; + margin-left: 5px; + + input { + border-radius: 5px; + border: none; + padding: 4px; + min-width: 100%; + margin: 4px 0 4px 0; + } + + } + + .group-content { + padding-left: 1em; + padding-right: 1em; + justify-content: space-around; + text-align: left; + + overflow-y: auto; + width: 100%; + + .group-row { + display: flex; + position: relative; + + margin-bottom: 17.5; + + .group-name { + position: relative; + max-width: 65%; + } + + button { + position: absolute; + width: 30%; + right: 0; + } + } + + + + ::placeholder { + color: $intermediate-color; + } + + input { + border-radius: 5px; + border: none; + padding: 4px; + min-width: 100%; + margin: 2px 0; + } + + .error-text { + color: #C40233; + } + + .success-text { + color: #009F6B; + } + + p { + padding: 0 0 .1em .2em; + } + + } + } + + .focus-span { + text-decoration: underline; + } + + h1 { + color: $dark-color; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 120%; + } + + .container { + display: block; + position: relative; + margin-top: 10px; + margin-bottom: 10px; + font-size: 22px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: 700px; + min-width: 700px; + max-width: 700px; + text-align: left; + font-style: normal; + font-size: 15; + font-weight: normal; + padding: 0; + + .padding { + padding: 0 0 0 20px; + color: black; + } + + + + } +} \ No newline at end of file diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 881583d37..2617047f7 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -12,9 +12,15 @@ import SharingManager, { User } from "./SharingManager"; import { Utils } from "../../Utils"; import * as RequestPromise from "request-promise"; import Select from 'react-select'; +import "./GroupManager.scss"; library.add(fa.faWindowClose); +interface UserOptions { + label: string; + value: string; +} + @observer export default class GroupManager extends React.Component<{}> { @@ -23,7 +29,8 @@ export default class GroupManager extends React.Component<{}> { @observable private dialogueBoxOpacity: number = 1; @observable private overlayOpacity: number = 0.4; @observable private users: string[] = []; - @observable private selectedUsers: string[] | null = null; + @observable private selectedUsers: UserOptions[] | null = null; + private inputRef: React.RefObject = React.createRef(); constructor(props: Readonly<{}>) { super(props); @@ -65,7 +72,7 @@ export default class GroupManager extends React.Component<{}> { } getGroup(groupName: string): Doc | undefined { - const groupDoc = GroupManager.Instance.getAllGroups().find(group => group.name === groupName); + const groupDoc = GroupManager.Instance.getAllGroups().find(group => group.groupName === groupName); return groupDoc; } @@ -74,6 +81,7 @@ export default class GroupManager extends React.Component<{}> { } hasEditAccess(groupDoc: Doc): boolean { + if (!groupDoc) return false; const accessList: string[] = JSON.parse(groupDoc.owners as string); return accessList.includes(Doc.CurrentUserEmail) || GroupManager.Instance.adminGroupMembers.includes(Doc.CurrentUserEmail); } @@ -87,25 +95,17 @@ export default class GroupManager extends React.Component<{}> { } addGroup(groupDoc: Doc): boolean { - // const groupList = GroupManager.Instance.getAllGroups(); - // groupList.push(groupDoc); if (GroupManager.Instance.GroupManagerDoc) { Doc.AddDocToList(GroupManager.Instance.GroupManagerDoc, "data", groupDoc); - // GroupManager.Instance.GroupManagerDoc.data = new List(groupList); return true; } return false; } deleteGroup(groupName: string): boolean { - // const groupList = GroupManager.Instance.getAllGroups(); - // const index = groupList.indexOf(groupDoc); - // if (index !== -1) { - // groupList.splice(index, 1); const groupDoc = GroupManager.Instance.getGroup(groupName); if (groupDoc) { if (GroupManager.Instance.GroupManagerDoc && GroupManager.Instance.hasEditAccess(groupDoc)) { - // GroupManager.Instance.GroupManagerDoc.data = new List(groupList); Doc.RemoveDocFromList(GroupManager.Instance.GroupManagerDoc, "data", groupDoc); return true; } @@ -134,44 +134,63 @@ export default class GroupManager extends React.Component<{}> { @action handleChange = (selectedOptions: any) => { - const castOptions = selectedOptions as { label: string, value: string }[]; - console.log(castOptions); - this.selectedUsers = castOptions.map(option => option.value); + console.log(selectedOptions); + this.selectedUsers = selectedOptions as UserOptions[]; } @action - resetSelection = () => { - console.log(this.selectedUsers?.[0]); - this.selectedUsers = null; - } - createGroup = () => { + if (!this.inputRef.current?.value) { + alert("Please enter a group name"); + return; + } + if (!this.selectedUsers) { + alert("Please select users"); + return; + } + if (this.getAllGroups().find(group => group.groupName === this.inputRef.current!.value)) { // why do I need null check here? + alert("Please select a unique group name"); + return; + } + this.createGroupDoc(this.inputRef.current.value, this.selectedUsers.map(user => user.value)); this.selectedUsers = null; + this.inputRef.current.value = ""; } private get groupInterface() { return ( -
-
+
+

Groups

-
- - - +
+
+ + + 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} +
+ ))} +
+
); } @@ -175,7 +208,7 @@ export default class GroupManager extends React.Component<{}> {
{
- +
-

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

-
- {!existOtherUsers ? "There are no other users in your database." : - this.users.map(({ user, notificationDoc }) => { - const userKey = user.userDocumentId; - const permissions = this.computePermissions(userKey); - const color = ColorMapping.get(permissions); - return ( -
- - {user.email} -
- ); - }) - } +
+
+

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

+
{/*200*/} + {!existOtherUsers ? "There are no other users in your database." : + this.users.map(({ user, notificationDoc }) => { + const userKey = user.userDocumentId; + const permissions = this.computePermissions(userKey); + const color = ColorMapping.get(permissions); + return ( +
+ + {user.email} +
+ ); + }) + } +
+
+
+

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

+
{/*200*/} + {!existGroups ? "There are no groups in your database." : + this.groups.map(group => { + const permissions = this.computePermissions(group[Id]); + const color = ColorMapping.get(permissions); + return ( +
+ + {group.groupName} +
+ ); + }) + + } + +
+
Done
-- cgit v1.2.3-70-g09d2 From db3ed8fc7acbc1722160992b66fd6b3664c64007 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Fri, 26 Jun 2020 19:40:03 +0530 Subject: convoluted permissions stuff + SharingManager ui changes --- src/client/util/GroupManager.tsx | 3 +- src/client/util/SharingManager.scss | 15 +++----- src/client/util/SharingManager.tsx | 74 +++++++++++++++++++++++-------------- 3 files changed, 54 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index fa8da86c1..aaba84202 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -7,7 +7,7 @@ import { Doc, DocListCast, Opt } 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'; @@ -172,6 +172,7 @@ export default class GroupManager extends React.Component<{}> { if (group) { if (this.GroupManagerDoc && this.hasEditAccess(group)) { Doc.RemoveDocFromList(this.GroupManagerDoc, "data", group); + SharingManager.Instance.setInternalGroupSharing(group, "Not Shared"); if (group === this.currentGroup) { runInAction(() => this.currentGroup = undefined); } diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index 1aff345a0..7b7412ec7 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -23,9 +23,8 @@ border: 1px solid black; padding-left: 10px; padding-right: 10px; - // max-height: 200px; - overflow: scroll; - height: -webkit-fill-available; + overflow-y: scroll; + overflow-x: hidden; text-align: left; display: flex; align-content: center; @@ -33,9 +32,8 @@ text-align: center; justify-content: center; color: red; - - // width: 50%; - max-height: 150px; + height: 150px; + margin: 0 2; } } } @@ -45,7 +43,6 @@ } p { - // font-size: 20px; font-size: 15px; text-align: left; font-style: italic; @@ -92,12 +89,12 @@ max-width: 700px; text-align: left; font-style: normal; - font-size: 15; + font-size: 14; font-weight: normal; padding: 0; .padding { - padding: 0 0 0 20px; + padding: 0 0 0 10px; color: black; } diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 3d796539c..521e5366c 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -40,11 +40,17 @@ const ColorMapping = new Map([ [SharingPermissions.Edit, "green"] ]); -const HierarchyMapping = new Map([ - [SharingPermissions.None, 0], - [SharingPermissions.View, 1], - [SharingPermissions.Add, 2], - [SharingPermissions.Edit, 3] +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] + ]); const SharingKey = "sharingPermissions"; @@ -138,39 +144,51 @@ export default class SharingManager extends React.Component<{}> { const sharingDoc = this.sharingDoc!; if (permission === SharingPermissions.None) { - const metadata = sharingDoc[group[Id]]; - if (metadata) sharingDoc[group[Id]] = undefined; + const metadata = sharingDoc[StrCast(group.groupName)]; + if (metadata) sharingDoc[StrCast(group.groupName)] = undefined; } else { - sharingDoc[group[Id]] = permission; + sharingDoc[StrCast(group.groupName)] = permission; } users.forEach(user => { - this.setInternalSharing(user, permission); + this.setInternalSharing(user, permission, group); }); } - setInternalSharing = async (recipient: ValidatedUser, state: string) => { + setInternalSharing = async (recipient: ValidatedUser, state: string, group: Opt) => { const { user, notificationDoc } = recipient; const target = this.targetDoc!; const manager = this.sharingDoc!; const key = user.userDocumentId; - if (state === SharingPermissions.None) { - const metadata = (await DocCastAsync(manager[key])); - if (metadata) { - const sharedAlias = (await DocCastAsync(metadata.sharedAlias))!; - Doc.RemoveDocFromList(notificationDoc, storage, sharedAlias); - manager[key] = undefined; - } - } else { - const sharedAlias = Doc.MakeAlias(target); - Doc.AddDocToList(notificationDoc, storage, sharedAlias); - const metadata = new Doc; - metadata.permissions = state; - metadata.sharedAlias = sharedAlias; - manager[key] = metadata; + 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)); + + 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); + break; } + + if (metadata) metadata.maxPermission = HierarchyMapping.get(`${max}`); } private setExternalSharing = (state: string) => { @@ -245,7 +263,7 @@ export default class SharingManager extends React.Component<{}> { if (!metadata) { return SharingPermissions.None; } - return StrCast(metadata instanceof Doc ? metadata.permissions : metadata, SharingPermissions.None); + return StrCast(metadata instanceof Doc ? metadata.maxPermission : metadata, SharingPermissions.None); } @@ -302,7 +320,7 @@ export default class SharingManager extends React.Component<{}> { className={"permissions-dropdown"} value={permissions} style={{ color, borderColor: color }} - onChange={e => this.setInternalSharing({ user, notificationDoc }, e.currentTarget.value)} + onChange={e => this.setInternalSharing({ user, notificationDoc }, e.currentTarget.value, undefined)} > {this.sharingOptions} @@ -318,11 +336,11 @@ export default class SharingManager extends React.Component<{}> {
{/*200*/} {!existGroups ? "There are no groups in your database." : this.groups.map(group => { - const permissions = this.computePermissions(group[Id]); + const permissions = this.computePermissions(StrCast(group.groupName)); const color = ColorMapping.get(permissions); return (
GroupManager.Instance.addMemberToGroup(this.props.group, (selectedOption as UserOptions).value)} + placeholder={"Add members"} + value={null} + closeMenuOnSelect={true} + /> +
+ +
: + null} +
+
+ {members.map(member => ( +
+
+ {member} +
+ {GroupManager.Instance.hasEditAccess(this.props.group) ? : null} +
+ ))} +
+
+ ); + + } + + render() { + return ; + } + + +} \ No newline at end of file diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index 7b7412ec7..aac9a33e8 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -1,11 +1,33 @@ +@import "../views/globalCssVariables"; + .sharing-interface { display: flex; flex-direction: column; width: 730px; + .dialogue-box { + width: 450; + height: 300; + } + .sharing-contents { display: flex; + button { + background: $darker-alt-accent; + outline: none; + border-radius: 5px; + border: 0px; + color: #fcfbf7; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 75%; + padding: 0 10; + margin: 0 5; + transition: transform 0.2s; + height: 25; + } + .individual-container, .group-container { width: 50%; @@ -72,12 +94,10 @@ } } - - .container { - display: block; + display: flex; position: relative; - margin-top: 10px; + margin-top: 5px; margin-bottom: 10px; font-size: 22px; -webkit-user-select: none; @@ -92,15 +112,24 @@ font-size: 14; font-weight: normal; padding: 0; + align-items: baseline; .padding { - padding: 0 0 0 10px; + padding: 0 10px 0 0; color: black; } .permissions-dropdown { outline: none; + height: 25; } + + .edit-actions { + display: flex; + position: absolute; + right: 51.5%; + } + } .no-users { diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 521e5366c..1f62c824d 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -18,6 +18,7 @@ import { DocumentManager } from "./DocumentManager"; import { CollectionView } from "../views/collections/CollectionView"; import { DictationOverlay } from "../views/DictationOverlay"; import GroupManager from "./GroupManager"; +import GroupMemberView from "./GroupMemberView"; library.add(fa.faCopy); @@ -75,6 +76,7 @@ export default class SharingManager extends React.Component<{}> { @observable private copied = false; @observable private dialogueBoxOpacity = 1; @observable private overlayOpacity = 0.4; + @observable private groupToView: Opt; private get linkVisible() { return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; @@ -266,12 +268,18 @@ export default class SharingManager extends React.Component<{}> { return StrCast(metadata instanceof Doc ? metadata.maxPermission : metadata, SharingPermissions.None); } - private get sharingInterface() { const existOtherUsers = this.users.length > 0; const existGroups = this.groups.length > 0; return (
+ {this.groupToView ? + this.groupToView = undefined)} + /> : + null} +

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

{!this.linkVisible ? (null) :
@@ -316,15 +324,17 @@ export default class SharingManager extends React.Component<{}> { key={userKey} className={"container"} > - {user.email} +
+ +
); }) @@ -343,15 +353,18 @@ export default class SharingManager extends React.Component<{}> { key={StrCast(group.groupName)} className={"container"} > - {group.groupName} +
+ + +
); }) -- cgit v1.2.3-70-g09d2 From cb8b5b3d9e735c88b9efdd9b4a444cac40fdedb2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 29 Jun 2020 10:55:27 -0400 Subject: cleaned up undo handling on internal drag/drop --- src/client/util/CurrentUserUtils.ts | 2 +- src/client/util/DragManager.ts | 4 ++-- src/client/views/collections/CollectionSubView.tsx | 1 - .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 1 - 4 files changed, 3 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index d0ca0e57e..9c1881ef8 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -654,7 +654,7 @@ export class CurrentUserUtils { // Finally, setup the list of buttons to display in the sidebar if (doc["tabs-buttons"] === undefined) { - doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([searchBtn, libraryBtn, toolsBtn], { + doc["tabs-buttons"] = new PrefetchProxy(Docs.Create.StackingDocument([libraryBtn, searchBtn, toolsBtn], { _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", _columnsHideIfEmpty: true, ignoreClick: true, _chromeStatus: "view-mode", title: "sidebar btn row stack", backgroundColor: "dimGray", })); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 0db3963b2..2ceafff30 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -202,7 +202,6 @@ export namespace DragManager { dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(dropDoc); return dropDoc; }; - const batch = UndoManager.StartBatch("dragging"); const finishDrag = (e: DragCompleteEvent) => { const docDragData = e.docDragData; if (docDragData && !docDragData.droppedDocuments.length) { @@ -216,7 +215,6 @@ export namespace DragManager { const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps)); remProps.map(prop => drop[prop] = undefined); }); - batch.end(); } return e; }; @@ -315,6 +313,7 @@ export namespace DragManager { export let docsBeingDragged: Doc[] = []; export let CanEmbed = false; export function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) { + const batch = UndoManager.StartBatch("dragging"); eles = eles.filter(e => e); CanEmbed = false; if (!dragDiv) { @@ -449,6 +448,7 @@ export namespace DragManager { document.removeEventListener("pointermove", moveHandler, true); document.removeEventListener("pointerup", upHandler); SnappingManager.clearSnapLines(); + batch.end(); }); AbortDrag = () => { diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 49480e759..29144abaf 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -208,7 +208,6 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: addDocument = (doc: Doc | Doc[]) => this.props.addDocument(doc); - @undoBatch @action protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean { const docDragData = de.complete.docDragData; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ba20e9830..546a4307c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -194,7 +194,6 @@ export class CollectionFreeFormView extends CollectionSubView super.onExternalDrop(e, { x: pt[0], y: pt[1] }))(this.getTransform().transformPoint(e.pageX, e.pageY)); } - @undoBatch @action internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) { if (!super.onInternalDrop(e, de)) return false; -- cgit v1.2.3-70-g09d2 From ba44a0aff2a4c78d3fc033981c7b2daa740cb017 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Tue, 30 Jun 2020 00:13:41 +0530 Subject: pull request for Sally --- package-lock.json | 16 +++++++++++++++- src/client/util/GroupManager.tsx | 2 +- src/client/util/SharingManager.scss | 4 ++++ src/client/util/SharingManager.tsx | 29 +++++++++++++++++++++++++++-- 4 files changed, 47 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/package-lock.json b/package-lock.json index ae8818538..53b2fc009 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1050,6 +1050,15 @@ "@types/react": "*" } }, + "@types/react-transition-group": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", + "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/reactcss": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.3.tgz", @@ -8354,6 +8363,11 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==" + }, "memory-fs": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", @@ -17832,4 +17846,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 885f9da8e..7c68fc2a0 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -119,7 +119,7 @@ export default class GroupManager extends React.Component<{}> { */ getAllGroupsCopy(): Doc[] { return this.getAllGroups().map(({ groupName, owners, members }) => - Doc.assign(new Doc, { groupName: (groupName as string), owners: (owners as string), members: (members as string) }) + Doc.assign(new Doc, { groupName: (StrCast(groupName)), owners: (StrCast(owners)), members: (StrCast(members)) }) ); } diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index aac9a33e8..fcbc05f8a 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -10,6 +10,10 @@ height: 300; } + .overlay { + transform: translate(-20px, -20px); + } + .sharing-contents { display: flex; diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 1f62c824d..127ee33ce 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -169,6 +169,19 @@ export default class SharingManager extends React.Component<{}> { 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; + // } + // } + switch (max) { case 0: if (metadata) { @@ -187,6 +200,7 @@ export default class SharingManager extends React.Component<{}> { manager[key] = metadata; } metadata.permissions = JSON.stringify(permissions); + // metadata.usersShared = JSON.stringify(keys); break; } @@ -271,6 +285,9 @@ export default class SharingManager extends React.Component<{}> { private get sharingInterface() { const existOtherUsers = this.users.length > 0; const existGroups = this.groups.length > 0; + + // const manager = this.sharingDoc!; + return (
{this.groupToView ? @@ -279,7 +296,6 @@ export default class SharingManager extends React.Component<{}> { onCloseButtonClick={action(() => this.groupToView = undefined)} /> : null} -

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

{!this.linkVisible ? (null) :
@@ -315,16 +331,24 @@ export default class SharingManager extends React.Component<{}> {

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

{/*200*/} {!existOtherUsers ? "There are no other users in your database." : - this.users.map(({ user, notificationDoc }) => { + 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}
*/}