diff options
-rw-r--r-- | src/client/DocServer.ts | 2 | ||||
-rw-r--r-- | src/client/util/GroupManager.tsx | 11 | ||||
-rw-r--r-- | src/client/util/SharingManager.scss | 62 | ||||
-rw-r--r-- | src/client/util/SharingManager.tsx | 258 | ||||
-rw-r--r-- | src/client/views/MainViewModal.scss | 5 | ||||
-rw-r--r-- | src/client/views/MainViewModal.tsx | 1 |
6 files changed, 218 insertions, 121 deletions
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<Doc>).push(target) : group.docsShared = new List<Doc>([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 : ( + <div + key={userKey} + className={"container"} + > + <span className={"padding"}>{user.email}</span> + {/* <div className={"shared-by"}>{usersShared}</div> */} + <div className="edit-actions"> + <select + className={"permissions-dropdown"} + value={permissions} + // style={{ color, borderColor: color }} + onChange={e => this.setInternalSharing({ user, notificationDoc }, e.currentTarget.value)} + > + {this.sharingOptions} + </select> + </div> + </div> + ); + }); + + + const groupListContents = GroupManager.Instance?.getAllGroups().map(group => { + const permissions = this.computePermissions(StrCast(group.groupName)); + // const color = ColorMapping.get(permissions); + + return permissions === SharingPermissions.None ? null : ( + <div + key={StrCast(group.groupName)} + className={"container"} + > + <span className={"padding"}>{group.groupName}</span> + <div className="edit-actions"> + <select + className={"permissions-dropdown"} + value={permissions} + // style={{ color, borderColor: color }} + onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)} + > + {this.sharingOptions} + </select> + <button onClick={action(() => GroupManager.Instance.currentGroup = group)}>Edit</button> + </div> + </div> + ); + }); + + const displayUserList = userListContents?.every(user => user === null); + const displayGroupList = groupListContents?.every(group => group === null); + return ( <div className={"sharing-interface"}> {GroupManager.Instance?.currentGroup ? @@ -385,6 +490,9 @@ export default class SharingManager extends React.Component<{}> { <div className={"hr-substitute"} /> */} <div className="sharing-contents"> <p className={"share-title"}><b>Share </b>{this.focusOn(StrCast(this.targetDoc?.title, "this document"))}</p> + <div className={"close-button"} onClick={this.close}> + <FontAwesomeIcon icon={fa.faWindowClose} size={"lg"} /> + </div> {this.targetDoc?.author !== Doc.CurrentUserEmail ? null : <div className="share-setup"> @@ -407,69 +515,30 @@ export default class SharingManager extends React.Component<{}> { } <div className="main-container"> <div className={"individual-container"}> - <div className={"users-list"} style={{ display: existOtherUsers ? "block" : "flex", minHeight: existOtherUsers ? undefined : 150 }}>{/*200*/} + <div className={"users-list"} style={{ display: displayUserList ? "flex" : "block" }}>{/*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 : ( - <div - key={userKey} - className={"container"} - > - <span className={"padding"}>{user.email}</span> - {/* <div className={"shared-by"}>{usersShared}</div> */} - <div className="edit-actions"> - <select - className={"permissions-dropdown"} - value={permissions} - // style={{ color, borderColor: color }} - onChange={e => this.setInternalSharing({ user, notificationDoc }, e.currentTarget.value)} - > - {this.sharingOptions} - </select> - </div> - </div> - ); - }) + displayUserList ? + <div + className={"none"} + > + There are no users this document has been shared with. + </div> + : + userListContents } </div> </div> <div className={"group-container"}> - <div className={"groups-list"} style={{ display: existGroups ? "block" : "flex", minHeight: existOtherUsers ? undefined : 150 }}>{/*200*/} + <div className={"groups-list"} style={{ display: displayGroupList ? "flex" : "block" }}>{/*200*/} { - GroupManager.Instance?.getAllGroups().map(group => { - const permissions = this.computePermissions(StrCast(group.groupName)); - // const color = ColorMapping.get(permissions); - return permissions === SharingPermissions.None ? null : ( - <div - key={StrCast(group.groupName)} - className={"container"} - > - <span className={"padding"}>{group.groupName}</span> - <div className="edit-actions"> - <select - className={"permissions-dropdown"} - value={permissions} - // style={{ color, borderColor: color }} - onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)} - > - {this.sharingOptions} - </select> - <button onClick={action(() => GroupManager.Instance.currentGroup = group)}>Edit</button> - </div> + displayGroupList ? + <div + className={"none"} + > + There are no groups this document has been shared with. </div> - ); - }) - + : + groupListContents } </div> @@ -477,7 +546,6 @@ export default class SharingManager extends React.Component<{}> { </div> </div> - <div className={"close-button"} onClick={this.close}>Done</div> </div> ); } 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<MainViewOverlayProps> <div className={"dialogue-box"} style={{ - backgroundColor: "gainsboro", borderColor: "black", ...(p.dialogueBoxStyle || {}), opacity: p.isDisplayed ? dialogueOpacity : 0 |