aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/DictationManager.ts46
-rw-r--r--src/client/util/History.ts6
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx80
-rw-r--r--src/client/util/SharingManager.scss136
-rw-r--r--src/client/util/SharingManager.tsx293
5 files changed, 510 insertions, 51 deletions
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index fb3c15cea..0711effe6 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -3,7 +3,7 @@ import { DocumentView } from "../views/nodes/DocumentView";
import { UndoManager } from "./UndoManager";
import * as interpreter from "words-to-numbers";
import { DocumentType } from "../documents/DocumentTypes";
-import { Doc } from "../../new_fields/Doc";
+import { Doc, Opt } from "../../new_fields/Doc";
import { List } from "../../new_fields/List";
import { Docs } from "../documents/Documents";
import { CollectionViewType } from "../views/collections/CollectionBaseView";
@@ -40,12 +40,26 @@ export namespace DictationManager {
webkitSpeechRecognition: any;
}
}
- const { webkitSpeechRecognition }: CORE.IWindow = window as CORE.IWindow;
+ const { webkitSpeechRecognition }: CORE.IWindow = window as any as CORE.IWindow;
export const placeholder = "Listening...";
export namespace Controls {
export const Infringed = "unable to process: dictation manager still involved in previous session";
+ const browser = (() => {
+ let identifier = navigator.userAgent.toLowerCase();
+ if (identifier.indexOf("safari") >= 0) {
+ return "Safari";
+ }
+ if (identifier.indexOf("chrome") >= 0) {
+ return "Chrome";
+ }
+ if (identifier.indexOf("firefox") >= 0) {
+ return "Firefox";
+ }
+ return "Unidentified Browser";
+ })();
+ const unsupported = `listening is not supported in ${browser}`;
const intraSession = ". ";
const interSession = " ... ";
@@ -55,8 +69,7 @@ export namespace DictationManager {
let current: string | undefined = undefined;
let sessionResults: string[] = [];
- const recognizer: SpeechRecognition = new webkitSpeechRecognition() || new SpeechRecognition();
- recognizer.onstart = () => console.log("initiating speech recognition session...");
+ const recognizer: Opt<SpeechRecognition> = webkitSpeechRecognition ? new webkitSpeechRecognition() : undefined;
export type InterimResultHandler = (results: string) => any;
export type ContinuityArgs = { indefinite: boolean } | false;
@@ -109,6 +122,10 @@ export namespace DictationManager {
};
const listenImpl = (options?: Partial<ListeningOptions>) => {
+ if (!recognizer) {
+ console.log(unsupported);
+ return unsupported;
+ }
if (isListening) {
return Infringed;
}
@@ -121,6 +138,7 @@ export namespace DictationManager {
let intra = options && options.delimiters ? options.delimiters.intra : undefined;
let inter = options && options.delimiters ? options.delimiters.inter : undefined;
+ recognizer.onstart = () => console.log("initiating speech recognition session...");
recognizer.interimResults = handler !== undefined;
recognizer.continuous = continuous === undefined ? false : continuous !== false;
recognizer.lang = language === undefined ? "en-US" : language;
@@ -167,14 +185,20 @@ export namespace DictationManager {
} else {
resolve(current);
}
- reset();
+ current = undefined;
+ sessionResults = [];
+ isListening = false;
+ isManuallyStopped = false;
+ recognizer.onresult = null;
+ recognizer.onerror = null;
+ recognizer.onend = null;
};
});
};
export const stop = (salvageSession = true) => {
- if (!isListening) {
+ if (!isListening || !recognizer) {
return;
}
isManuallyStopped = true;
@@ -197,16 +221,6 @@ export namespace DictationManager {
return transcripts.join(delimiter || intraSession);
};
- const reset = () => {
- current = undefined;
- sessionResults = [];
- isListening = false;
- isManuallyStopped = false;
- recognizer.onresult = null;
- recognizer.onerror = null;
- recognizer.onend = null;
- };
-
}
export namespace Commands {
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index e9ff21b22..c72ae05de 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -16,8 +16,10 @@ export namespace HistoryUtil {
initializers?: {
[docId: string]: DocInitializerList;
};
+ safe?: boolean;
readonly?: boolean;
nro?: boolean;
+ sharing?: boolean;
}
export type ParsedUrl = DocUrl;
@@ -141,7 +143,7 @@ export namespace HistoryUtil {
};
}
- addParser("doc", {}, { readonly: true, initializers: true, nro: true }, (pathname, opts, current) => {
+ addParser("doc", {}, { readonly: true, initializers: true, nro: true, sharing: true }, (pathname, opts, current) => {
if (pathname.length !== 2) return undefined;
current.initializers = current.initializers || {};
@@ -156,7 +158,7 @@ export namespace HistoryUtil {
export function parseUrl(location: Location | URL): ParsedUrl | undefined {
const pathname = location.pathname.substring(1);
const search = location.search;
- const opts = qs.parse(search, { sort: false });
+ const opts = search.length ? qs.parse(search, { sort: false }) : {};
let pathnameSplit = pathname.split("/");
const type = pathnameSplit[0];
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 75b0b52a7..260c6a629 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -18,8 +18,14 @@ import { Id } from "../../../new_fields/FieldSymbols";
import { List } from "../../../new_fields/List";
import { Cast, BoolCast, NumCast } from "../../../new_fields/Types";
import { listSpec } from "../../../new_fields/Schema";
+import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils";
const unsupported = ["text/html", "text/plain"];
+interface FileResponse {
+ name: string;
+ path: string;
+ type: string;
+}
@observer
export default class DirectoryImportBox extends React.Component<FieldViewProps> {
@@ -31,7 +37,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
@observable private entries: ImportMetadataEntry[] = [];
@observable private quota = 1;
- @observable private remaining = 1;
+ @observable private completed = 0;
@observable private uploading = false;
@observable private removeHover = false;
@@ -68,13 +74,12 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
handleSelection = async (e: React.ChangeEvent<HTMLInputElement>) => {
runInAction(() => this.uploading = true);
- let promises: Promise<void>[] = [];
let docs: Doc[] = [];
let files = e.target.files;
if (!files || files.length === 0) return;
- let directory = (files.item(0) as any).webkitRelativePath.split("/", 1);
+ let directory = (files.item(0) as any).webkitRelativePath.split("/", 1)[0];
let validated: File[] = [];
for (let i = 0; i < files.length; i++) {
@@ -82,37 +87,46 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
file && !unsupported.includes(file.type) && validated.push(file);
}
- runInAction(() => this.quota = validated.length);
+ runInAction(() => {
+ this.quota = validated.length;
+ this.completed = 0;
+ });
let sizes = [];
let modifiedDates = [];
- for (let uploaded_file of validated) {
+ let i = 0;
+ const uploads: FileResponse[] = [];
+ const batchSize = 15;
+
+ while (i < validated.length) {
+ const cap = Math.min(validated.length, i + batchSize);
let formData = new FormData();
- formData.append('file', uploaded_file);
- let dropFileName = uploaded_file ? uploaded_file.name : "-empty-";
- let type = uploaded_file.type;
-
- sizes.push(uploaded_file.size);
- modifiedDates.push(uploaded_file.lastModified);
-
- runInAction(() => this.remaining++);
-
- let prom = fetch(Utils.prepend(RouteStore.upload), {
- method: 'POST',
- body: formData
- }).then(async (res: Response) => {
- (await res.json()).map(action((file: any) => {
- let docPromise = Docs.Get.DocumentFromType(type, Utils.prepend(file), { nativeWidth: 300, width: 300, title: dropFileName });
- docPromise.then(doc => {
- doc && docs.push(doc) && runInAction(() => this.remaining--);
- });
- }));
- });
- promises.push(prom);
+ const batch = validated.slice(i, cap);
+
+ sizes.push(...batch.map(file => file.size));
+ modifiedDates.push(...batch.map(file => file.lastModified));
+
+ batch.forEach(file => formData.append(Utils.GenerateGuid(), file));
+ const parameters = { method: 'POST', body: formData };
+ uploads.push(...(await (await fetch(Utils.prepend(RouteStore.upload), parameters)).json()));
+
+ runInAction(() => this.completed += batch.length);
+ i = cap;
}
- await Promise.all(promises);
+
+ await Promise.all(uploads.map(async upload => {
+ const type = upload.type;
+ const path = Utils.prepend(upload.path);
+ const options = {
+ nativeWidth: 300,
+ width: 300,
+ title: upload.name
+ };
+ const document = await Docs.Get.DocumentFromType(type, path, options);
+ document && docs.push(document);
+ }));
for (let i = 0; i < docs.length; i++) {
let doc = docs[i];
@@ -136,18 +150,18 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
};
let parent = this.props.ContainingCollectionView;
if (parent) {
- let importContainer = Docs.Create.StackingDocument(docs, options);
+ let importContainer = Docs.Create.MasonryDocument(docs, options);
+ await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer });
importContainer.singleColumn = false;
Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
!this.persistent && this.props.removeDocument && this.props.removeDocument(doc);
DocumentManager.Instance.jumpToDocument(importContainer, true);
-
}
runInAction(() => {
this.uploading = false;
this.quota = 1;
- this.remaining = 1;
+ this.completed = 0;
});
}
@@ -196,12 +210,12 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
let dimensions = 50;
let entries = DocListCast(this.props.Document.data);
let isEditing = this.editingMetadata;
- let remaining = this.remaining;
+ let completed = this.completed;
let quota = this.quota;
let uploading = this.uploading;
let showRemoveLabel = this.removeHover;
let persistent = this.persistent;
- let percent = `${100 - (remaining / quota * 100)}`;
+ let percent = `${completed / quota * 100}`;
percent = percent.split(".")[0];
percent = percent.startsWith("100") ? "99" : percent;
let marginOffset = (percent.length === 1 ? 5 : 0) - 1.6;
@@ -313,7 +327,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
style={{
pointerEvents: "none",
position: "absolute",
- right: isEditing ? 16.3 : 14.5,
+ right: isEditing ? 14 : 15,
top: isEditing ? 15.4 : 16,
opacity: uploading ? 0 : 1,
transition: "0.4s opacity ease"
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
new file mode 100644
index 000000000..9a4c5db30
--- /dev/null
+++ b/src/client/util/SharingManager.scss
@@ -0,0 +1,136 @@
+.sharing-interface {
+ display: flex;
+ flex-direction: column;
+
+ p {
+ font-size: 20px;
+ text-align: left;
+ font-style: italic;
+ padding: 0;
+ margin: 0 0 20px 0;
+ }
+
+ .hr-substitute {
+ border: solid black 0.5px;
+ margin-top: 20px;
+ }
+
+ .people-with-container {
+ display: flex;
+ height: 25px;
+
+ .people-with {
+ font-size: 14px;
+ margin: 0;
+ padding-top: 3px;
+ font-style: normal;
+ }
+
+ .people-with-select {
+ width: 126px;
+ outline: none;
+ }
+ }
+
+ .share-individual {
+ margin-top: 20px;
+ margin-bottom: 20px;
+ }
+
+ .users-list {
+ font-style: italic;
+ background: white;
+ border: 1px solid black;
+ padding-left: 10px;
+ padding-right: 10px;
+ max-height: 200px;
+ overflow: scroll;
+ height: -webkit-fill-available;
+ text-align: left;
+ display: flex;
+ align-content: center;
+ align-items: center;
+ text-align: center;
+ justify-content: center;
+ color: red;
+ }
+
+ .container {
+ display: block;
+ position: relative;
+ margin-top: 10px;
+ margin-bottom: 10px;
+ font-size: 22px;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ width: 700px;
+ min-width: 700px;
+ max-width: 700px;
+ text-align: left;
+ font-style: normal;
+ font-size: 15;
+ font-weight: normal;
+ padding: 0;
+
+ .padding {
+ padding: 0 0 0 20px;
+ color: black;
+ }
+
+ .permissions-dropdown {
+ outline: none;
+ }
+ }
+
+ .no-users {
+ margin-top: 20px;
+ }
+
+ .link-container {
+ display: flex;
+ flex-direction: row;
+ margin-bottom: 10px;
+ margin-left: auto;
+ margin-right: auto;
+
+ .link-box,
+ .copy {
+ padding: 10px;
+ border-radius: 10px;
+ padding: 10px;
+ border: solid black 1px;
+ }
+
+ .link-box {
+ background: white;
+ color: blue;
+ text-decoration: underline;
+ }
+
+ .copy {
+ margin-left: 20px;
+ cursor: alias;
+ border-radius: 50%;
+ width: 42px;
+ height: 42px;
+ transition: 1.5s all ease;
+ padding-top: 12px;
+ }
+ }
+
+ .close-button {
+ border-radius: 5px;
+ margin-top: 20px;
+ padding: 10px 0;
+ background: aliceblue;
+ transition: 0.5s ease all;
+ border: 1px solid;
+ border-color: aliceblue;
+ }
+
+ .close-button:hover {
+ border-color: black;
+ }
+} \ No newline at end of file
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
new file mode 100644
index 000000000..72a4b4141
--- /dev/null
+++ b/src/client/util/SharingManager.tsx
@@ -0,0 +1,293 @@
+import { observable, runInAction, action, autorun } from "mobx";
+import * as React from "react";
+import MainViewModal from "../views/MainViewModal";
+import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils";
+import { Doc, Opt } from "../../new_fields/Doc";
+import { DocServer } from "../DocServer";
+import { Cast, StrCast } from "../../new_fields/Types";
+import { listSpec } from "../../new_fields/Schema";
+import { List } from "../../new_fields/List";
+import { RouteStore } from "../../server/RouteStore";
+import * as RequestPromise from "request-promise";
+import { Utils } from "../../Utils";
+import "./SharingManager.scss";
+import { Id } from "../../new_fields/FieldSymbols";
+import { observer } from "mobx-react";
+import { MainView } from "../views/MainView";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { library } from '@fortawesome/fontawesome-svg-core';
+import * as fa from '@fortawesome/free-solid-svg-icons';
+import { DocumentView } from "../views/nodes/DocumentView";
+import { SelectionManager } from "./SelectionManager";
+import { DocumentManager } from "./DocumentManager";
+import { CollectionVideoView } from "../views/collections/CollectionVideoView";
+import { CollectionPDFView } from "../views/collections/CollectionPDFView";
+import { CollectionView } from "../views/collections/CollectionView";
+
+library.add(fa.faCopy);
+
+export interface User {
+ email: string;
+ userDocumentId: string;
+}
+
+export enum SharingPermissions {
+ None = "Not Shared",
+ View = "Can View",
+ Comment = "Can Comment",
+ Edit = "Can Edit"
+}
+
+const ColorMapping = new Map<string, string>([
+ [SharingPermissions.None, "red"],
+ [SharingPermissions.View, "maroon"],
+ [SharingPermissions.Comment, "blue"],
+ [SharingPermissions.Edit, "green"]
+]);
+
+const SharingKey = "sharingPermissions";
+const PublicKey = "publicLinkPermissions";
+const DefaultColor = "black";
+
+@observer
+export default class SharingManager extends React.Component<{}> {
+ public static Instance: SharingManager;
+ @observable private isOpen = false;
+ @observable private users: User[] = [];
+ @observable private targetDoc: Doc | undefined;
+ @observable private targetDocView: DocumentView | undefined;
+ @observable private copied = false;
+ @observable private dialogueBoxOpacity = 1;
+ @observable private overlayOpacity = 0.4;
+
+ private get linkVisible() {
+ return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false;
+ }
+
+ public open = (target: DocumentView) => {
+ SelectionManager.DeselectAll();
+ this.populateUsers().then(action(() => {
+ this.targetDocView = target;
+ this.targetDoc = target.props.Document;
+ MainView.Instance.hasActiveModal = true;
+ this.isOpen = true;
+ if (!this.sharingDoc) {
+ this.sharingDoc = new Doc;
+ }
+ }));
+ }
+
+ public close = action(() => {
+ this.isOpen = false;
+ setTimeout(action(() => {
+ this.copied = false;
+ MainView.Instance.hasActiveModal = false;
+ this.targetDoc = undefined;
+ }), 500);
+ });
+
+ private get sharingDoc() {
+ return this.targetDoc ? Cast(this.targetDoc[SharingKey], Doc) as Doc : undefined;
+ }
+
+ private set sharingDoc(value: Doc | undefined) {
+ this.targetDoc && (this.targetDoc[SharingKey] = value);
+ }
+
+ constructor(props: {}) {
+ super(props);
+ SharingManager.Instance = this;
+ }
+
+ populateUsers = async () => {
+ let userList = await RequestPromise.get(Utils.prepend(RouteStore.getUsers));
+ runInAction(() => {
+ this.users = (JSON.parse(userList) as User[]).filter(({ email }) => email !== CurrentUserUtils.email);
+ });
+ }
+
+ setInternalSharing = async (user: User, state: string) => {
+ if (!this.sharingDoc) {
+ console.log("SHARING ABORTED!");
+ return;
+ }
+ let sharingDoc = await this.sharingDoc;
+ sharingDoc[user.userDocumentId] = state;
+ const userDocument = await DocServer.GetRefField(user.userDocumentId);
+ if (!(userDocument instanceof Doc)) {
+ console.log(`Couldn't get user document of user ${user.email}`);
+ return;
+ }
+ let target = this.targetDoc;
+ if (!target) {
+ console.log("SharingManager trying to share an undefined document!!");
+ return;
+ }
+ const notifDoc = await Cast(userDocument.optionalRightCollection, Doc);
+ if (notifDoc instanceof Doc) {
+ const data = await Cast(notifDoc.data, listSpec(Doc));
+ if (!data) {
+ console.log("UNABLE TO ACCESS NOTIFICATION DATA");
+ return;
+ }
+ console.log(`Attempting to set permissions to ${state} for the document ${target[Id]}`);
+ if (state !== SharingPermissions.None) {
+ const sharedDoc = Doc.MakeAlias(target);
+ if (data) {
+ data.push(sharedDoc);
+ } else {
+ notifDoc.data = new List([sharedDoc]);
+ }
+ } else {
+ let dataDocs = (await Promise.all(data.map(doc => doc))).map(doc => Doc.GetProto(doc));
+ if (dataDocs.includes(target)) {
+ console.log("Searching in ", dataDocs, "for", target);
+ dataDocs.splice(dataDocs.indexOf(target), 1);
+ console.log("SUCCESSFULLY UNSHARED DOC");
+ } else {
+ console.log("DIDN'T THINK WE HAD IT, SO NOT SUCCESSFULLY UNSHARED");
+ }
+ }
+ }
+ }
+
+ private setExternalSharing = (state: string) => {
+ let sharingDoc = this.sharingDoc;
+ if (!sharingDoc) {
+ return;
+ }
+ sharingDoc[PublicKey] = state;
+ }
+
+ private get sharingUrl() {
+ if (!this.targetDoc) {
+ return undefined;
+ }
+ let baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
+ return `${baseUrl}?sharing=true`;
+ }
+
+ copy = action(() => {
+ if (this.sharingUrl) {
+ Utils.CopyText(this.sharingUrl);
+ this.copied = true;
+ }
+ });
+
+ private get sharingOptions() {
+ return Object.values(SharingPermissions).map(permission => {
+ return (
+ <option key={permission} value={permission}>
+ {permission}
+ </option>
+ );
+ });
+ }
+
+ private focusOn = (contents: string) => {
+ let title = this.targetDoc ? StrCast(this.targetDoc.title) : "";
+ return (
+ <span
+ title={title}
+ onClick={() => {
+ let context: Opt<CollectionVideoView | CollectionPDFView | CollectionView>;
+ if (this.targetDoc && this.targetDocView && (context = this.targetDocView.props.ContainingCollectionView)) {
+ DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, undefined, undefined, context.props.Document);
+ }
+ }}
+ onPointerEnter={action(() => {
+ if (this.targetDoc) {
+ Doc.BrushDoc(this.targetDoc);
+ this.dialogueBoxOpacity = 0.1;
+ this.overlayOpacity = 0.1;
+ }
+ })}
+ onPointerLeave={action(() => {
+ this.targetDoc && Doc.UnBrushDoc(this.targetDoc);
+ this.dialogueBoxOpacity = 1;
+ this.overlayOpacity = 0.4;
+ })}
+ >
+ {contents}
+ </span>
+ );
+ }
+
+ private get sharingInterface() {
+ return (
+ <div className={"sharing-interface"}>
+ <p className={"share-link"}>Manage the public link to {this.focusOn("this document...")}</p>
+ {!this.linkVisible ? (null) :
+ <div className={"link-container"}>
+ <div className={"link-box"} onClick={this.copy}>{this.sharingUrl}</div>
+ <div
+ title={"Copy link to clipboard"}
+ className={"copy"}
+ style={{ backgroundColor: this.copied ? "lawngreen" : "gainsboro" }}
+ onClick={this.copy}
+ >
+ <FontAwesomeIcon icon={fa.faCopy} />
+ </div>
+ </div>
+ }
+ <div className={"people-with-container"}>
+ {!this.linkVisible ? (null) : <p className={"people-with"}>People with this link</p>}
+ <select
+ className={"people-with-select"}
+ value={this.sharingDoc ? StrCast(this.sharingDoc[PublicKey], SharingPermissions.None) : SharingPermissions.None}
+ style={{
+ marginLeft: this.linkVisible ? 10 : 0,
+ color: this.sharingDoc ? ColorMapping.get(StrCast(this.sharingDoc[PublicKey], SharingPermissions.None)) : DefaultColor,
+ borderColor: this.sharingDoc ? ColorMapping.get(StrCast(this.sharingDoc[PublicKey], SharingPermissions.None)) : DefaultColor
+ }}
+ onChange={e => this.setExternalSharing(e.currentTarget.value)}
+ >
+ {this.sharingOptions}
+ </select>
+ </div>
+ <div className={"hr-substitute"} />
+ <p className={"share-individual"}>Privately share {this.focusOn("this document")} with an individual...</p>
+ <div className={"users-list"} style={{ display: this.users.length ? "block" : "flex" }}>
+ {!this.users.length ? "There are no other users in your database." :
+ this.users.map(user => {
+ return (
+ <div
+ key={user.email}
+ className={"container"}
+ >
+ <select
+ className={"permissions-dropdown"}
+ value={this.sharingDoc ? StrCast(this.sharingDoc[user.userDocumentId], SharingPermissions.None) : SharingPermissions.None}
+ style={{
+ color: this.sharingDoc ? ColorMapping.get(StrCast(this.sharingDoc[user.userDocumentId], SharingPermissions.None)) : DefaultColor,
+ borderColor: this.sharingDoc ? ColorMapping.get(StrCast(this.sharingDoc[user.userDocumentId], SharingPermissions.None)) : DefaultColor
+ }}
+ onChange={e => this.setInternalSharing(user, e.currentTarget.value)}
+ >
+ {this.sharingOptions}
+
+ </select>
+ <span className={"padding"}>{user.email}</span>
+ </div>
+ );
+ })
+ }
+ </div>
+ <div className={"close-button"} onClick={this.close}>Done</div>
+ </div>
+ );
+ }
+
+ render() {
+ return (
+ <MainViewModal
+ contents={this.sharingInterface}
+ isDisplayed={this.isOpen}
+ interactive={true}
+ dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity}
+ overlayDisplayedOpacity={this.overlayOpacity}
+ />
+ );
+ }
+
+} \ No newline at end of file