aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/CaptureManager.tsx157
-rw-r--r--src/client/util/CurrentUserUtils.ts2
-rw-r--r--src/client/util/GroupManager.tsx253
-rw-r--r--src/client/util/ScrollBox.tsx23
-rw-r--r--src/client/util/SharingManager.tsx566
5 files changed, 486 insertions, 515 deletions
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index c247afa26..0b5957fac 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -1,17 +1,17 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { convertToObject } from "typescript";
-import { Doc, DocListCast } from "../../fields/Doc";
-import { BoolCast, StrCast, Cast } from "../../fields/Types";
-import { addStyleSheet, addStyleSheetRule, Utils } from "../../Utils";
-import { LightboxView } from "../views/LightboxView";
-import { MainViewModal } from "../views/MainViewModal";
-import "./CaptureManager.scss";
-import { SelectionManager } from "./SelectionManager";
-import { undoBatch } from "./UndoManager";
-const higflyout = require("@hig/flyout");
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { convertToObject } from 'typescript';
+import { Doc, DocListCast } from '../../fields/Doc';
+import { BoolCast, StrCast, Cast } from '../../fields/Types';
+import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
+import { LightboxView } from '../views/LightboxView';
+import { MainViewModal } from '../views/MainViewModal';
+import './CaptureManager.scss';
+import { SelectionManager } from './SelectionManager';
+import { undoBatch } from './UndoManager';
+const higflyout = require('@hig/flyout');
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -22,32 +22,31 @@ export class CaptureManager extends React.Component<{}> {
@observable _document: any;
@observable isOpen: boolean = false; // whether the CaptureManager is to be displayed or not.
-
constructor(props: {}) {
super(props);
CaptureManager.Instance = this;
}
- public close = action(() => this.isOpen = false);
+ public close = action(() => (this.isOpen = false));
public open = action((doc: Doc) => {
this.isOpen = true;
this._document = doc;
});
-
@computed get visibilityContent() {
-
- return <div className="capture-block">
- <div className="capture-block-title">Visibility</div>
- <div className="capture-block-radio">
- <div className="radio-container">
- <input type="radio" value="private" name="visibility" style={{ margin: 0, marginRight: 5 }} /> Private
- </div>
- <div className="radio-container">
- <input type="radio" value="public" name="visibility" style={{ margin: 0, marginRight: 5 }} /> Public
+ return (
+ <div className="capture-block">
+ <div className="capture-block-title">Visibility</div>
+ <div className="capture-block-radio">
+ <div className="radio-container">
+ <input type="radio" value="private" name="visibility" style={{ margin: 0, marginRight: 5 }} /> Private
+ </div>
+ <div className="radio-container">
+ <input type="radio" value="public" name="visibility" style={{ margin: 0, marginRight: 5 }} /> Public
+ </div>
</div>
</div>
- </div>;
+ );
}
@computed get linksContent() {
@@ -66,75 +65,79 @@ export class CaptureManager extends React.Component<{}> {
order.push(
<div className="list-item">
<div className="number">{i}</div>
- {(l.anchor1 as Doc).title}
+ {StrCast((l.anchor1 as Doc).title)}
</div>
);
}
});
}
- return <div className="capture-block">
- <div className="capture-block-title">Links</div>
- <div className="capture-block-list">
- {order}
+ return (
+ <div className="capture-block">
+ <div className="capture-block-title">Links</div>
+ <div className="capture-block-list">{order}</div>
</div>
- </div>;
+ );
}
@computed get closeButtons() {
- return <div className="capture-block">
- <div className="buttons">
- <div className="save" onClick={() => {
- LightboxView.SetLightboxDoc(this._document);
- this.close();
- }}>
- Save
- </div>
- <div className="cancel" onClick={() => {
- const selected = SelectionManager.Views().slice();
- SelectionManager.DeselectAll();
- selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
- this.close();
- }}>
- Cancel
+ return (
+ <div className="capture-block">
+ <div className="buttons">
+ <div
+ className="save"
+ onClick={() => {
+ LightboxView.SetLightboxDoc(this._document);
+ this.close();
+ }}>
+ Save
+ </div>
+ <div
+ className="cancel"
+ onClick={() => {
+ const selected = SelectionManager.Views().slice();
+ SelectionManager.DeselectAll();
+ selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
+ this.close();
+ }}>
+ Cancel
+ </div>
</div>
</div>
- </div>;
+ );
}
-
-
private get captureInterface() {
- return <div className="capture-interface">
- <div className="capture-t1">
- <div className="recordButtonOutline" style={{}}>
- <div className="recordButtonInner" style={{}}>
+ return (
+ <div className="capture-interface">
+ <div className="capture-t1">
+ <div className="recordButtonOutline" style={{}}>
+ <div className="recordButtonInner" style={{}}></div>
</div>
+ Conversation Capture
</div>
- Conversation Capture
- </div>
- <div className="capture-t2">
-
- </div>
- {this.visibilityContent}
- {this.linksContent}
- <div className="close-button" onClick={this.close}>
- <FontAwesomeIcon icon={"times"} color="black" size={"lg"} />
+ <div className="capture-t2"></div>
+ {this.visibilityContent}
+ {this.linksContent}
+ <div className="close-button" onClick={this.close}>
+ <FontAwesomeIcon icon={'times'} color="black" size={'lg'} />
+ </div>
+ {this.closeButtons}
</div>
- {this.closeButtons}
- </div>;
-
+ );
}
render() {
- return <MainViewModal
- contents={this.captureInterface}
- isDisplayed={this.isOpen}
- interactive={true}
- closeOnExternalClick={this.close}
- dialogueBoxStyle={{ width: "500px", height: "350px", border: "none", background: "whitesmoke" }}
- overlayStyle={{ background: "black" }}
- overlayDisplayedOpacity={0.6}
- />;
+ return (
+ <MainViewModal
+ contents={this.captureInterface}
+ isDisplayed={this.isOpen}
+ interactive={true}
+ closeOnExternalClick={this.close}
+ dialogueBoxStyle={{ width: '500px', height: '350px', border: 'none', background: 'whitesmoke' }}
+ overlayStyle={{ background: 'black' }}
+ overlayDisplayedOpacity={0.6}
+ />
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b2a5fddcd..84efcb966 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -957,7 +957,7 @@ export class CurrentUserUtils {
}
new LinkManager();
- DocServer.UPDATE_SERVER_CACHE();
+ setTimeout(DocServer.UPDATE_SERVER_CACHE, 2500);
return doc;
}
static setupFieldInfos(doc:Doc, field="fieldInfos") {
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 62d656c5d..59334f6a2 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -1,19 +1,19 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
import Select from 'react-select';
-import * as RequestPromise from "request-promise";
-import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
-import { StrCast, Cast } from "../../fields/Types";
-import { Utils } from "../../Utils";
-import { MainViewModal } from "../views/MainViewModal";
-import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
-import "./GroupManager.scss";
-import { GroupMemberView } from "./GroupMemberView";
-import { SharingManager, User } from "./SharingManager";
-import { listSpec } from "../../fields/Schema";
-import { DateField } from "../../fields/DateField";
+import * as RequestPromise from 'request-promise';
+import { Doc, DocListCast, DocListCastAsync, Opt } from '../../fields/Doc';
+import { StrCast, Cast } from '../../fields/Types';
+import { Utils } from '../../Utils';
+import { MainViewModal } from '../views/MainViewModal';
+import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
+import './GroupManager.scss';
+import { GroupMemberView } from './GroupMemberView';
+import { SharingManager, User } from './SharingManager';
+import { listSpec } from '../../fields/Schema';
+import { DateField } from '../../fields/DateField';
/**
* Interface for options for the react-select component
@@ -25,7 +25,6 @@ export interface UserOptions {
@observer
export class GroupManager extends React.Component<{}> {
-
static Instance: GroupManager;
@observable isOpen: boolean = false; // whether the GroupManager is to be displayed or not.
@observable private users: string[] = []; // list of users populated from the database.
@@ -34,24 +33,26 @@ export class GroupManager extends React.Component<{}> {
@observable private createGroupModalOpen: boolean = false;
private inputRef: React.RefObject<HTMLInputElement> = React.createRef(); // the ref for the input box.
private createGroupButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // the ref for the group creation button
- @observable private buttonColour: "#979797" | "black" = "#979797";
- @observable private groupSort: "ascending" | "descending" | "none" = "none";
+ @observable private buttonColour: '#979797' | 'black' = '#979797';
+ @observable private groupSort: 'ascending' | 'descending' | 'none' = 'none';
constructor(props: Readonly<{}>) {
super(props);
GroupManager.Instance = this;
}
- componentDidMount() { this.populateUsers(); }
+ componentDidMount() {
+ this.populateUsers();
+ }
/**
* Fetches the list of users stored on the database.
*/
populateUsers = async () => {
- const userList = await RequestPromise.get(Utils.prepend("/getUsers"));
+ const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
const raw = JSON.parse(userList) as User[];
raw.map(action(user => !this.users.some(umail => umail === user.email) && this.users.push(user.email)));
- }
+ };
/**
* @returns the options to be rendered in the dropdown menu to add users and create a group.
@@ -68,11 +69,11 @@ export class GroupManager extends React.Component<{}> {
// SelectionManager.DeselectAll();
this.isOpen = true;
this.populateUsers();
- }
+ };
/**
* Hides the GroupManager.
- */
+ */
@action
close = () => {
this.isOpen = false;
@@ -81,26 +82,32 @@ export class GroupManager extends React.Component<{}> {
// this.users = [];
this.createGroupModalOpen = false;
TaskCompletionBox.taskCompleted = false;
- }
+ };
/**
* @returns the database of groups.
*/
- @computed get GroupManagerDoc(): Doc | undefined { return Doc.UserDoc().globalGroupDatabase as Doc; }
+ @computed get GroupManagerDoc(): Doc | undefined {
+ return Doc.UserDoc().globalGroupDatabase as Doc;
+ }
/**
* @returns a list of all group documents.
*/
- @computed get allGroups(): Doc[] { return DocListCast(this.GroupManagerDoc?.data); }
+ @computed get allGroups(): Doc[] {
+ return DocListCast(this.GroupManagerDoc?.data);
+ }
/**
* @returns the members of the admin group.
*/
- @computed get adminGroupMembers(): string[] { return this.getGroup("Admin") ? JSON.parse(StrCast(this.getGroup("Admin")!.members)) : ""; }
+ @computed get adminGroupMembers(): string[] {
+ return this.getGroup('Admin') ? JSON.parse(StrCast(this.getGroup('Admin')!.members)) : '';
+ }
/**
* @returns a group document based on the group name.
- * @param groupName
+ * @param groupName
*/
getGroup(groupName: string): Doc | undefined {
return this.allGroups.find(group => group.title === groupName);
@@ -114,10 +121,9 @@ export class GroupManager extends React.Component<{}> {
return JSON.parse(StrCast(this.getGroup(group)!.members)) as string[];
}
-
/**
* @returns a boolean indicating whether the current user has access to edit group documents.
- * @param groupDoc
+ * @param groupDoc
*/
hasEditAccess(groupDoc: Doc): boolean {
if (!groupDoc) return false;
@@ -127,12 +133,12 @@ export class GroupManager extends React.Component<{}> {
/**
* Helper method that sets up the group document.
- * @param groupName
- * @param memberEmails
+ * @param groupName
+ * @param memberEmails
*/
createGroupDoc(groupName: string, memberEmails: string[] = []) {
- const name = groupName.toLowerCase() === "admin" ? "Admin" : groupName;
- const groupDoc = new Doc("GROUP:" + name, true);
+ const name = groupName.toLowerCase() === 'admin' ? 'Admin' : groupName;
+ const groupDoc = new Doc('GROUP:' + name, true);
groupDoc.title = name;
groupDoc.owners = JSON.stringify([Doc.CurrentUserEmail]);
groupDoc.members = JSON.stringify(memberEmails);
@@ -141,12 +147,12 @@ export class GroupManager extends React.Component<{}> {
/**
* Helper method that adds a group document to the database of group documents and @returns whether it was successfully added or not.
- * @param groupDoc
+ * @param groupDoc
*/
addGroup(groupDoc: Doc): boolean {
if (this.GroupManagerDoc) {
- Doc.AddDocToList(this.GroupManagerDoc, "data", groupDoc);
- this.GroupManagerDoc["data-lastModified"] = new DateField;
+ Doc.AddDocToList(this.GroupManagerDoc, 'data', groupDoc);
+ this.GroupManagerDoc['data-lastModified'] = new DateField();
return true;
}
return false;
@@ -154,20 +160,20 @@ export class GroupManager extends React.Component<{}> {
/**
* Deletes a group from the database of group documents and @returns whether the group was deleted or not.
- * @param group
+ * @param group
*/
@action
deleteGroup(group: Doc): boolean {
if (group) {
if (this.GroupManagerDoc && this.hasEditAccess(group)) {
- Doc.RemoveDocFromList(this.GroupManagerDoc, "data", group);
+ Doc.RemoveDocFromList(this.GroupManagerDoc, 'data', group);
SharingManager.Instance.removeGroup(group);
const members = JSON.parse(StrCast(group.members));
if (members.includes(Doc.CurrentUserEmail)) {
const index = DocListCast(this.GroupManagerDoc.data).findIndex(grp => grp === group);
index !== -1 && Cast(this.GroupManagerDoc.data, listSpec(Doc), [])?.splice(index, 1);
}
- this.GroupManagerDoc["data-lastModified"] = new DateField;
+ this.GroupManagerDoc['data-lastModified'] = new DateField();
if (group === this.currentGroup) {
this.currentGroup = undefined;
}
@@ -179,8 +185,8 @@ export class GroupManager extends React.Component<{}> {
/**
* Adds a member to a group.
- * @param groupDoc
- * @param email
+ * @param groupDoc
+ * @param email
*/
addMemberToGroup(groupDoc: Doc, email: string) {
if (this.hasEditAccess(groupDoc)) {
@@ -188,14 +194,14 @@ export class GroupManager extends React.Component<{}> {
!memberList.includes(email) && memberList.push(email);
groupDoc.members = JSON.stringify(memberList);
SharingManager.Instance.shareWithAddedMember(groupDoc, email);
- this.GroupManagerDoc && (this.GroupManagerDoc["data-lastModified"] = new DateField);
+ this.GroupManagerDoc && (this.GroupManagerDoc['data-lastModified'] = new DateField());
}
}
/**
* Removes a member from the group.
- * @param groupDoc
- * @param email
+ * @param groupDoc
+ * @param email
*/
removeMemberFromGroup(groupDoc: Doc, email: string) {
if (this.hasEditAccess(groupDoc)) {
@@ -205,27 +211,27 @@ export class GroupManager extends React.Component<{}> {
const user = memberList.splice(index, 1)[0];
groupDoc.members = JSON.stringify(memberList);
SharingManager.Instance.removeMember(groupDoc, email);
- this.GroupManagerDoc && (this.GroupManagerDoc["data-lastModified"] = new DateField);
+ this.GroupManagerDoc && (this.GroupManagerDoc['data-lastModified'] = new DateField());
}
}
}
/**
* Handles changes in the users selected in the "Select users" dropdown.
- * @param selectedOptions
+ * @param selectedOptions
*/
@action
handleChange = (selectedOptions: any) => {
this.selectedUsers = selectedOptions as UserOptions[];
- }
+ };
/**
* Creates the group when the enter key has been pressed (when in the input).
- * @param e
+ * @param e
*/
handleKeyDown = (e: React.KeyboardEvent) => {
- e.key === "Enter" && this.createGroup();
- }
+ e.key === 'Enter' && this.createGroup();
+ };
/**
* Handles the input of required fields in the setup of a group and resets the relevant variables.
@@ -234,32 +240,37 @@ export class GroupManager extends React.Component<{}> {
createGroup = () => {
const { value } = this.inputRef.current!;
if (!value) {
- alert("Please enter a group name");
+ alert('Please enter a group name');
return;
}
- if (["admin", "public", "override"].includes(value.toLowerCase())) {
- if (value.toLowerCase() !== "admin" || (value.toLowerCase() === "admin" && this.getGroup("Admin"))) {
+ if (['admin', 'public', 'override'].includes(value.toLowerCase())) {
+ if (value.toLowerCase() !== 'admin' || (value.toLowerCase() === 'admin' && this.getGroup('Admin'))) {
alert(`You cannot override the ${value.charAt(0).toUpperCase() + value.slice(1)} group`);
return;
}
}
if (this.getGroup(value)) {
- alert("Please select a unique group name");
+ alert('Please select a unique group name');
return;
}
- this.createGroupDoc(value, this.selectedUsers?.map(user => user.value));
+ this.createGroupDoc(
+ value,
+ this.selectedUsers?.map(user => user.value)
+ );
this.selectedUsers = null;
- this.inputRef.current!.value = "";
- this.buttonColour = "#979797";
+ this.inputRef.current!.value = '';
+ this.buttonColour = '#979797';
const { left, width, top } = this.createGroupButtonRef.current!.getBoundingClientRect();
TaskCompletionBox.popupX = left - 2 * width;
TaskCompletionBox.popupY = top;
- TaskCompletionBox.textDisplayed = "Group created!";
+ TaskCompletionBox.textDisplayed = 'Group created!';
TaskCompletionBox.taskCompleted = true;
- setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2000);
-
- }
+ setTimeout(
+ action(() => (TaskCompletionBox.taskCompleted = false)),
+ 2000
+ );
+ };
/**
* @returns the MainViewModal which allows the user to create groups.
@@ -268,50 +279,43 @@ export class GroupManager extends React.Component<{}> {
const contents = (
<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; TaskCompletionBox.taskCompleted = false;
- })}>
- <FontAwesomeIcon icon={"times"} color={"black"} size={"lg"} />
+ <p>
+ <b>New Group</b>
+ </p>
+ <div
+ className={'close-button'}
+ onClick={action(() => {
+ this.createGroupModalOpen = false;
+ TaskCompletionBox.taskCompleted = false;
+ })}>
+ <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} />
</div>
</div>
- <input
- 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")} />
+ <input 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'))} />
<Select
isMulti
options={this.options}
onChange={this.handleChange}
- placeholder={"Select users"}
+ placeholder={'Select users'}
value={this.selectedUsers}
closeMenuOnSelect={false}
styles={{
dropdownIndicator: (base, state) => ({
...base,
transition: '0.5s all ease',
- transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : undefined
+ transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : undefined,
}),
- multiValue: (base) => ({
+ multiValue: base => ({
...base,
- maxWidth: "50%",
+ maxWidth: '50%',
'&:hover': {
- maxWidth: "unset"
- }
- })
+ maxWidth: 'unset',
+ },
+ }),
}}
/>
- <button
- ref={this.createGroupButtonRef}
- onClick={this.createGroup}
- style={{ background: this.buttonColour }}
- disabled={this.buttonColour === "#979797"}
- >
+ <button ref={this.createGroupButtonRef} onClick={this.createGroup} style={{ background: this.buttonColour }} disabled={this.buttonColour === '#979797'}>
Create
</button>
</div>
@@ -322,8 +326,12 @@ export class GroupManager extends React.Component<{}> {
isDisplayed={this.createGroupModalOpen}
interactive={true}
contents={contents}
- dialogueBoxStyle={{ width: "90%", height: "70%" }}
- closeOnExternalClick={action(() => { this.createGroupModalOpen = false; this.selectedUsers = null; TaskCompletionBox.taskCompleted = false; })}
+ dialogueBoxStyle={{ width: '90%', height: '70%' }}
+ closeOnExternalClick={action(() => {
+ this.createGroupModalOpen = false;
+ this.selectedUsers = null;
+ TaskCompletionBox.taskCompleted = false;
+ })}
/>
);
}
@@ -332,7 +340,6 @@ export 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.title);
const g2 = StrCast(d2.title);
@@ -340,62 +347,50 @@ export class GroupManager extends React.Component<{}> {
return g1 < g2 ? -1 : g1 === g2 ? 0 : 1;
};
- const groups = this.groupSort === "ascending" ? this.allGroups.sort(sortGroups) : this.groupSort === "descending" ? this.allGroups.sort(sortGroups).reverse() : this.allGroups;
+ const groups = this.groupSort === 'ascending' ? this.allGroups.sort(sortGroups) : this.groupSort === 'descending' ? this.allGroups.sort(sortGroups).reverse() : this.allGroups;
return (
<div className="group-interface">
{this.groupCreationModal}
- {this.currentGroup ?
- <GroupMemberView
- group={this.currentGroup}
- onCloseButtonClick={action(() => this.currentGroup = undefined)}
- />
- : null}
+ {this.currentGroup ? <GroupMemberView group={this.currentGroup} onCloseButtonClick={action(() => (this.currentGroup = undefined))} /> : null}
<div className="group-heading">
- <p><b>Manage Groups</b></p>
- <button onClick={action(() => this.createGroupModalOpen = true)}>
- <FontAwesomeIcon icon={"plus"} size={"sm"} /> Create Group
+ <p>
+ <b>Manage Groups</b>
+ </p>
+ <button onClick={action(() => (this.createGroupModalOpen = true))}>
+ <FontAwesomeIcon icon={'plus'} size={'sm'} /> Create Group
</button>
- <div className={"close-button"} onClick={this.close}>
- <FontAwesomeIcon icon={"times"} color={"black"} size={"lg"} />
+ <div className={'close-button'} onClick={this.close}>
+ <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} />
</div>
</div>
<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" ? <FontAwesomeIcon icon={"caret-up"} size={"xs"} />
- : this.groupSort === "descending" ? <FontAwesomeIcon icon={"caret-down"} size={"xs"} />
- : <FontAwesomeIcon icon={"caret-right"} size={"xs"} />
- }
+ <div className="sort-groups" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
+ Name{' '}
+ {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>
<div className="group-body">
- {groups.map(group =>
- <div
- className="group-row"
- key={StrCast(group.title || group.groupName)}
- >
- <div className="group-name" >{group.title || group.groupName}</div>
- <div className="group-info" onClick={action(() => this.currentGroup = group)}>
- <FontAwesomeIcon icon={"info-circle"} color={"#e8e8e8"} size={"sm"} style={{ backgroundColor: "#1e89d7", borderRadius: "100%", border: "1px solid #1e89d7" }} />
+ {groups.map(group => (
+ <div className="group-row" key={StrCast(group.title || group.groupName)}>
+ <div className="group-name">{StrCast(group.title || group.groupName)}</div>
+ <div className="group-info" onClick={action(() => (this.currentGroup = group))}>
+ <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
</div>
</div>
- )}
+ ))}
</div>
</div>
-
</div>
);
}
render() {
- return <MainViewModal
- contents={this.groupInterface}
- isDisplayed={this.isOpen}
- interactive={true}
- dialogueBoxStyle={{ zIndex: 1002 }}
- overlayStyle={{ zIndex: 1001 }}
- closeOnExternalClick={this.close}
- />;
+ return <MainViewModal contents={this.groupInterface} isDisplayed={this.isOpen} interactive={true} dialogueBoxStyle={{ zIndex: 1002 }} overlayStyle={{ zIndex: 1001 }} closeOnExternalClick={this.close} />;
}
-} \ No newline at end of file
+}
diff --git a/src/client/util/ScrollBox.tsx b/src/client/util/ScrollBox.tsx
index a209874a3..d4620ae3f 100644
--- a/src/client/util/ScrollBox.tsx
+++ b/src/client/util/ScrollBox.tsx
@@ -1,21 +1,24 @@
-import React = require("react");
+import React = require('react');
-export class ScrollBox extends React.Component {
+export class ScrollBox extends React.Component<React.PropsWithChildren<{}>> {
onWheel = (e: React.WheelEvent) => {
- if (e.currentTarget.scrollHeight > e.currentTarget.clientHeight) { // If the element has a scroll bar, then we don't want the containing collection to zoom
+ if (e.currentTarget.scrollHeight > e.currentTarget.clientHeight) {
+ // If the element has a scroll bar, then we don't want the containing collection to zoom
e.stopPropagation();
}
- }
+ };
render() {
return (
- <div style={{
- overflow: "auto",
- width: "100%",
- height: "100%",
- }} onWheel={this.onWheel}>
+ <div
+ style={{
+ overflow: 'auto',
+ width: '100%',
+ height: '100%',
+ }}
+ onWheel={this.onWheel}>
{this.props.children}
</div>
);
}
-} \ No newline at end of file
+}
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index d77633b8d..1acb3ab38 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,29 +1,29 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { 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 { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt, AclSelfEdit } from "../../fields/Doc";
-import { List } from "../../fields/List";
-import { Cast, NumCast, StrCast } from "../../fields/Types";
-import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from "../../fields/util";
-import { Utils } from "../../Utils";
-import { DocServer } from "../DocServer";
-import { CollectionView } from "../views/collections/CollectionView";
-import { DictationOverlay } from "../views/DictationOverlay";
-import { MainViewModal } from "../views/MainViewModal";
-import { DocumentView } from "../views/nodes/DocumentView";
-import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
-import { SearchBox } from "../views/search/SearchBox";
-import { CurrentUserUtils } from "./CurrentUserUtils";
-import { DocumentManager } from "./DocumentManager";
-import { GroupManager, UserOptions } from "./GroupManager";
-import { GroupMemberView } from "./GroupMemberView";
-import { SelectionManager } from "./SelectionManager";
-import "./SharingManager.scss";
-import { LinkManager } from "./LinkManager";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { 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 { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt, AclSelfEdit } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
+import { Utils } from '../../Utils';
+import { DocServer } from '../DocServer';
+import { CollectionView } from '../views/collections/CollectionView';
+import { DictationOverlay } from '../views/DictationOverlay';
+import { MainViewModal } from '../views/MainViewModal';
+import { DocumentView } from '../views/nodes/DocumentView';
+import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
+import { SearchBox } from '../views/search/SearchBox';
+import { CurrentUserUtils } from './CurrentUserUtils';
+import { DocumentManager } from './DocumentManager';
+import { GroupManager, UserOptions } from './GroupManager';
+import { GroupMemberView } from './GroupMemberView';
+import { SelectionManager } from './SelectionManager';
+import './SharingManager.scss';
+import { LinkManager } from './LinkManager';
export interface User {
email: string;
@@ -44,19 +44,19 @@ interface GroupedOptions {
// const DefaultColor = "black";
// used to differentiate between individuals and groups when sharing
-const indType = "!indType/";
-const groupType = "!groupType/";
+const indType = '!indType/';
+const groupType = '!groupType/';
-const storage = "data";
+const storage = 'data';
/**
* A user who also has a sharing doc.
*/
interface ValidatedUser {
- user: User; // database minimal info to identify / communicate with a user (email, sharing doc id)
- sharingDoc: Doc; // document to share/message another user
+ user: User; // database minimal info to identify / communicate with a user (email, sharing doc id)
+ sharingDoc: Doc; // document to share/message another user
linkDatabase: Doc;
- userColor: string; // stored on the sharinDoc, extracted for convenience?
+ userColor: string; // stored on the sharinDoc, extracted for convenience?
}
@observer
@@ -71,8 +71,8 @@ export class SharingManager extends React.Component<{}> {
@observable private overlayOpacity = 0.4; // for the modal
@observable private selectedUsers: UserOptions[] | null = null; // users (individuals/groups) selected to share with
@observable private permissions: SharingPermissions = SharingPermissions.Edit; // the permission with which to share with other users
- @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
+ @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
@@ -89,7 +89,7 @@ export class SharingManager extends React.Component<{}> {
[AclAugment, SharingPermissions.Augment],
[AclSelfEdit, SharingPermissions.SelfEdit],
[AclEdit, SharingPermissions.Edit],
- [AclAdmin, SharingPermissions.Admin]
+ [AclAdmin, SharingPermissions.Admin],
]);
// private get linkVisible() {
@@ -105,17 +105,20 @@ export class SharingManager extends React.Component<{}> {
this.isOpen = this.targetDoc !== undefined;
this.permissions = SharingPermissions.Augment;
});
- }
+ };
public close = action(() => {
this.isOpen = false;
this.selectedUsers = null; // resets the list of users and selected users (in the react-select component)
TaskCompletionBox.taskCompleted = false;
- setTimeout(action(() => {
- // this.copied = false;
- DictationOverlay.Instance.hasActiveModal = false;
- this.targetDoc = undefined;
- }), 500);
+ setTimeout(
+ action(() => {
+ // this.copied = false;
+ DictationOverlay.Instance.hasActiveModal = false;
+ this.targetDoc = undefined;
+ }),
+ 500
+ );
});
constructor(props: {}) {
@@ -136,7 +139,7 @@ export class SharingManager extends React.Component<{}> {
populateUsers = async () => {
if (!this.populating) {
this.populating = true;
- const userList = await RequestPromise.get(Utils.prepend("/getUsers"));
+ const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
const raw = JSON.parse(userList) as User[];
const sharingDocs: ValidatedUser[] = [];
const evaluating = raw.map(async user => {
@@ -146,7 +149,8 @@ export class SharingManager extends React.Component<{}> {
const linkDatabase = await DocServer.GetRefField(user.linkDatabaseId);
if (sharingDoc instanceof Doc && linkDatabase instanceof Doc) {
await DocListCastAsync(linkDatabase.data);
- (await DocListCastAsync(Cast(linkDatabase, Doc, null).data))?.forEach(async link => { // makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager
+ (await DocListCastAsync(Cast(linkDatabase, Doc, null).data))?.forEach(async link => {
+ // makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager
const a1 = await Cast(link?.anchor1, Doc, null);
const a2 = await Cast(link?.anchor2, Doc, null);
});
@@ -166,7 +170,7 @@ export class SharingManager extends React.Component<{}> {
this.populating = false;
});
}
- }
+ };
/**
* Shares the document with a user.
@@ -179,31 +183,31 @@ export class SharingManager extends React.Component<{}> {
const isDashboard = DocListCast(CurrentUserUtils.MyDashboards.data).indexOf(target) !== -1;
const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
- return !docs.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;
- }
+ return !docs
+ .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);
+ 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);
- else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc || doc));
- }).some(success => !success);
- }
+ this.setDashboardBackground(doc, permission as SharingPermissions);
+ if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
+ else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc);
+ })
+ .some(success => !success);
+ };
/**
* Sets the permission on the target for the group.
- * @param group
- * @param permission
+ * @param group
+ * @param permission
*/
setInternalGroupSharing = (group: Doc | { title: string }, permission: string, targetDoc?: Doc) => {
-
const target = targetDoc || this.targetDoc!;
const key = normalizeEmail(StrCast(group.title));
const acl = `acl-${key}`;
@@ -212,38 +216,41 @@ export class SharingManager extends React.Component<{}> {
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 => {
- 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;
- }
+ return !docs
+ .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;
+ }
- distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
- this.setDashboardBackground(doc, permission as SharingPermissions);
+ distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
+ this.setDashboardBackground(doc, permission as SharingPermissions);
- 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) {
+ const members: string[] = JSON.parse(StrCast(group.members));
+ const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
- // 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]);
+ // 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.aliasOf as Doc || doc)); // remove the doc from the list if it already exists
- }).some(success => !success);
- }
- }).some(success => success);
- }
+ 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.aliasOf as Doc) || doc); // remove the doc from the list if it already exists
+ })
+ .some(success => !success);
+ }
+ })
+ .some(success => success);
+ };
/**
* Shares the documents shared with a group with a new user who has been added to that group.
- * @param group
- * @param emailId
+ * @param group
+ * @param emailId
*/
shareWithAddedMember = (group: Doc, emailId: string, retry: boolean = true) => {
const user = this.users.find(({ user: { email } }) => email === emailId)!;
@@ -255,30 +262,30 @@ export class SharingManager extends React.Component<{}> {
DocListCastAsync(group.docsShared).then(dl => {
const filtered = dl?.filter(doc => !userdocs?.includes(doc));
filtered && userdocs?.push(...filtered);
- }));
+ })
+ );
}
}
- }
+ };
/**
* Called from the properties sidebar to change permissions of a user.
*/
shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, docs: Doc[]) => {
- if (shareWith !== "Public" && shareWith !== "Override") {
- const user = this.users.find(({ user: { email } }) => email === (shareWith === "Me" ? Doc.CurrentUserEmail : shareWith));
+ if (shareWith !== 'Public' && shareWith !== 'Override') {
+ 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 {
+ } else {
const dashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
docs.forEach(doc => {
const isDashboard = dashboards.indexOf(doc) !== -1;
if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
});
}
- }
+ };
/**
* Sets the background of the Dashboard if it has been shared as a visual indicator
@@ -287,22 +294,21 @@ export class SharingManager extends React.Component<{}> {
if (Doc.IndexOf(doc, DocListCast(CurrentUserUtils.MyDashboards.data)) !== -1) {
if (permission !== SharingPermissions.None) {
doc.isShared = true;
- doc.backgroundColor = "green";
- }
- else {
+ doc.backgroundColor = 'green';
+ } else {
const acls = doc[DataSym][AclSym];
- if (Object.keys(acls).every(key => key === `acl-${Doc.CurrentUserEmailNormalized}` ? true : [AclUnset, AclPrivate].includes(acls[key]))) {
+ if (Object.keys(acls).every(key => (key === `acl-${Doc.CurrentUserEmailNormalized}` ? true : [AclUnset, AclPrivate].includes(acls[key])))) {
doc.isShared = undefined;
doc.backgroundColor = undefined;
}
}
}
- }
+ };
/**
* Removes the documents shared with a user through a group when the user is removed from the group.
- * @param group
- * @param emailId
+ * @param group
+ * @param emailId
*/
removeMember = (group: Doc, emailId: string) => {
const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!;
@@ -315,11 +321,11 @@ export class SharingManager extends React.Component<{}> {
})
);
}
- }
+ };
/**
* Removes a group's permissions from documents that have been shared with it.
- * @param group
+ * @param group
*/
removeGroup = (group: Doc) => {
if (group.docsShared) {
@@ -335,9 +341,7 @@ export class SharingManager extends React.Component<{}> {
users.forEach(({ sharingDoc }) => Doc.RemoveDocFromList(sharingDoc, storage, doc));
});
}
- }
-
-
+ };
// private setExternalSharing = (permission: string) => {
// const targetDoc = this.targetDoc;
@@ -367,23 +371,23 @@ export class SharingManager extends React.Component<{}> {
*/
private sharingOptions(uniform: boolean, override?: boolean) {
const dropdownValues: string[] = Object.values(SharingPermissions);
- if (!uniform) dropdownValues.unshift("-multiple-");
- if (override) dropdownValues.unshift("None");
- return dropdownValues.filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
- (
- <option key={permission} value={permission}>
- {permission}
- </option>
- )
- );
+ if (!uniform) dropdownValues.unshift('-multiple-');
+ if (override) dropdownValues.unshift('None');
+ return dropdownValues
+ .filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any))
+ .map(permission => (
+ <option key={permission} value={permission}>
+ {permission}
+ </option>
+ ));
}
private focusOn = (contents: string) => {
- const title = this.targetDoc ? StrCast(this.targetDoc.title) : "";
+ const title = this.targetDoc ? StrCast(this.targetDoc.title) : '';
const docs = SelectionManager.Views().length > 1 ? SelectionManager.Views().map(docView => docView.props.Document) : [this.targetDoc];
return (
<span
- className={"focus-span"}
+ className={'focus-span'}
title={title}
onClick={() => {
let context: Opt<CollectionView>;
@@ -404,12 +408,11 @@ export class SharingManager extends React.Component<{}> {
this.dialogueBoxOpacity = 1;
this.overlayOpacity = 0.4;
}
- })}
- >
+ })}>
{contents}
</span>
);
- }
+ };
/**
* Handles changes in the users selected in react-select
@@ -417,7 +420,7 @@ export class SharingManager extends React.Component<{}> {
@action
handleUsersChange = (selectedOptions: any) => {
this.selectedUsers = selectedOptions as UserOptions[];
- }
+ };
/**
* Handles changes in the permission chosen to share with someone with
@@ -425,7 +428,7 @@ export class SharingManager extends React.Component<{}> {
@action
handlePermissionsChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
this.permissions = event.currentTarget.value as SharingPermissions;
- }
+ };
/**
* Calls the relevant method for sharing, displays the popup, and resets the relevant variables.
@@ -436,8 +439,7 @@ export class SharingManager extends React.Component<{}> {
this.selectedUsers.forEach(user => {
if (user.value.includes(indType)) {
this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions);
- }
- else {
+ } else {
this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
}
});
@@ -445,13 +447,16 @@ export class SharingManager extends React.Component<{}> {
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.textDisplayed = 'Document shared!';
TaskCompletionBox.taskCompleted = true;
- setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2000);
+ setTimeout(
+ action(() => (TaskCompletionBox.taskCompleted = false)),
+ 2000
+ );
this.selectedUsers = null;
}
- }
+ };
// distributeOverCollection = (targetDoc?: Doc) => {
// const target = targetDoc || this.targetDoc!;
@@ -471,7 +476,7 @@ export class SharingManager extends React.Component<{}> {
const { email: e1 } = u1.user;
const { email: e2 } = u2.user;
return e1 < e2 ? -1 : e1 === e2 ? 0 : 1;
- }
+ };
/**
* Sorting algorithm to sort groups.
@@ -480,7 +485,7 @@ export class SharingManager extends React.Component<{}> {
const g1 = StrCast(group1.title);
const g2 = StrCast(group2.title);
return g1 < g2 ? -1 : g1 === g2 ? 0 : 1;
- }
+ };
/**
* @returns the main interface of the SharingManager.
@@ -488,28 +493,29 @@ export class SharingManager extends React.Component<{}> {
@computed get sharingInterface() {
TraceMobx();
const groupList = GroupManager.Instance?.allGroups || [];
- const sortedUsers = this.users.slice().sort(this.sortUsers).map(({ user: { email } }) => ({ label: email, value: indType + email }));
- const sortedGroups = groupList.slice().sort(this.sortGroups).map(({ title }) => ({ label: StrCast(title), value: groupType + StrCast(title) }));
+ const sortedUsers = this.users
+ .slice()
+ .sort(this.sortUsers)
+ .map(({ user: { email } }) => ({ label: email, value: indType + email }));
+ const sortedGroups = groupList
+ .slice()
+ .sort(this.sortGroups)
+ .map(({ title }) => ({ label: StrCast(title), value: groupType + StrCast(title) }));
// the next block handles the users shown (individuals/groups/both)
const options: GroupedOptions[] = [];
if (GroupManager.Instance) {
if ((this.showUserOptions && this.showGroupOptions) || (!this.showUserOptions && !this.showGroupOptions)) {
- options.push(
- { label: 'Individuals', options: sortedUsers },
- { label: 'Groups', options: sortedGroups });
- }
- else if (this.showUserOptions) options.push({ label: 'Individuals', options: sortedUsers });
+ options.push({ label: 'Individuals', options: sortedUsers }, { label: 'Groups', options: sortedGroups });
+ } else if (this.showUserOptions) options.push({ label: 'Individuals', options: sortedUsers });
else options.push({ label: 'Groups', options: sortedGroups });
}
- 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;
+ 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.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DataSym]));
if (this.myDocAcls) {
const newDocs: Doc[] = [];
@@ -524,107 +530,78 @@ export class SharingManager extends React.Component<{}> {
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?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym]?.[AclSym] && Object.keys(doc[DataSym][AclSym])));
+ const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym]?.[AclSym] && Object.keys(doc[DataSym][AclSym]))));
// 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).map(({ user, linkDatabase, sharingDoc, userColor }) => {
- const userKey = `acl-${normalizeEmail(user.email)}`;
- const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey] : doc?.[DataSym]?.[AclSym]?.[userKey] === docs[0]?.[DataSym]?.[AclSym]?.[userKey]);
- const 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)}
- >
- {this.sharingOptions(uniform)}
- </select>
- ) : (
- <div className={"permissions-dropdown"}>
- {permissions}
- </div>
- )}
+ const userListContents: (JSX.Element | null)[] = users
+ .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
+ .map(({ user, linkDatabase, sharingDoc, userColor }) => {
+ const userKey = `acl-${normalizeEmail(user.email)}`;
+ const uniform = docs.every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey] : doc?.[DataSym]?.[AclSym]?.[userKey] === docs[0]?.[DataSym]?.[AclSym]?.[userKey]));
+ const 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)}>
+ {this.sharingOptions(uniform)}
+ </select>
+ ) : (
+ <div className={'permissions-dropdown'}>{permissions}</div>
+ )}
+ </div>
</div>
- </div>
- );
- });
+ );
+ });
// checks if every doc has the same author
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.
userListContents.unshift(
- sameAuthor ?
- (
- <div
- key={"owner"}
- className={"container"}
- >
- <span className={"padding"}>{targetDoc?.author === Doc.CurrentUserEmail ? "Me" : targetDoc?.author}</span>
- <div className="edit-actions">
- <div className={"permissions-dropdown"}>
- Owner
- </div>
- </div>
+ sameAuthor ? (
+ <div key={'owner'} className={'container'}>
+ <span className={'padding'}>{targetDoc?.author === Doc.CurrentUserEmail ? 'Me' : targetDoc?.author}</span>
+ <div className="edit-actions">
+ <div className={'permissions-dropdown'}>Owner</div>
</div>
- ) : null,
- sameAuthor && targetDoc?.author !== Doc.CurrentUserEmail ?
- (
- <div
- key={"me"}
- className={"container"}
- >
- <span className={"padding"}>Me</span>
- <div className="edit-actions">
- <div className={"permissions-dropdown"}>
- {effectiveAcls.every(acl => acl === effectiveAcls[0]) ? this.AclMap.get(effectiveAcls[0])! : "-multiple-"}
- </div>
- </div>
+ </div>
+ ) : null,
+ sameAuthor && targetDoc?.author !== Doc.CurrentUserEmail ? (
+ <div key={'me'} className={'container'}>
+ <span className={'padding'}>Me</span>
+ <div className="edit-actions">
+ <div className={'permissions-dropdown'}>{effectiveAcls.every(acl => acl === effectiveAcls[0]) ? this.AclMap.get(effectiveAcls[0])! : '-multiple-'}</div>
</div>
- ) : null
+ </div>
+ ) : null
);
-
// 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" });
+ const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
+ groupListMap.unshift({ title: 'Public' }); //, { title: "ALL" });
const groupListContents = groupListMap.map(group => {
const groupKey = `acl-${StrCast(group.title)}`;
- const uniform = docs.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-";
-
- return !permissions ? (null) : (
- <div
- key={groupKey}
- className={"container"}
- >
- <div className={"padding"}>{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)}
+ const uniform = docs.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-';
+
+ return !permissions ? null : (
+ <div key={groupKey} className={'container'}>
+ <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">
{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'} value={permissions} onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}>
+ {this.sharingOptions(uniform, group.title === 'Override')}
</select>
) : (
- <div className={"permissions-dropdown"}>
- {permissions}
- </div>
+ <div className={'permissions-dropdown'}>{permissions}</div>
)}
</div>
</div>
@@ -632,102 +609,95 @@ 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-interface'}>
+ {GroupManager.Instance?.currentGroup ? <GroupMemberView group={GroupManager.Instance.currentGroup} onCloseButtonClick={action(() => (GroupManager.Instance.currentGroup = undefined))} /> : null}
<div className="sharing-contents">
- <p className={"share-title"}><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"} />
+ <p className={'share-title'}>
+ <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>
{/* {this.linkVisible ?
<div>
{this.sharingUrl}
</div> :
(null)} */}
- {<div className="share-container">
- <div className="share-setup">
- <Select
- className="user-search"
- placeholder="Enter user or group name..."
- isMulti
- isSearchable
- closeMenuOnSelect={false}
- options={options}
- onKeyDown={e => e.stopPropagation()}
- onChange={this.handleUsersChange}
- value={this.selectedUsers}
- styles={{
- indicatorSeparator: () => ({
- visibility: "hidden"
- })
- }}
- />
- <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>
- <div className="sort-checkboxes">
- <input type="checkbox" onChange={action(() => this.showUserOptions = !this.showUserOptions)} /> <label style={{ marginRight: 10 }}>Individuals</label>
- <input type="checkbox" onChange={action(() => this.showGroupOptions = !this.showGroupOptions)} /> <label>Groups</label>
- </div>
+ {
+ <div className="share-container">
+ <div className="share-setup">
+ <Select
+ className="user-search"
+ placeholder="Enter user or group name..."
+ isMulti
+ isSearchable
+ closeMenuOnSelect={false}
+ options={options}
+ onKeyDown={e => e.stopPropagation()}
+ onChange={this.handleUsersChange}
+ value={this.selectedUsers}
+ styles={{
+ indicatorSeparator: () => ({
+ visibility: 'hidden',
+ }),
+ }}
+ />
+ <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>
+ <div className="sort-checkboxes">
+ <input type="checkbox" onChange={action(() => (this.showUserOptions = !this.showUserOptions))} /> <label style={{ marginRight: 10 }}>Individuals</label>
+ <input type="checkbox" onChange={action(() => (this.showGroupOptions = !this.showGroupOptions))} /> <label>Groups</label>
+ </div>
- <div className="acl-container">
- {Doc.noviceMode ? (null) :
- <div className="layoutDoc-acls">
- <input type="checkbox" onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)} checked={this.layoutDocAcls} /> <label>Layout</label>
- </div>}
+ <div className="acl-container">
+ {Doc.noviceMode ? null : (
+ <div className="layoutDoc-acls">
+ <input type="checkbox" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <label>Layout</label>
+ </div>
+ )}
+ </div>
</div>
- </div>
}
<div className="main-container">
- <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>
- <div className={"users-list"}>
- {userListContents}
+ <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>
+ <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>
- <div className={"groups-list"}>
- {groupListContents}
+ <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>
+ <div className={'groups-list'}>{groupListContents}</div>
</div>
</div>
-
</div>
</div>
);
}
render() {
- return <MainViewModal
- contents={this.sharingInterface}
- isDisplayed={this.isOpen}
- interactive={true}
- dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity}
- overlayDisplayedOpacity={this.overlayOpacity}
- closeOnExternalClick={this.close}
- />;
+ return <MainViewModal contents={this.sharingInterface} isDisplayed={this.isOpen} interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} closeOnExternalClick={this.close} />;
}
-} \ No newline at end of file
+}