diff options
Diffstat (limited to 'src/client/util/SharingManager.tsx')
-rw-r--r-- | src/client/util/SharingManager.tsx | 435 |
1 files changed, 218 insertions, 217 deletions
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 0eecb31cf..6171c01d7 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -1,14 +1,16 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { intersection } from 'lodash'; +import { Button, IconButton, Size, Type } from 'browndash-components'; +import { concat, intersection } from 'lodash'; import { action, computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import Select from 'react-select'; import * as RequestPromise from 'request-promise'; -import { AclAdmin, AclPrivate, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, HierarchyMapping } from '../../fields/Doc'; +import { Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc'; +import { AclAdmin, AclPrivate, DocAcl, DocData } from '../../fields/DocSymbols'; +import { FieldLoader } from '../../fields/FieldLoader'; import { Id } from '../../fields/FieldSymbols'; -import { List } from '../../fields/List'; -import { NumCast, StrCast } from '../../fields/Types'; +import { StrCast } from '../../fields/Types'; import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util'; import { Utils } from '../../Utils'; import { DocServer } from '../DocServer'; @@ -20,8 +22,11 @@ import { SearchBox } from '../views/search/SearchBox'; import { DocumentManager } from './DocumentManager'; import { GroupManager, UserOptions } from './GroupManager'; import { GroupMemberView } from './GroupMemberView'; +import { LinkManager } from './LinkManager'; import { SelectionManager } from './SelectionManager'; +import { SettingsManager } from './SettingsManager'; import './SharingManager.scss'; +import { undoable } from './UndoManager'; export interface User { email: string; @@ -46,6 +51,7 @@ const indType = '!indType/'; const groupType = '!groupType/'; const storage = 'data'; +const dashStorage = 'data_dashboards'; /** * A user who also has a sharing doc. @@ -72,13 +78,14 @@ export class SharingManager extends React.Component<{}> { @observable private individualSort: 'ascending' | 'descending' | 'none' = 'none'; // sorting options for the list of individuals @observable private groupSort: 'ascending' | 'descending' | 'none' = 'none'; // sorting options for the list of groups private shareDocumentButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // ref for the share button, used for the position of the popup - private distributeAclsButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // ref for the distribute button, used for the position of the popup // if both showUserOptions and showGroupOptions are false then both are displayed @observable private showUserOptions: boolean = false; // whether to show individuals as options when sharing (in the react-select component) @observable private showGroupOptions: boolean = false; // // whether to show groups as options when sharing (in the react-select component) private populating: boolean = false; // whether the list of users is populating or not + @observable private upgradeNested: boolean = false; // whether child docs in a collection/dashboard should be changed to be less private - initially selected so default is upgrade all @observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used @observable private myDocAcls: boolean = false; // whether the My Docs checkbox is selected or not + @observable private _buttonDown = false; // private get linkVisible() { // return this.targetDoc ? this.targetDoc["acl-" + PublicKey] !== SharingPermissions.None : false; @@ -92,6 +99,7 @@ export class SharingManager extends React.Component<{}> { DictationOverlay.Instance.hasActiveModal = true; this.isOpen = this.targetDoc !== undefined; this.permissions = SharingPermissions.Augment; + this.upgradeNested = true; }); }; @@ -107,6 +115,7 @@ export class SharingManager extends React.Component<{}> { }), 500 ); + this.layoutDocAcls = false; }); constructor(props: {}) { @@ -129,6 +138,7 @@ export class SharingManager extends React.Component<{}> { this.populating = true; const userList = await RequestPromise.get(Utils.prepend('/getUsers')); const raw = (JSON.parse(userList) as User[]).filter(user => user.email !== 'guest' && user.email !== Doc.CurrentUserEmail); + runInAction(() => (FieldLoader.ServerLoadStatus.message = 'users')); const docs = await DocServer.GetRefFields(raw.reduce((list, user) => [...list, user.sharingDocumentId, user.linkDatabaseId], [] as string[])); raw.map( action((newUser: User) => { @@ -137,7 +147,7 @@ export class SharingManager extends React.Component<{}> { if (sharingDoc instanceof Doc && linkDatabase instanceof Doc) { if (!this.users.find(user => user.user.email === newUser.email)) { this.users.push({ user: newUser, sharingDoc, linkDatabase, userColor: StrCast(sharingDoc.userColor) }); - // LinkManager.addLinkDB(sharer.linkDatabase); + //LinkManager.addLinkDB(linkDatabase); } } }) @@ -149,79 +159,44 @@ export class SharingManager extends React.Component<{}> { /** * Shares the document with a user. */ - setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc?: Doc) => { + setInternalSharing = undoable((recipient: ValidatedUser, permission: string, targetDoc: Doc | undefined) => { const { user, sharingDoc } = recipient; const target = targetDoc || this.targetDoc!; const acl = `acl-${normalizeEmail(user.email)}`; - const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`; - const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1; - - const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document); - return !docs - .map(doc => (this.layoutDocAcls ? doc : doc[DataSym])) - .map(doc => { - doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard); - - if (permission === SharingPermissions.None) { - if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 1) - 1; - } else { - if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1; - } - - distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard); - - this.setDashboardBackground(doc, permission as SharingPermissions); - if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); - return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc); - }) - .some(success => !success); - }; + const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc); + docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => { + distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.upgradeNested ? true : undefined); + if (permission !== SharingPermissions.None) { + Doc.AddDocToList(sharingDoc, doc.dockingConfig ? dashStorage : storage, doc); + } else GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, ((doc.createdFrom as Doc) || doc).dockingConfig ? dashStorage : storage, (doc.createdFrom as Doc) || doc); + }); + }, 'set Doc permissions'); /** * Sets the permission on the target for the group. * @param group * @param permission */ - setInternalGroupSharing = (group: Doc | { title: string }, permission: string, targetDoc?: Doc) => { + setInternalGroupSharing = undoable((group: Doc | { title: string }, permission: string, targetDoc?: Doc) => { const target = targetDoc || this.targetDoc!; - const key = normalizeEmail(StrCast(group.title)); - const acl = `acl-${key}`; - const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1; - - const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document); - - // ! ensures it returns true if document has been shared successfully, false otherwise - return !docs - .map(doc => (this.layoutDocAcls ? doc : doc[DataSym])) - .map(doc => { - doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc, undefined, undefined, isDashboard); - - if (permission === SharingPermissions.None) { - if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 1) - 1; - } else { - if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 0) + 1; - } + const acl = `acl-${normalizeEmail(StrCast(group.title))}`; - distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard); - this.setDashboardBackground(doc, permission as SharingPermissions); + const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc); + docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => { + distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.upgradeNested ? true : undefined); - if (group instanceof Doc) { - const members: string[] = JSON.parse(StrCast(group.members)); - const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); + if (group instanceof Doc) { + Doc.AddDocToList(group, 'docsShared', doc); - // if documents have been shared, add the doc to that list if it doesn't already exist, otherwise create a new list with the doc - group.docsShared ? Doc.IndexOf(doc, DocListCast(group.docsShared)) === -1 && (group.docsShared as List<Doc>).push(doc) : (group.docsShared = new List<Doc>([doc])); - - return users - .map(({ user, sharingDoc }) => { - if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added - else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc); // remove the doc from the list if it already exists - }) - .some(success => !success); - } - }) - .some(success => success); - }; + this.users + .filter(({ user: { email } }) => JSON.parse(StrCast(group.members)).includes(email)) + .forEach(({ user, sharingDoc }) => { + if (permission !== SharingPermissions.None) Doc.AddDocToList(sharingDoc, doc.dockingConfig ? dashStorage : storage, doc); // add the doc to the sharingDoc if it hasn't already been added + else GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, ((doc.createdFrom as Doc) || doc).dockingConfig ? dashStorage : storage, (doc.createdFrom as Doc) || doc); // remove the doc from the list if it already exists + }); + } + }); + }, 'set group permissions'); /** * Shares the documents shared with a group with a new user who has been added to that group. @@ -236,7 +211,13 @@ export class SharingManager extends React.Component<{}> { else { DocListCastAsync(user.sharingDoc[storage]).then(userdocs => DocListCastAsync(group.docsShared).then(dl => { - const filtered = dl?.filter(doc => !userdocs?.includes(doc)); + const filtered = dl?.filter(doc => !doc.dockingConfig && !userdocs?.includes(doc)); + filtered && userdocs?.push(...filtered); + }) + ); + DocListCastAsync(user.sharingDoc[dashStorage]).then(userdocs => + DocListCastAsync(group.docsShared).then(dl => { + const filtered = dl?.filter(doc => doc.dockingConfig && !userdocs?.includes(doc)); filtered && userdocs?.push(...filtered); }) ); @@ -247,44 +228,23 @@ export class SharingManager extends React.Component<{}> { /** * Called from the properties sidebar to change permissions of a user. */ - shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, docs: Doc[]) => { - if (shareWith !== 'Public' && shareWith !== 'Override') { + shareFromPropertiesSidebar = undoable((shareWith: string, permission: SharingPermissions, docs: Doc[], layout: boolean) => { + if (layout) this.layoutDocAcls = true; + if (shareWith !== 'Guest') { const user = this.users.find(({ user: { email } }) => email === (shareWith === 'Me' ? Doc.CurrentUserEmail : shareWith)); docs.forEach(doc => { if (user) this.setInternalSharing(user, permission, doc); - else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc); + else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc, undefined, true); }); } else { - const dashboards = DocListCast(Doc.MyDashboards.data); docs.forEach(doc => { - const isDashboard = dashboards.indexOf(doc) !== -1; - if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard); - this.setDashboardBackground(doc, permission as SharingPermissions); - }); - } - }; - - /** - * Sets the background of the Dashboard if it has been shared as a visual indicator - */ - setDashboardBackground = (doc: Doc, permission: SharingPermissions) => { - if (Doc.IndexOf(doc, DocListCast(Doc.MyDashboards.data)) !== -1) { - if (permission !== SharingPermissions.None) { - doc.isShared = true; - doc.backgroundColor = 'green'; - } else { - const acls = doc[DataSym][AclSym]; - if ( - Object.keys(acls) - .filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && key !== 'acl-Me') - .every(key => [AclUnset, AclPrivate].includes(acls[key])) - ) { - doc.isShared = undefined; - doc.backgroundColor = undefined; + if (GetEffectiveAcl(doc) === AclAdmin) { + distributeAcls(`acl-${shareWith}`, permission, doc, undefined); } - } + }); } - }; + this.layoutDocAcls = false; + }, 'sidebar set permissions'); /** * Removes the documents shared with a user through a group when the user is removed from the group. @@ -294,13 +254,19 @@ export class SharingManager extends React.Component<{}> { removeMember = (group: Doc, emailId: string) => { const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!; - if (group.docsShared) { + if (group.docsShared && user) { DocListCastAsync(user.sharingDoc[storage]).then(userdocs => DocListCastAsync(group.docsShared).then(dl => { const remaining = userdocs?.filter(doc => !dl?.includes(doc)) || []; userdocs?.splice(0, userdocs.length, ...remaining); }) ); + DocListCastAsync(user.sharingDoc[dashStorage]).then(userdocs => + DocListCastAsync(group.docsShared).then(dl => { + const remaining = userdocs?.filter(doc => !dl?.includes(doc)) || []; + userdocs?.splice(0, userdocs.length, ...remaining); + }) + ); } }; @@ -310,11 +276,9 @@ export class SharingManager extends React.Component<{}> { */ removeGroup = (group: Doc) => { if (group.docsShared) { - const dashboards = DocListCast(Doc.MyDashboards.data); DocListCast(group.docsShared).forEach(doc => { const acl = `acl-${StrCast(group.title)}`; - const isDashboard = dashboards.indexOf(doc) !== -1; - distributeAcls(acl, SharingPermissions.None, doc, undefined, undefined, isDashboard); + distributeAcls(acl, SharingPermissions.None, doc); const members: string[] = JSON.parse(StrCast(group.members)); const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); @@ -330,37 +294,26 @@ export class SharingManager extends React.Component<{}> { // return; // } // targetDoc["acl-" + PublicKey] = permission; - // } - - // private get sharingUrl() { - // if (!this.targetDoc) { - // return undefined; - // } - // const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]); - // return `${baseUrl}?sharing=true`; - // } + // }s - // copy = action(() => { - // if (this.sharingUrl) { - // Utils.CopyText(this.sharingUrl); - // this.copied = true; - // } - // }); + /** + * Copies the Public sharing url to the user's clipboard. + */ + private copyURL = (e: any) => { + Utils.CopyText(Utils.shareUrl(this.targetDoc![Id])); + }; /** * Returns the SharingPermissions (Admin, Can Edit etc) access that's used to share */ - private sharingOptions(uniform: boolean, override?: boolean) { - const dropdownValues: string[] = Object.values(SharingPermissions); + private sharingOptions(uniform: boolean, showGuestOptions?: boolean) { + const dropdownValues: string[] = showGuestOptions ? [SharingPermissions.None, SharingPermissions.View] : Object.values(SharingPermissions); if (!uniform) dropdownValues.unshift('-multiple-'); - if (!override) dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1); - return dropdownValues - .filter(permission => !Doc.noviceMode || ![SharingPermissions.SelfEdit].includes(permission as any)) - .map(permission => ( - <option key={permission} value={permission}> - {permission} - </option> - )); + return dropdownValues.map(permission => ( + <option key={permission} value={permission}> + {concat(ReverseHierarchyMap.get(permission)?.image, ' ', permission)} + </option> + )); } private focusOn = (contents: string) => { @@ -405,38 +358,43 @@ export class SharingManager extends React.Component<{}> { /** * Handles changes in the permission chosen to share with someone with */ - @action - handlePermissionsChange = (event: React.ChangeEvent<HTMLSelectElement>) => { - this.permissions = event.currentTarget.value as SharingPermissions; - }; + handlePermissionsChange = undoable( + action((event: React.ChangeEvent<HTMLSelectElement>) => { + this.permissions = event.currentTarget.value as SharingPermissions; + }), + 'permission change' + ); /** * Calls the relevant method for sharing, displays the popup, and resets the relevant variables. */ - @action - share = () => { - if (this.selectedUsers) { - this.selectedUsers.forEach(user => { - if (user.value.includes(indType)) { - this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions); - } else { - this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions); - } - }); - - const { left, width, top, height } = this.shareDocumentButtonRef.current!.getBoundingClientRect(); - TaskCompletionBox.popupX = left - 1.5 * width; - TaskCompletionBox.popupY = top - 1.5 * height; - TaskCompletionBox.textDisplayed = 'Document shared!'; - TaskCompletionBox.taskCompleted = true; - setTimeout( - action(() => (TaskCompletionBox.taskCompleted = false)), - 2000 - ); + share = undoable( + action(() => { + if (this.selectedUsers) { + this.selectedUsers.forEach(user => { + if (user.value.includes(indType)) { + this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions, undefined); + } else { + this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions); + } + }); + + const { left, width, top, height } = this.shareDocumentButtonRef.current!.getBoundingClientRect(); + TaskCompletionBox.popupX = left - 1.5 * width; + TaskCompletionBox.popupY = top - 1.5 * height; + TaskCompletionBox.textDisplayed = 'Document shared!'; + TaskCompletionBox.taskCompleted = true; + setTimeout( + action(() => (TaskCompletionBox.taskCompleted = false)), + 2000 + ); - this.selectedUsers = null; - } - }; + this.layoutDocAcls = false; + this.selectedUsers = null; + } + }), + 'share Doc' + ); /** * Sorting algorithm to sort users. @@ -463,6 +421,7 @@ export class SharingManager extends React.Component<{}> { if (!this.targetDoc) return null; TraceMobx(); const groupList = GroupManager.Instance?.allGroups || []; + const sortedUsers = this.users .slice() .sort(this.sortUsers) @@ -484,8 +443,7 @@ export class SharingManager extends React.Component<{}> { const users = this.individualSort === 'ascending' ? this.users.slice().sort(this.sortUsers) : this.individualSort === 'descending' ? this.users.slice().sort(this.sortUsers).reverse() : this.users; const groups = this.groupSort === 'ascending' ? groupList.slice().sort(this.sortGroups) : this.groupSort === 'descending' ? groupList.slice().sort(this.sortGroups).reverse() : groupList; - // handles the case where multiple documents are selected - let docs = SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DataSym])); + let docs = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.rootDoc); if (this.myDocAcls) { const newDocs: Doc[] = []; @@ -493,33 +451,39 @@ export class SharingManager extends React.Component<{}> { docs = newDocs.filter(doc => GetEffectiveAcl(doc) === AclAdmin); } - const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DataSym]; + const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData]; // tslint:disable-next-line: no-unnecessary-callback-wrapper const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc)); const admin = this.myDocAcls ? Boolean(docs.length) : effectiveAcls.every(acl => acl === AclAdmin); // users in common between all docs - const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).map(doc => doc?.[AclSym] && Object.keys(doc[AclSym]))); + const commonKeys = intersection(docs).reduce((list, doc) => (doc?.[DocAcl] ? [...list, ...Object.keys(doc[DocAcl])] : list), [] as string[]); // the list of users shared with - const userListContents: (JSX.Element | null)[] = users - .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email)) + const userListContents = users + // .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email)) + .filter(({ user }) => docs[0]?.author !== user.email) .map(({ user, linkDatabase, sharingDoc, userColor }) => { const userKey = `acl-${normalizeEmail(user.email)}`; - const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).every(doc => doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey]); - const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-'; + const uniform = docs.every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]); + // const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-'; + let permissions = targetDoc[DocAcl][userKey] ? HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name : StrCast(targetDoc[userKey]); + permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-'; return !permissions ? null : ( <div key={userKey} className={'container'}> <span className={'padding'}>{user.email}</span> <div className="edit-actions"> {admin || this.myDocAcls ? ( - <select className={'permissions-dropdown'} value={permissions} onChange={e => this.setInternalSharing({ user, linkDatabase, sharingDoc, userColor }, e.currentTarget.value)}> + <select className={`permissions-dropdown-${permissions}`} value={permissions} onChange={e => this.setInternalSharing({ user, linkDatabase, sharingDoc, userColor }, e.currentTarget.value, undefined)}> {this.sharingOptions(uniform)} </select> ) : ( - <div className={'permissions-dropdown'}>{permissions}</div> + <div className={`permissions-dropdown-${permissions}`}> + {concat(ReverseHierarchyMap.get(permissions)?.image, ' ', permissions)} + + </div> )} </div> </div> @@ -530,10 +494,13 @@ export class SharingManager extends React.Component<{}> { const sameAuthor = docs.every(doc => doc?.author === docs[0]?.author); // the owner of the doc and the current user are placed at the top of the user list. + const userKey = `acl-${normalizeEmail(Doc.CurrentUserEmail)}`; + const curUserPermission = StrCast(targetDoc[userKey]); + // const curUserPermission = HierarchyMapping.get(effectiveAcls[0])!.name userListContents.unshift( sameAuthor ? ( <div key={'owner'} className={'container'}> - <span className={'padding'}>{targetDoc?.author === Doc.CurrentUserEmail ? 'Me' : targetDoc?.author}</span> + <span className="padding">{targetDoc?.author === Doc.CurrentUserEmail ? 'Me' : StrCast(targetDoc?.author)}</span> <div className="edit-actions"> <div className={'permissions-dropdown'}>Owner</div> </div> @@ -543,7 +510,10 @@ export class SharingManager extends React.Component<{}> { <div key={'me'} className={'container'}> <span className={'padding'}>Me</span> <div className="edit-actions"> - <div className={'permissions-dropdown'}>{effectiveAcls.every(acl => acl === effectiveAcls[0]) ? HierarchyMapping.get(effectiveAcls[0])!.name : '-multiple-'}</div> + <div className={`permissions-dropdown-${curUserPermission}`}> + {effectiveAcls.every(acl => acl === effectiveAcls[0]) ? concat(ReverseHierarchyMap.get(curUserPermission!)?.image, ' ', curUserPermission) : '-multiple-'} + + </div> </div> </div> ) : null @@ -551,29 +521,27 @@ export class SharingManager extends React.Component<{}> { // the list of groups shared with const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true)); - groupListMap.unshift({ title: 'Public' }); //, { title: "ALL" }); + groupListMap.unshift({ title: 'Guest' }); //, { title: "ALL" }); const groupListContents = groupListMap.map(group => { - const groupKey = `acl-${StrCast(group.title)}`; - const uniform = docs - .map(doc => (this.layoutDocAcls ? doc : doc[DataSym])) - .every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey])); - const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-'; + let groupKey = `acl-${StrCast(group.title)}`; + const uniform = docs.every(doc => doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey]); + const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-'; return !permissions ? null : ( - <div key={groupKey} className={'container'}> + <div key={groupKey} className={'container'} style={{ background: SettingsManager.Instance.userBackgroundColor, color: SettingsManager.Instance.userColor }}> <div className={'padding'}>{StrCast(group.title)}</div> - {group instanceof Doc ? ( - <div className="group-info" onClick={action(() => (GroupManager.Instance.currentGroup = group))}> - <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} /> - </div> - ) : null} - <div className="edit-actions"> + + {group instanceof Doc ? <IconButton icon={<FontAwesomeIcon icon={'info-circle'} />} size={Size.XSMALL} color={SettingsManager.Instance.userColor} onClick={action(() => (GroupManager.Instance.currentGroup = group))} /> : null} + <div className={'edit-actions'}> {admin || this.myDocAcls ? ( - <select className={'permissions-dropdown'} value={permissions} onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}> - {this.sharingOptions(uniform, group.title === 'Override')} + <select className={`permissions-dropdown-${permissions}`} value={permissions} onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}> + {this.sharingOptions(uniform, group.title === 'Guest')} </select> ) : ( - <div className={'permissions-dropdown'}>{permissions}</div> + <div className={`permissions-dropdown-${permissions}`}> + {concat(ReverseHierarchyMap.get(permissions)?.image, ' ', permissions)} + + </div> )} </div> </div> @@ -582,22 +550,28 @@ export class SharingManager extends React.Component<{}> { return ( <div className="sharing-interface"> {GroupManager.Instance?.currentGroup ? <GroupMemberView group={GroupManager.Instance.currentGroup} onCloseButtonClick={action(() => (GroupManager.Instance.currentGroup = undefined))} /> : null} - <div className="sharing-contents"> - <p className={'share-title'}> + <div + className="sharing-contents" + style={{ + background: SettingsManager.Instance.userBackgroundColor, + color: StrCast(Doc.UserDoc().userColor), + }}> + <p className="share-title" style={{ color: StrCast(Doc.UserDoc().userColor) }}> + <div className="share-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/features/collaboration/', '_blank')}> + <FontAwesomeIcon icon={'question-circle'} size={'sm'} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/features/collaboration/', '_blank')} /> + </div> <b>Share </b> {this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, 'this document') : '-multiple-')} </p> - <div className={'close-button'} onClick={this.close}> - <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} /> + <div className="share-copy-link"> + <Button type={Type.TERT} color={StrCast(Doc.UserDoc().userColor)} icon={<FontAwesomeIcon icon={'copy'} size="sm" />} iconPlacement={'left'} text={'Copy Guest URL'} onClick={this.copyURL} /> </div> - {/* {this.linkVisible ? - <div> - {this.sharingUrl} - </div> : - (null)} */} - { + <div className="close-button"> + <Button icon={<FontAwesomeIcon icon={'times'} size={'lg'} />} onClick={this.close} color={StrCast(Doc.UserDoc().userColor)} /> + </div> + {admin ? ( <div className="share-container"> - <div className="share-setup"> + <div className="share-setup" style={{ border: StrCast(Doc.UserDoc().userColor) }}> <Select className="user-search" placeholder="Enter user or group name..." @@ -609,17 +583,34 @@ export class SharingManager extends React.Component<{}> { onChange={this.handleUsersChange} value={this.selectedUsers} styles={{ + control: () => ({ + display: 'inline-flex', + width: '100%', + }), indicatorSeparator: () => ({ + display: 'inline-flex', visibility: 'hidden', }), + indicatorsContainer: () => ({ + display: 'inline-flex', + textDecorationColor: 'black', + }), + valueContainer: () => ({ + display: 'inline-flex', + fontStyle: StrCast(Doc.UserDoc().userColor), + color: StrCast(Doc.UserDoc().userColor), + width: '100%', + }), }} /> - <select className="permissions-select" onChange={this.handlePermissionsChange} value={this.permissions}> - {this.sharingOptions(true)} - </select> - <button ref={this.shareDocumentButtonRef} className="share-button" onClick={this.share}> - Share - </button> + <div className="permissions-select"> + <select className={`permissions-dropdown-${this.permissions}`} onChange={this.handlePermissionsChange} value={this.permissions}> + {this.sharingOptions(true)} + </select> + </div> + <div className="share-button"> + <Button text={'SHARE'} type={Type.TERT} color={StrCast(Doc.UserDoc().userColor)} onClick={this.share} /> + </div> </div> <div className="sort-checkboxes"> <input type="checkbox" onChange={action(() => (this.showUserOptions = !this.showUserOptions))} /> <label style={{ marginRight: 10 }}>Individuals</label> @@ -629,36 +620,46 @@ export class SharingManager extends React.Component<{}> { <div className="acl-container"> {Doc.noviceMode ? null : ( <div className="layoutDoc-acls"> + <input type="checkbox" onChange={action(() => (this.upgradeNested = !this.upgradeNested))} checked={this.upgradeNested} /> <label>Upgrade Nested </label> <input type="checkbox" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <label>Layout</label> </div> )} </div> </div> - } - <div className="main-container"> + ) : ( + <div className="share-container"> + <div className="acl-container"> + <div className="layoutDoc-acls"> + <input type="checkbox" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <label>Layout</label> + </div> + </div> + </div> + )} + <div className="main-container" style={{ color: StrCast(Doc.UserDoc().userColor), border: StrCast(Doc.UserDoc().userColor) }}> <div className={'individual-container'}> <div className="user-sort" onClick={action(() => (this.individualSort = this.individualSort === 'ascending' ? 'descending' : this.individualSort === 'descending' ? 'none' : 'ascending'))}> - Individuals{' '} - {this.individualSort === 'ascending' ? ( - <FontAwesomeIcon icon={'caret-up'} size={'xs'} /> - ) : this.individualSort === 'descending' ? ( - <FontAwesomeIcon icon={'caret-down'} size={'xs'} /> - ) : ( - <FontAwesomeIcon icon={'caret-right'} size={'xs'} /> - )} + <div className="title-individual"> + Individuals + <IconButton + icon={<FontAwesomeIcon icon={this.individualSort === 'ascending' ? 'caret-up' : this.individualSort === 'descending' ? 'caret-down' : 'caret-right'} />} + size={Size.XSMALL} + color={StrCast(Doc.UserDoc().userColor)} + /> + </div> </div> - <div className={'users-list'}>{userListContents}</div> + <div className="users-list">{userListContents}</div> </div> <div className={'group-container'}> <div className="user-sort" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}> - Groups{' '} - {this.groupSort === 'ascending' ? ( - <FontAwesomeIcon icon={'caret-up'} size={'xs'} /> - ) : this.groupSort === 'descending' ? ( - <FontAwesomeIcon icon={'caret-down'} size={'xs'} /> - ) : ( - <FontAwesomeIcon icon={'caret-right'} size={'xs'} /> - )} + <div className="title-group"> + Groups + <IconButton icon={<FontAwesomeIcon icon={'info-circle'} />} size={Size.XSMALL} color={StrCast(Doc.UserDoc().userColor)} onClick={action(() => GroupManager.Instance.open())} /> + <IconButton + icon={<FontAwesomeIcon icon={this.groupSort === 'ascending' ? 'caret-up' : this.groupSort === 'descending' ? 'caret-down' : 'caret-right'} />} + size={Size.XSMALL} + color={StrCast(Doc.UserDoc().userColor)} + /> + </div> </div> <div className={'groups-list'}>{groupListContents}</div> </div> |