diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/GroupManager.tsx | 47 | ||||
-rw-r--r-- | src/client/util/SharingManager.tsx | 87 | ||||
-rw-r--r-- | src/client/views/DocComponent.tsx | 19 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 6 | ||||
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 19 |
5 files changed, 85 insertions, 93 deletions
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 229a846a9..45c26d87a 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -42,6 +42,7 @@ export default class GroupManager extends React.Component<{}> { private currentUserGroups: string[] = []; // the list of groups the current user is a member of @observable private buttonColour: "#979797" | "black" = "#979797"; @observable private groupSort: "ascending" | "descending" | "none" = "none"; + private populating: boolean = false; @@ -62,21 +63,24 @@ export default class GroupManager extends React.Component<{}> { * 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 userDocument = await DocServer.GetRefField(user.userDocumentId); - if (userDocument instanceof Doc) { - const notificationDoc = await Cast(userDocument["sidebar-sharing"], Doc); - runInAction(() => { - if (notificationDoc instanceof Doc) { - this.users.push(user.email); - } - }); - } - }); - return Promise.all(evaluating); + if (!this.populating) { + this.populating = true; + 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 userDocument = await DocServer.GetRefField(user.userDocumentId); + if (userDocument instanceof Doc) { + const notificationDoc = await Cast(userDocument["sidebar-sharing"], Doc); + runInAction(() => { + if (notificationDoc instanceof Doc) { + this.users.push(user.email); + } + }); + } + }); + return Promise.all(evaluating).then(() => this.populating = false); + } } /** @@ -88,7 +92,6 @@ export default class GroupManager extends React.Component<{}> { const members: string[] = JSON.parse(StrCast(group.members)); if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(StrCast(group.groupName)); }); - setGroups(this.currentUserGroups); }); } @@ -120,6 +123,7 @@ export default class GroupManager extends React.Component<{}> { this.currentGroup = undefined; // this.users = []; this.createGroupModalOpen = false; + TaskCompletionBox.taskCompleted = false; } /** @@ -132,7 +136,6 @@ export default class GroupManager extends React.Component<{}> { /** * @returns a list of all group documents. */ - // private ? getAllGroups(): Doc[] { const groupDoc = this.GroupManagerDoc; return groupDoc ? DocListCast(groupDoc.data) : []; @@ -142,7 +145,6 @@ export default class GroupManager extends React.Component<{}> { * @returns a group document based on the group name. * @param groupName */ - // private? getGroup(groupName: string): Doc | undefined { const groupDoc = this.getAllGroups().find(group => group.groupName === groupName); return groupDoc; @@ -209,8 +211,6 @@ export default class GroupManager extends React.Component<{}> { deleteGroup(group: Doc): boolean { if (group) { if (this.GroupManagerDoc && this.hasEditAccess(group)) { - // TODO look at this later - // SharingManager.Instance.setInternalGroupSharing(group, "Not Shared"); Doc.RemoveDocFromList(this.GroupManagerDoc, "data", group); SharingManager.Instance.removeGroup(group); const members: string[] = JSON.parse(StrCast(group.members)); @@ -311,7 +311,9 @@ export default class GroupManager extends React.Component<{}> { <div className="group-create"> <div className="group-heading" style={{ marginBottom: 0 }}> <p><b>New Group</b></p> - <div className={"close-button"} onClick={action(() => this.createGroupModalOpen = false)}> + <div className={"close-button"} onClick={action(() => { + this.createGroupModalOpen = false; TaskCompletionBox.taskCompleted = false; + })}> <FontAwesomeIcon icon={fa.faTimes} color={"black"} size={"lg"} /> </div> </div> @@ -319,6 +321,7 @@ export default class GroupManager extends React.Component<{}> { className="group-input" ref={this.inputRef} onKeyDown={this.handleKeyDown} + autoFocus type="text" placeholder="Group name" onChange={action(() => this.buttonColour = this.inputRef.current?.value ? "black" : "#979797")} /> @@ -363,7 +366,7 @@ export default class GroupManager extends React.Component<{}> { interactive={true} contents={contents} dialogueBoxStyle={{ width: "90%", height: "70%" }} - closeOnExternalClick={action(() => this.createGroupModalOpen = false)} + closeOnExternalClick={action(() => { this.createGroupModalOpen = false; TaskCompletionBox.taskCompleted = false; })} /> ); } diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 9d91ce1ba..adfc8c2bf 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, DocListCastAsync, AclAdmin, DataSym, AclPrivate } from "../../fields/Doc"; +import { Doc, Opt, AclAdmin, AclPrivate, DocListCast } from "../../fields/Doc"; import { DocServer } from "../DocServer"; import { Cast, StrCast } from "../../fields/Types"; import * as RequestPromise from "request-promise"; @@ -71,6 +71,7 @@ export default class SharingManager extends React.Component<{}> { // 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; // private get linkVisible() { // return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; @@ -93,7 +94,7 @@ export default class SharingManager extends React.Component<{}> { public close = action(() => { this.isOpen = false; this.selectedUsers = null; // resets the list of users and seleected users (in the react-select component) - + TaskCompletionBox.taskCompleted = false; setTimeout(action(() => { // this.copied = false; DictationOverlay.Instance.hasActiveModal = false; @@ -117,24 +118,27 @@ export default class SharingManager extends React.Component<{}> { * Populates the list of validated users (this.users) by adding registered users which have a sidebar-sharing. */ 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["sidebar-sharing"], Doc); - runInAction(() => { - if (notificationDoc instanceof Doc) { - this.users.push({ user, notificationDoc }); - } - }); + if (!this.populating) { + this.populating = true; + 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["sidebar-sharing"], Doc); + runInAction(() => { + if (notificationDoc instanceof Doc) { + this.users.push({ user, notificationDoc }); + } + }); + } } - } - }); - return Promise.all(evaluating); + }); + return Promise.all(evaluating).then(() => this.populating = false); + } } /** @@ -152,13 +156,11 @@ export default class SharingManager extends React.Component<{}> { target.author === Doc.CurrentUserEmail && distributeAcls(ACL, permission as SharingPermissions, target); // if documents have been shared, add the target to that list if it doesn't already exist, otherwise create a new list with the target - group.docsShared ? DocListCastAsync(group.docsShared).then(resolved => Doc.IndexOf(target, resolved!) === -1 && (group.docsShared as List<Doc>).push(target)) : group.docsShared = new List<Doc>([target]); + 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(resolved => { - if (permission !== SharingPermissions.None) Doc.IndexOf(target, resolved!) === -1 && Doc.AddDocToList(notificationDoc, storage, target); // add the target to the notificationDoc if it hasn't already been added - else Doc.IndexOf(target, resolved!) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); // remove the target from the list if it already exists - }); + if (permission !== SharingPermissions.None) Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target); // add the target to the notificationDoc if it hasn't already been added + else Doc.IndexOf(target, DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); // remove the target from the list if it already exists }); } @@ -170,13 +172,7 @@ export default class SharingManager extends React.Component<{}> { shareWithAddedMember = (group: Doc, emailId: string) => { const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!; - if (group.docsShared) { - DocListCastAsync(group.docsShared).then(docsShared => { - docsShared?.forEach(doc => { - DocListCastAsync(user.notificationDoc[storage]).then(resolved => Doc.IndexOf(doc, resolved!) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc)); // add the doc if it isn't already in the list - }); - }); - } + if (group.docsShared) DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc)); } shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, target: Doc) => { @@ -194,10 +190,8 @@ export default class SharingManager extends React.Component<{}> { const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!; if (group.docsShared) { - DocListCastAsync(group.docsShared).then(docsShared => { - docsShared?.forEach(doc => { - DocListCastAsync(user.notificationDoc[storage]).then(resolved => Doc.IndexOf(doc, resolved!) !== -1 && Doc.RemoveDocFromList(user.notificationDoc, storage, doc)); // remove the doc only if it is in the list - }); + DocListCast(group.docsShared).forEach(doc => { + Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(user.notificationDoc, storage, doc); // remove the doc only if it is in the list }); } } @@ -208,18 +202,15 @@ export default class SharingManager extends React.Component<{}> { */ removeGroup = (group: Doc) => { if (group.docsShared) { - DocListCastAsync(group.docsShared).then(resolved => { - resolved?.forEach(doc => { - const ACL = `ACL-${StrCast(group.groupName)}`; + DocListCast(group.docsShared).forEach(doc => { + const ACL = `ACL-${StrCast(group.groupName)}`; - distributeAcls(ACL, SharingPermissions.None, doc); + distributeAcls(ACL, SharingPermissions.None, doc); - const members: string[] = JSON.parse(StrCast(group.members)); - const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); - - users.forEach(({ notificationDoc }) => Doc.RemoveDocFromList(notificationDoc, storage, doc)); - }); + const members: string[] = JSON.parse(StrCast(group.members)); + const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); + users.forEach(({ notificationDoc }) => Doc.RemoveDocFromList(notificationDoc, storage, doc)); }); } } @@ -233,14 +224,10 @@ export default class SharingManager extends React.Component<{}> { target.author === Doc.CurrentUserEmail && distributeAcls(ACL, permission as SharingPermissions, target); if (permission !== SharingPermissions.None) { - DocListCastAsync(notificationDoc[storage]).then(resolved => { - Doc.IndexOf(target, resolved!) === -1 && Doc.AddDocToList(notificationDoc, storage, target); - }); + Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target); } else { - DocListCastAsync(notificationDoc[storage]).then(resolved => { - Doc.IndexOf(target, resolved!) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); - }); + Doc.IndexOf(target, DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); } } diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 804c7a8d4..34144d3eb 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -154,15 +154,16 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T return false; } else { - // if (this.props.Document[AclSym]) { - // added.forEach(d => { - // const dataDoc = d[DataSym]; - // dataDoc[AclSym] = d[AclSym] = this.props.Document[AclSym]; - // for (const [key, value] of Object.entries(this.props.Document[AclSym])) { - // dataDoc[key] = d[key] = this.AclMap.get(value); - // } - // }); - // } + if (this.props.Document[AclSym]) { + added.forEach(d => { + const dataDoc = d[DataSym]; + dataDoc[AclSym] = d[AclSym] = this.props.Document[AclSym]; + for (const [key, value] of Object.entries(this.props.Document[AclSym])) { + dataDoc[key] = d[key] = this.AclMap.get(value); + } + }); + } + if (effectiveAcl === AclAddonly) { added.map(doc => Doc.AddDocToList(targetDataDoc, this.annotationKey, doc)); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index f16cb273b..8dcba27e4 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -588,7 +588,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { return (null); } - const canDelete = SelectionManager.SelectedDocuments().map(docView => GetEffectiveAcl(docView.props.ContainingCollectionDoc)).some(permission => permission === AclAdmin || permission === AclEdit); + const canDelete = SelectionManager.SelectedDocuments().some(docView => { + const docAcl = GetEffectiveAcl(docView.props.Document); + const collectionAcl = GetEffectiveAcl(docView.props.ContainingCollectionDoc); + return [docAcl, collectionAcl].some(acl => [AclAdmin, AclEdit].includes(acl)); + }); const minimal = bounds.r - bounds.x < 100 ? true : false; const maximizeIcon = minimal ? ( <Tooltip title={<><div className="dash-tooltip">Show context menu</div></>} placement="top"> diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 89034a0c0..44875ac1e 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -17,7 +17,7 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; -import { TraceMobx, GetEffectiveAcl, SharingPermissions } from '../../../fields/util'; +import { TraceMobx, GetEffectiveAcl, SharingPermissions, distributeAcls } from '../../../fields/util'; import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; @@ -147,16 +147,13 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus return false; } else { - // if (this.props.Document[AclSym]) { - // // change so it only adds if more restrictive - // added.forEach(d => { - // // const dataDoc = d[DataSym]; - // for (const [key, value] of Object.entries(this.props.Document[AclSym])) { - // // key.substring(4).replace("_", ".") !== Doc.CurrentUserEmail && distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true); - // distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true); - // } - // }); - // } + if (this.props.Document[AclSym]) { + added.forEach(d => { + for (const [key, value] of Object.entries(this.props.Document[AclSym])) { + distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true); + } + }); + } if (effectiveAcl === AclAddonly) { added.map(doc => Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc)); |