diff options
-rw-r--r-- | src/client/util/GroupManager.scss | 99 | ||||
-rw-r--r-- | src/client/util/GroupManager.tsx | 74 | ||||
-rw-r--r-- | src/client/util/GroupMemberView.scss | 27 | ||||
-rw-r--r-- | src/client/util/GroupMemberView.tsx | 21 | ||||
-rw-r--r-- | src/client/util/SharingManager.scss | 23 | ||||
-rw-r--r-- | src/client/util/SharingManager.tsx | 68 |
6 files changed, 215 insertions, 97 deletions
diff --git a/src/client/util/GroupManager.scss b/src/client/util/GroupManager.scss index 2da1f0d95..8a2c616b1 100644 --- a/src/client/util/GroupManager.scss +++ b/src/client/util/GroupManager.scss @@ -20,9 +20,10 @@ // border: none; padding: 8px; min-width: 100%; - margin: 4px 0 4px 0; + // margin: 4px 0 4px 0; border: 1px solid hsl(0, 0%, 80%); outline: none; + height: 30; &:focus { // border: unset; @@ -100,59 +101,69 @@ } } + .main-container { + display: flex; + flex-direction: column; - .group-body { - // display: flex; - justify-content: space-between; - // max-height: 80%; - height: 100%; - background-color: #e8e8e8; - // flex-direction: column; + .sort-groups { + text-align: left; + margin-left: 5; + cursor: pointer; + } - // padding-left: 1em; - padding-right: 1em; - justify-content: space-around; - text-align: left; + .group-body { + // display: flex; + justify-content: space-between; + // max-height: 80%; + height: 220; + background-color: #e8e8e8; + // flex-direction: column; + + // padding-left: 1em; + padding-right: 1em; + justify-content: space-around; + text-align: left; - overflow-y: auto; - width: 100%; + overflow-y: auto; + width: 100%; - .group-row { - display: flex; - // position: relative; - margin-bottom: 5px; - min-height: 30px; - // border: 1px solid; - // border-radius: 10px; - align-items: center; - - .group-name { + .group-row { + display: flex; // position: relative; - max-width: 65%; - // left: 10; - margin: 0 10; - color: black; - } + margin-bottom: 5px; + min-height: 30px; + // border: 1px solid; + // border-radius: 10px; + align-items: center; + + .group-name { + // position: relative; + max-width: 65%; + // left: 10; + margin: 0 10; + color: black; + } - .group-info { - cursor: pointer; + .group-info { + cursor: pointer; + } + + button { + position: absolute; + width: 30%; + right: 2; + margin-top: 0; + } } - button { - position: absolute; - width: 30%; - right: 2; - margin-top: 0; + input { + border-radius: 5px; + border: none; + padding: 4px; + min-width: 100%; + margin: 2px 0; } - } - input { - border-radius: 5px; - border: none; - padding: 4px; - min-width: 100%; - margin: 2px 0; } - } }
\ No newline at end of file diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index f2b502ae0..2d8930660 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -38,31 +38,38 @@ export default class GroupManager extends React.Component<{}> { private inputRef: React.RefObject<HTMLInputElement> = React.createRef(); // the ref for the input box. private currentUserGroups: string[] = []; @observable private buttonColour: "#979797" | "black" = "#979797"; + @observable private groupSort: "ascending" | "descending" | "none" = "none"; + constructor(props: Readonly<{}>) { super(props); GroupManager.Instance = this; } + componentDidMount() { + this.populateUsers(); + } + /** * Fetches the list of users stored on the database. */ populateUsers = async () => { + runInAction(() => this.users = []); const userList = await RequestPromise.get(Utils.prepend("/getUsers")); const raw = JSON.parse(userList) as User[]; const evaluating = raw.map(async user => { - const isCandidate = user.email !== Doc.CurrentUserEmail; - if (isCandidate) { - const userDocument = await DocServer.GetRefField(user.userDocumentId); - if (userDocument instanceof Doc) { - const notificationDoc = await Cast(userDocument.rightSidebarCollection, Doc); - runInAction(() => { - if (notificationDoc instanceof Doc) { - this.users.push(user.email); - } - }); - } + // const isCandidate = user.email !== Doc.CurrentUserEmail; + // if (isCandidate) { + const userDocument = await DocServer.GetRefField(user.userDocumentId); + if (userDocument instanceof Doc) { + const notificationDoc = await Cast(userDocument.rightSidebarCollection, Doc); + runInAction(() => { + if (notificationDoc instanceof Doc) { + this.users.push(user.email); + } + }); } + // } }); return Promise.all(evaluating); } @@ -99,7 +106,7 @@ export default class GroupManager extends React.Component<{}> { close = () => { this.isOpen = false; this.currentGroup = undefined; - this.users = []; + // this.users = []; this.createGroupModalOpen = false; } @@ -304,6 +311,7 @@ export default class GroupManager extends React.Component<{}> { </div> </div> <input + className="group-input" ref={this.inputRef} onKeyDown={this.handleKeyDown} type="text" @@ -357,6 +365,17 @@ export default class GroupManager extends React.Component<{}> { * A getter that @returns the main interface for the GroupManager. */ private get groupInterface() { + + const sortGroups = (d1: Doc, d2: Doc) => { + const g1 = StrCast(d1.groupName); + const g2 = StrCast(d2.groupName); + + return g1 < g2 ? -1 : g1 === g2 ? 0 : 1; + }; + + let groups = this.getAllGroups(); + groups = this.groupSort === "ascending" ? groups.sort(sortGroups) : this.groupSort === "descending" ? groups.sort(sortGroups).reverse() : groups; + return ( <div className="group-interface"> {this.groupCreationModal} @@ -375,19 +394,30 @@ export default class GroupManager extends React.Component<{}> { <FontAwesomeIcon icon={fa.faTimes} color={"black"} size={"lg"} /> </div> </div> - <div className="group-body"> - {this.getAllGroups().map(group => - <div className="group-row"> - <div className="group-name" >{group.groupName}</div> - <div className="group-info" onClick={action(() => this.currentGroup = group)}> - <FontAwesomeIcon icon={fa.faInfoCircle} color={"#e8e8e8"} size={"sm"} style={{ backgroundColor: "#1e89d7", borderRadius: "100%", border: "1px solid #1e89d7" }} /> - </div> - {/* <button onClick={action(() => this.currentGroup = group)}> + <div className="main-container"> + <div + className="sort-groups" + onClick={action(() => this.groupSort = this.groupSort === "ascending" ? "descending" : this.groupSort === "descending" ? "none" : "ascending")}> + Name {this.groupSort === "ascending" ? "↑" : this.groupSort === "descending" ? "↓" : ""} {/* → */} + </div> + <div className="group-body"> + {groups.map(group => + <div + className="group-row" + key={StrCast(group.groupName)} + > + <div className="group-name" >{group.groupName}</div> + <div className="group-info" onClick={action(() => this.currentGroup = group)}> + <FontAwesomeIcon icon={fa.faInfoCircle} color={"#e8e8e8"} size={"sm"} style={{ backgroundColor: "#1e89d7", borderRadius: "100%", border: "1px solid #1e89d7" }} /> + </div> + {/* <button onClick={action(() => this.currentGroup = group)}> {this.hasEditAccess(group) ? "Edit" : "View"} </button> */} - </div> - )} + </div> + )} + </div> </div> + </div> ); } diff --git a/src/client/util/GroupMemberView.scss b/src/client/util/GroupMemberView.scss index 9bdf832e0..a34e5b989 100644 --- a/src/client/util/GroupMemberView.scss +++ b/src/client/util/GroupMemberView.scss @@ -8,7 +8,7 @@ // color: black; hr { - margin-top: 10; + margin-top: 20; } button { @@ -17,7 +17,7 @@ border-radius: 5px; border: 0px; color: #fcfbf7; - // text-transform: uppercase; + text-transform: none; letter-spacing: 2px; font-size: 75%; padding: 10px; @@ -45,15 +45,33 @@ outline: none; color: black; margin-top: -5; + height: 20; + text-overflow: ellipsis; + + &:hover { + text-overflow: visible; + overflow-x: auto; + } + } + + .sort-emails { + float: left; + margin: -18 0 0 5; + cursor: pointer; } .group-buttons { display: flex; margin-top: 5; + margin-bottom: 25; .add-member-dropdown { width: 65%; margin: 0 5; + + input { + height: 30; + } } } } @@ -61,7 +79,7 @@ .editing-contents { overflow-y: auto; // max-height: 67%; - height: 67%; + height: 65%; width: 100%; color: black; margin-top: -15px; @@ -70,6 +88,7 @@ display: flex; align-items: center; margin-bottom: 10px; + position: relative; // border: 1px solid; // border-radius: 10px; @@ -83,7 +102,7 @@ .remove-button { position: absolute; - right: 30; + right: 10; cursor: pointer; } } diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index 2462ee4d5..ebe9830ba 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react"; import GroupManager, { UserOptions } from "./GroupManager"; import { library } from "@fortawesome/fontawesome-svg-core"; import { StrCast } from "../../fields/Types"; -import { action } from "mobx"; +import { action, observable } from "mobx"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import * as fa from '@fortawesome/free-solid-svg-icons'; import Select from "react-select"; @@ -21,10 +21,17 @@ interface GroupMemberViewProps { @observer export default class GroupMemberView extends React.Component<GroupMemberViewProps> { + @observable private memberSort: "ascending" | "descending" | "none" = "none"; private get editingInterface() { - const members: string[] = this.props.group ? JSON.parse(StrCast(this.props.group.members)) : []; + let members: string[] = this.props.group ? JSON.parse(StrCast(this.props.group.members)) : []; + members = this.memberSort === "ascending" ? members.sort() : this.memberSort === "descending" ? members.sort().reverse() : members; + const options: UserOptions[] = this.props.group ? GroupManager.Instance.options.filter(option => !(JSON.parse(StrCast(this.props.group.members)) as string[]).includes(option.value)) : []; + console.log(this.props.group, options); + console.log(GroupManager.Instance.options); + + return (!this.props.group ? null : <div className="editing-interface"> <div className="editing-header"> @@ -59,11 +66,19 @@ export default class GroupMemberView extends React.Component<GroupMemberViewProp <button onClick={() => GroupManager.Instance.deleteGroup(this.props.group)}>Delete group</button> </div> : null} + <div + className="sort-emails" + onClick={action(() => this.memberSort = this.memberSort === "ascending" ? "descending" : this.memberSort === "descending" ? "none" : "ascending")}> + Emails {this.memberSort === "ascending" ? "↑" : this.memberSort === "descending" ? "↓" : ""} {/* → */} + </div> </div> <hr /> <div className="editing-contents"> {members.map(member => ( - <div className="editing-row"> + <div + className="editing-row" + key={member} + > <div className="user-email"> {member} </div> diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index ce23ce413..572b94ffb 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -1,4 +1,4 @@ -@import "../views/globalCssVariables"; +// @import "../views/globalCssVariables"; .sharing-interface { // display: flex; @@ -40,6 +40,10 @@ .user-search { width: 90%; + + input { + height: 30; + } } .permissions-select { @@ -68,6 +72,12 @@ display: flex; flex-direction: column; + .user-sort { + text-align: left; + margin-left: 10; + cursor: pointer; + } + .share-title { margin-top: 20px; margin-bottom: 20px; @@ -103,12 +113,12 @@ } button { - background: $darker-alt-accent; + // background: $darker-alt-accent; outline: none; border-radius: 5px; border: 0px; color: #fcfbf7; - text-transform: uppercase; + text-transform: none; letter-spacing: 2px; font-size: 75%; padding: 0 10; @@ -173,6 +183,10 @@ padding: 0; align-items: center; + .group-info { + cursor: pointer; + } + &:hover .padding { white-space: unset; } @@ -183,12 +197,13 @@ text-overflow: ellipsis; overflow: hidden; white-space: nowrap; - max-width: 48%; + max-width: 40%; } .permissions-dropdown { border: none; height: 25; + background-color: #e8e8e8; } .edit-actions { diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 6c7c634eb..817b7c6b8 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -75,6 +75,9 @@ 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 individualSort: "ascending" | "descending" | "none" = "none"; + @observable private groupSort: "ascending" | "descending" | "none" = "none"; + // private get linkVisible() { // return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; @@ -151,8 +154,6 @@ export default class SharingManager extends React.Component<{}> { // 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 }) => { - - 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); @@ -353,28 +354,44 @@ export default class SharingManager extends React.Component<{}> { this.selectedUsers = null; } + sortUsers = (u1: ValidatedUser, u2: ValidatedUser) => { + const { email: e1 } = u1.user; + const { email: e2 } = u2.user; + return e1 < e2 ? -1 : e1 === e2 ? 0 : 1; + } + + sortGroups = (group1: Doc, group2: Doc) => { + const g1 = StrCast(group1.groupName); + const g2 = StrCast(group2.groupName); + return g1 < g2 ? -1 : g1 === g2 ? 0 : 1; + } + private get sharingInterface() { - const existOtherUsers = this.users.length > 0; - const existGroups = GroupManager.Instance?.getAllGroups().length > 0; - // const manager = this.sharingDoc!; + const groupList = GroupManager.Instance?.getAllGroups() || []; + + const sortedUsers = this.users.sort(this.sortUsers) + .map(({ user: { email } }) => ({ label: email, value: "!indType/" + email })); + const sortedGroups = groupList.sort(this.sortGroups) + .map(({ groupName }) => ({ label: StrCast(groupName), value: "!groupType/" + StrCast(groupName) })); const options: GroupOptions[] = GroupManager.Instance ? [ { label: 'Individuals', - options: this.users.map(({ user: { email } }) => ({ label: email, value: "!indType/" + email })) + options: sortedUsers }, { label: 'Groups', - options: GroupManager.Instance.getAllGroups().map(({ groupName }) => ({ label: StrCast(groupName), value: "!groupType/" + StrCast(groupName) })) + options: sortedGroups } ] : []; - console.log(this.users); + const users = this.individualSort === "ascending" ? this.users.sort(this.sortUsers) : this.individualSort === "descending" ? this.users.sort(this.sortUsers).reverse() : this.users; + const groups = this.groupSort === "ascending" ? groupList.sort(this.sortGroups) : this.groupSort === "descending" ? groupList.sort(this.sortGroups).reverse() : groupList; - const userListContents: (JSX.Element | null)[] = this.users.map(({ user, notificationDoc }) => { // can't use async here + const userListContents: (JSX.Element | null)[] = users.map(({ user, notificationDoc }) => { // can't use async here const userKey = user.email.replace('.', '_'); // const userKey = user.userDocumentId; const permissions = StrCast(this.targetDoc?.[`ACL-${userKey}`], SharingPermissions.None); @@ -422,7 +439,7 @@ export default class SharingManager extends React.Component<{}> { ) ); - const groupListContents = GroupManager.Instance?.getAllGroups().map(group => { + const groupListContents = groups.map(group => { const permissions = StrCast(this.targetDoc?.[`ACL-${StrCast(group.groupName)}`], SharingPermissions.None); // const color = ColorMapping.get(permissions); @@ -431,7 +448,10 @@ export default class SharingManager extends React.Component<{}> { key={StrCast(group.groupName)} className={"container"} > - <span className={"padding"}>{group.groupName}</span> + <div className={"padding"}>{group.groupName}</div> + <div className="group-info" onClick={action(() => GroupManager.Instance.currentGroup = group)}> + <FontAwesomeIcon icon={fa.faInfoCircle} color={"#e8e8e8"} size={"sm"} style={{ backgroundColor: "#1e89d7", borderRadius: "100%", border: "1px solid #1e89d7" }} /> + </div> <div className="edit-actions"> <select className={"permissions-dropdown"} @@ -441,14 +461,13 @@ export default class SharingManager extends React.Component<{}> { > {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); + const displayUserList = !userListContents?.every(user => user === null); + const displayGroupList = !groupListContents?.every(group => group === null); return ( <div className={"sharing-interface"}> @@ -515,10 +534,14 @@ export default class SharingManager extends React.Component<{}> { } <div className="main-container"> <div className={"individual-container"}> - <div>Individuals</div> - <div className={"users-list"} style={{ display: displayUserList ? "flex" : "block" }}>{/*200*/} + <div + className="user-sort" + onClick={action(() => this.individualSort = this.individualSort === "ascending" ? "descending" : this.individualSort === "descending" ? "none" : "ascending")}> + Individuals {this.individualSort === "ascending" ? "↑" : this.individualSort === "descending" ? "↓" : ""} {/* → */} + </div> + <div className={"users-list"} style={{ display: !displayUserList ? "flex" : "block" }}>{/*200*/} { - displayUserList ? + !displayUserList ? <div className={"none"} > @@ -530,10 +553,15 @@ export default class SharingManager extends React.Component<{}> { </div> </div> <div className={"group-container"}> - <div>Groups</div> - <div className={"groups-list"} style={{ display: displayGroupList ? "flex" : "block" }}>{/*200*/} + <div + className="user-sort" + onClick={action(() => this.groupSort = this.groupSort === "ascending" ? "descending" : this.groupSort === "descending" ? "none" : "ascending")}> + Groups {this.groupSort === "ascending" ? "↑" : this.groupSort === "descending" ? "↓" : ""} {/* → */} + + </div> + <div className={"groups-list"} style={{ display: !displayGroupList ? "flex" : "block" }}>{/*200*/} { - displayGroupList ? + !displayGroupList ? <div className={"none"} > |