import { observable, runInAction, action } from "mobx"; import * as React from "react"; import MainViewModal from "../views/MainViewModal"; import { Doc, Opt, DocCastAsync } from "../../fields/Doc"; import { DocServer } from "../DocServer"; import { Cast, StrCast } from "../../fields/Types"; import * as RequestPromise from "request-promise"; import { Utils } from "../../Utils"; import "./SharingManager.scss"; import { Id } from "../../fields/FieldSymbols"; import { observer } from "mobx-react"; 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 { CollectionView } from "../views/collections/CollectionView"; import { DictationOverlay } from "../views/DictationOverlay"; import GroupManager from "./GroupManager"; import GroupMemberView from "./GroupMemberView"; library.add(fa.faCopy); export interface User { email: string; userDocumentId: string; } export enum SharingPermissions { None = "Not Shared", View = "Can View", Add = "Can Add", Edit = "Can Edit" } const ColorMapping = new Map([ [SharingPermissions.None, "red"], [SharingPermissions.View, "maroon"], [SharingPermissions.Add, "blue"], [SharingPermissions.Edit, "green"] ]); const HierarchyMapping = new Map([ [SharingPermissions.None, "0"], [SharingPermissions.View, "1"], [SharingPermissions.Add, "2"], [SharingPermissions.Edit, "3"], ["0", SharingPermissions.None], ["1", SharingPermissions.View], ["2", SharingPermissions.Add], ["3", SharingPermissions.Edit] ]); const SharingKey = "sharingPermissions"; const PublicKey = "publicLinkPermissions"; const DefaultColor = "black"; interface ValidatedUser { user: User; notificationDoc: Doc; } const storage = "data"; @observer export default class SharingManager extends React.Component<{}> { public static Instance: SharingManager; @observable private isOpen = false; @observable private users: ValidatedUser[] = []; @observable private groups: Doc[] = []; @observable private targetDoc: Doc | undefined; @observable private targetDocView: DocumentView | undefined; @observable private copied = false; @observable private dialogueBoxOpacity = 1; @observable private overlayOpacity = 0.4; @observable private groupToView: Opt; 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; DictationOverlay.Instance.hasActiveModal = true; this.isOpen = true; if (!this.sharingDoc) { this.sharingDoc = new Doc; } })); runInAction(() => this.groups = GroupManager.Instance.getAllGroupsCopy()); } public close = action(() => { this.isOpen = false; this.users = []; setTimeout(action(() => { this.copied = false; DictationOverlay.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 () => { const userList = await RequestPromise.get(Utils.prepend("/getUsers")); const raw = JSON.parse(userList) as User[]; const evaluating = raw.map(async user => { const isCandidate = user.email !== Doc.CurrentUserEmail; if (isCandidate) { const userDocument = await DocServer.GetRefField(user.userDocumentId); if (userDocument instanceof Doc) { const notificationDoc = await Cast(userDocument.rightSidebarCollection, Doc); runInAction(() => { if (notificationDoc instanceof Doc) { this.users.push({ user, notificationDoc }); } }); } } }); return Promise.all(evaluating); } setInternalGroupSharing = (group: Doc, permission: string) => { const members: string[] = JSON.parse(StrCast(group.members)); const users: ValidatedUser[] = this.users.filter(user => members.includes(user.user.email)); const sharingDoc = this.sharingDoc!; if (permission === SharingPermissions.None) { const metadata = sharingDoc[StrCast(group.groupName)]; if (metadata) sharingDoc[StrCast(group.groupName)] = undefined; } else { sharingDoc[StrCast(group.groupName)] = permission; } users.forEach(user => { this.setInternalSharing(user, permission, group); }); } setInternalSharing = async (recipient: ValidatedUser, state: string, group: Opt) => { const { user, notificationDoc } = recipient; const target = this.targetDoc!; const manager = this.sharingDoc!; const key = user.userDocumentId; let metadata = await DocCastAsync(manager[key]); const permissions: { [key: string]: number } = metadata?.permissions ? JSON.parse(StrCast(metadata.permissions)) : {}; permissions[StrCast(group ? group.groupName : Doc.CurrentUserEmail)] = parseInt(HierarchyMapping.get(state)!); const max = Math.max(...Object.values(permissions)); // let max = 0; // const keys: string[] = []; // for (const [key, value] of Object.entries(permissions)) { // if (value === max && max !== 0) { // keys.push(key); // } // else if (value > max) { // keys.splice(0, keys.length); // keys.push(key); // max = value; // } // } switch (max) { case 0: if (metadata) { const sharedAlias = (await DocCastAsync(metadata.sharedAlias))!; Doc.RemoveDocFromList(notificationDoc, storage, sharedAlias); manager[key] = undefined; } break; case 1: case 2: case 3: if (!metadata) { metadata = new Doc; const sharedAlias = Doc.MakeAlias(target); Doc.AddDocToList(notificationDoc, storage, sharedAlias); metadata.sharedAlias = sharedAlias; manager[key] = metadata; } metadata.permissions = JSON.stringify(permissions); // metadata.usersShared = JSON.stringify(keys); break; } if (metadata) metadata.maxPermission = HierarchyMapping.get(`${max}`); } private setExternalSharing = (state: string) => { const sharingDoc = this.sharingDoc; if (!sharingDoc) { return; } sharingDoc[PublicKey] = state; } private get sharingUrl() { if (!this.targetDoc) { return undefined; } const 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 ( ); }); } private focusOn = (contents: string) => { const title = this.targetDoc ? StrCast(this.targetDoc.title) : ""; return ( { let context: Opt; if (this.targetDoc && this.targetDocView && (context = this.targetDocView.props.ContainingCollectionView)) { DocumentManager.Instance.jumpToDocument(this.targetDoc, true, 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} ); } private computePermissions = (userKey: string) => { const sharingDoc = this.sharingDoc; if (!sharingDoc) { return SharingPermissions.None; } const metadata = sharingDoc[userKey] as Doc | string; if (!metadata) { return SharingPermissions.None; } return StrCast(metadata instanceof Doc ? metadata.maxPermission : metadata, SharingPermissions.None); } private get sharingInterface() { const existOtherUsers = this.users.length > 0; const existGroups = this.groups.length > 0; // const manager = this.sharingDoc!; return (
{this.groupToView ? this.groupToView = undefined)} /> : null} {/*

Manage the public link to {this.focusOn("this document...")}

{!this.linkVisible ? (null) :
{this.sharingUrl}
}
{!this.linkVisible ? (null) :

People with this link

}
*/}

Privately share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))} with an individual...

{/*200*/} {!existOtherUsers ? "There are no other users in your database." : this.users.map(({ user, notificationDoc }) => { // can't use async here const userKey = user.userDocumentId; const permissions = this.computePermissions(userKey); const color = ColorMapping.get(permissions); // console.log(manager); // const metadata = manager[userKey] as Doc; // const usersShared = StrCast(metadata?.usersShared, ""); // console.log(usersShared) return (
{user.email} {/*
{usersShared}
*/}
); }) }

Privately share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))} with a group...

{/*200*/} {!existGroups ? "There are no groups in your database." : this.groups.map(group => { const permissions = this.computePermissions(StrCast(group.groupName)); const color = ColorMapping.get(permissions); return (
{group.groupName}
); }) }
Done
); } render() { // console.log(this.sharingDoc); return ( ); } }