diff options
-rw-r--r-- | src/client/util/GroupManager.tsx | 27 | ||||
-rw-r--r-- | src/client/util/SharingManager.scss | 62 | ||||
-rw-r--r-- | src/client/util/SharingManager.tsx | 121 |
3 files changed, 156 insertions, 54 deletions
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 7f48ca014..fa8da86c1 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -88,7 +88,7 @@ export default class GroupManager extends React.Component<{}> { /** * @returns a list of all group documents. */ - getAllGroups(): Doc[] { + private getAllGroups(): Doc[] { const groupDoc = this.GroupManagerDoc; return groupDoc ? DocListCast(groupDoc.data) : []; } @@ -97,12 +97,32 @@ export default class GroupManager extends React.Component<{}> { * @returns a group document based on the group name. * @param groupName */ - getGroup(groupName: string): Doc | undefined { + private getGroup(groupName: string): Doc | undefined { const groupDoc = this.getAllGroups().find(group => group.groupName === groupName); return groupDoc; } /** + * @returns a readonly copy of a single group document + */ + getGroupCopy(groupName: string): Doc | undefined { + const groupDoc = this.getGroup(groupName); + if (groupDoc) { + const { members, owners } = groupDoc; + return Doc.assign(new Doc, { groupName, members: StrCast(members), owners: StrCast(owners) }); + } + return undefined; + } + /** + * @returns a readonly copy of the list of group documents + */ + 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) }) + ); + } + + /** * @returns the members of the admin group. */ get adminGroupMembers(): string[] { @@ -194,7 +214,6 @@ export default class GroupManager extends React.Component<{}> { */ @action handleChange = (selectedOptions: any) => { - console.log(selectedOptions); this.selectedUsers = selectedOptions as UserOptions[]; } @@ -312,7 +331,7 @@ export default class GroupManager extends React.Component<{}> { <div className="group-row"> <div className="group-name">{group.groupName}</div> <button onClick={action(() => this.currentGroup = group)}> - {this.hasEditAccess(this.getGroup(StrCast(group.groupName)) as Doc) ? "Edit" : "View"} + {this.hasEditAccess(group) ? "Edit" : "View"} </button> </div> )} diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index dec9f751a..1aff345a0 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -1,13 +1,52 @@ .sharing-interface { display: flex; flex-direction: column; + width: 730px; + + .sharing-contents { + display: flex; + + .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; + // max-height: 200px; + overflow: scroll; + height: -webkit-fill-available; + text-align: left; + display: flex; + align-content: center; + align-items: center; + text-align: center; + justify-content: center; + color: red; + + // width: 50%; + max-height: 150px; + } + } + } .focus-span { text-decoration: underline; } p { - font-size: 20px; + // font-size: 20px; + font-size: 15px; text-align: left; font-style: italic; padding: 0; @@ -36,28 +75,7 @@ } } - .share-individual { - margin-top: 20px; - margin-bottom: 20px; - } - .users-list { - font-style: italic; - background: white; - border: 1px solid black; - padding-left: 10px; - padding-right: 10px; - max-height: 200px; - overflow: scroll; - height: -webkit-fill-available; - text-align: left; - display: flex; - align-content: center; - align-items: center; - text-align: center; - justify-content: center; - color: red; - } .container { display: block; diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 2e660e819..3d796539c 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -17,6 +17,7 @@ import { SelectionManager } from "./SelectionManager"; import { DocumentManager } from "./DocumentManager"; import { CollectionView } from "../views/collections/CollectionView"; import { DictationOverlay } from "../views/DictationOverlay"; +import GroupManager from "./GroupManager"; library.add(fa.faCopy); @@ -28,7 +29,7 @@ export interface User { export enum SharingPermissions { None = "Not Shared", View = "Can View", - Add = "Can Add and Comment", + Add = "Can Add", Edit = "Can Edit" } @@ -39,6 +40,13 @@ const ColorMapping = new Map<string, string>([ [SharingPermissions.Edit, "green"] ]); +const HierarchyMapping = new Map<string, number>([ + [SharingPermissions.None, 0], + [SharingPermissions.View, 1], + [SharingPermissions.Add, 2], + [SharingPermissions.Edit, 3] +]); + const SharingKey = "sharingPermissions"; const PublicKey = "publicLinkPermissions"; const DefaultColor = "black"; @@ -55,6 +63,7 @@ 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 targetDoc: Doc | undefined; @observable private targetDocView: DocumentView | undefined; @observable private copied = false; @@ -76,6 +85,8 @@ export default class SharingManager extends React.Component<{}> { this.sharingDoc = new Doc; } })); + + runInAction(() => this.groups = GroupManager.Instance.getAllGroupsCopy()); } public close = action(() => { @@ -121,11 +132,30 @@ export default class SharingManager extends React.Component<{}> { return Promise.all(evaluating); } + setInternalGroupSharing = (group: Doc, permission: string) => { + const members: string[] = JSON.parse(StrCast(group.members)); + const users: ValidatedUser[] = this.users.filter(user => members.includes(user.user.email)); + + const sharingDoc = this.sharingDoc!; + if (permission === SharingPermissions.None) { + const metadata = sharingDoc[group[Id]]; + if (metadata) sharingDoc[group[Id]] = undefined; + } + else { + sharingDoc[group[Id]] = permission; + } + + users.forEach(user => { + this.setInternalSharing(user, permission); + }); + } + setInternalSharing = async (recipient: ValidatedUser, state: string) => { 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) { @@ -211,15 +241,17 @@ export default class SharingManager extends React.Component<{}> { if (!sharingDoc) { return SharingPermissions.None; } - const metadata = sharingDoc[userKey] as Doc; + const metadata = sharingDoc[userKey] as Doc | string; if (!metadata) { return SharingPermissions.None; } - return StrCast(metadata.permissions, SharingPermissions.None); + return StrCast(metadata instanceof Doc ? metadata.permissions : metadata, SharingPermissions.None); } + private get sharingInterface() { const existOtherUsers = this.users.length > 0; + const existGroups = this.groups.length > 0; return ( <div className={"sharing-interface"}> <p className={"share-link"}>Manage the public link to {this.focusOn("this document...")}</p> @@ -252,31 +284,64 @@ export default class SharingManager extends React.Component<{}> { </select> </div> <div className={"hr-substitute"} /> - <p className={"share-individual"}>Privately share {this.focusOn("this document")} with an individual...</p> - <div className={"users-list"} style={{ display: existOtherUsers ? "block" : "flex", minHeight: existOtherUsers ? undefined : 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 ( - <div - key={userKey} - className={"container"} - > - <select - className={"permissions-dropdown"} - value={permissions} - style={{ color, borderColor: color }} - onChange={e => this.setInternalSharing({ user, notificationDoc }, e.currentTarget.value)} - > - {this.sharingOptions} - </select> - <span className={"padding"}>{user.email}</span> - </div> - ); - }) - } + <div className="sharing-contents"> + <div className={"individual-container"}> + <p className={"share-individual"}>Privately share {this.focusOn("this document")} with an individual...</p> + <div className={"users-list"} style={{ display: existOtherUsers ? "block" : "flex", minHeight: existOtherUsers ? undefined : 150 }}>{/*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 ( + <div + key={userKey} + className={"container"} + > + <select + className={"permissions-dropdown"} + value={permissions} + style={{ color, borderColor: color }} + onChange={e => this.setInternalSharing({ user, notificationDoc }, e.currentTarget.value)} + > + {this.sharingOptions} + </select> + <span className={"padding"}>{user.email}</span> + </div> + ); + }) + } + </div> + </div> + <div className={"group-container"}> + <p className={"share-groups"}>Privately share {this.focusOn("this document")} with a group...</p> + <div className={"groups-list"} style={{ display: existGroups ? "block" : "flex", minHeight: existOtherUsers ? undefined : 150 }}>{/*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 ( + <div + key={group[Id]} + className={"container"} + > + <select + className={"permissions-dropdown"} + value={permissions} + style={{ color, borderColor: color }} + onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)} + > + {this.sharingOptions} + </select> + <span className={"padding"}>{group.groupName}</span> + </div> + ); + }) + + } + + </div> + </div> </div> <div className={"close-button"} onClick={this.close}>Done</div> </div> |