From 716751fa420eda3a3f8322f579255a6532ddc7f5 Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Mon, 27 Jul 2020 18:34:31 +0530
Subject: dropdown filters + comments

---
 src/client/util/GroupManager.tsx | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

(limited to 'src/client/util/GroupManager.tsx')

diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 72fba5c1b..551216fa4 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -20,6 +20,9 @@ import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
 
 library.add(fa.faPlus, fa.faTimes, fa.faInfoCircle);
 
+/**
+ * Interface for options for the react-select component
+ */
 export interface UserOptions {
     label: string;
     value: string;
@@ -30,8 +33,6 @@ export default class GroupManager extends React.Component<{}> {
 
     static Instance: GroupManager;
     @observable isOpen: boolean = false; // whether the GroupManager is to be displayed or not.
-    @observable private dialogueBoxOpacity: number = 1; // opacity of the dialogue box div of the MainViewModal.
-    @observable private overlayOpacity: number = 0.4; // opacity of the overlay div of the MainViewModal.
     @observable private users: string[] = []; // list of users populated from the database.
     @observable private selectedUsers: UserOptions[] | null = null; // list of users selected in the "Select users" dropdown.
     @observable currentGroup: Opt<Doc>; // the currently selected group.
-- 
cgit v1.2.3-70-g09d2


From 626c7b04dcfff293a2a30ed059e661100143199b Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Thu, 30 Jul 2020 22:11:39 +0530
Subject: some playground mode stuff + minor sharingmanager changes

---
 src/client/util/GroupManager.tsx                |  2 +-
 src/client/util/SharingManager.tsx              | 41 ++++++++++++++++---------
 src/client/views/DocComponent.tsx               |  6 ++--
 src/client/views/collections/CollectionView.tsx |  7 +++--
 4 files changed, 35 insertions(+), 21 deletions(-)

(limited to 'src/client/util/GroupManager.tsx')

diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 551216fa4..a0db9a421 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -102,7 +102,7 @@ export default class GroupManager extends React.Component<{}> {
      */
     @action
     open = () => {
-        SelectionManager.DeselectAll();
+        // SelectionManager.DeselectAll();
         this.isOpen = true;
         this.populateUsers();
         this.populateGroups();
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 00d0691f2..7ede584c2 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,7 +1,7 @@
 import { observable, runInAction, action } from "mobx";
 import * as React from "react";
 import MainViewModal from "../views/MainViewModal";
-import { Doc, Opt, DocListCastAsync, AclAdmin, DataSym } from "../../fields/Doc";
+import { Doc, Opt, DocListCastAsync, AclAdmin, DataSym, AclPrivate } from "../../fields/Doc";
 import { DocServer } from "../DocServer";
 import { Cast, StrCast } from "../../fields/Types";
 import * as RequestPromise from "request-promise";
@@ -19,7 +19,7 @@ import GroupMemberView from "./GroupMemberView";
 import Select from "react-select";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { List } from "../../fields/List";
-import { distributeAcls, SharingPermissions } from "../../fields/util";
+import { distributeAcls, SharingPermissions, GetEffectiveAcl } from "../../fields/util";
 import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
 
 export interface User {
@@ -80,7 +80,7 @@ export default class SharingManager extends React.Component<{}> {
 
     public open = (target: DocumentView) => {
         runInAction(() => this.users = []);
-        SelectionManager.DeselectAll();
+        // SelectionManager.DeselectAll();
         this.populateUsers().then(action(() => {
             this.targetDocView = target;
             this.targetDoc = target.props.Document;
@@ -93,8 +93,8 @@ export default class SharingManager extends React.Component<{}> {
 
     public close = action(() => {
         this.isOpen = false;
-        this.users = []; // resets the list of users and seleected users (in the react-select component)
-        this.selectedUsers = null;
+        this.users = [];
+        this.selectedUsers = null; // resets the list of users and seleected users (in the react-select component)
 
         setTimeout(action(() => {
             // this.copied = false;
@@ -108,10 +108,15 @@ export default class SharingManager extends React.Component<{}> {
         SharingManager.Instance = this;
     }
 
+    componentDidMount() {
+        this.populateUsers();
+    }
+
     /**
      * Populates the list of validated users (this.users) by adding registered users which have a rightSidebarCollection.
      */
     populateUsers = async () => {
+        runInAction(() => this.users = []);
         const userList = await RequestPromise.get(Utils.prepend("/getUsers"));
         const raw = JSON.parse(userList) as User[];
         const evaluating = raw.map(async user => {
@@ -379,24 +384,32 @@ export default class SharingManager extends React.Component<{}> {
         const users = this.individualSort === "ascending" ? this.users.sort(this.sortUsers) : this.individualSort === "descending" ? this.users.sort(this.sortUsers).reverse() : this.users;
         const groups = this.groupSort === "ascending" ? groupList.sort(this.sortGroups) : this.groupSort === "descending" ? groupList.sort(this.sortGroups).reverse() : groupList;
 
+        const effectiveAcl = this.targetDoc ? GetEffectiveAcl(this.targetDoc) : AclPrivate;
+
         const userListContents: (JSX.Element | null)[] = users.map(({ user, notificationDoc }) => {
             const userKey = user.email.replace('.', '_');
-            const permissions = StrCast(this.targetDoc?.[`ACL-${userKey}`], SharingPermissions.None);
+            const permissions = StrCast(this.targetDoc?.[`ACL-${userKey}`]);
 
-            return user.email === this.targetDoc?.author ? null : (
+            return !permissions || user.email === this.targetDoc?.author ? null : (
                 <div
                     key={userKey}
                     className={"container"}
                 >
                     <span className={"padding"}>{user.email}</span>
                     <div className="edit-actions">
-                        <select
-                            className={"permissions-dropdown"}
-                            value={permissions}
-                            onChange={e => this.setInternalSharing({ user, notificationDoc }, e.currentTarget.value)}
-                        >
-                            {this.sharingOptions}
-                        </select>
+                        {effectiveAcl === AclAdmin ? (
+                            <select
+                                className={"permissions-dropdown"}
+                                value={permissions}
+                                onChange={e => this.setInternalSharing({ user, notificationDoc }, e.currentTarget.value)}
+                            >
+                                {this.sharingOptions}
+                            </select>
+                        ) : (
+                                <div className={"permissions-dropdown"}>
+                                    {this.sharingOptions}
+                                </div>
+                            )}
                     </div>
                 </div>
             );
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 777fb5323..3bcf4b922 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -163,13 +163,13 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
                     //         }
                     //     });
                     // }
-                    if (effectiveAcl === AclAddonly) {
+                    if (effectiveAcl === AclAddonly && !getPlaygroundMode()) {
                         added.map(doc => Doc.AddDocToList(targetDataDoc, this.annotationKey, doc));
                     }
                     else {
                         added.map(doc => doc.context = this.props.Document);
-                        targetDataDoc[this.annotationKey] = new List<Doc>([...docList, ...added]);
-                        targetDataDoc[this.annotationKey + "-lastModified"] = new DateField(new Date(Date.now()));
+                        (targetDataDoc[this.annotationKey] as List<Doc>).push(...added);
+                        if (!getPlaygroundMode()) targetDataDoc[this.annotationKey + "-lastModified"] = new DateField(new Date(Date.now()));
                     }
                 }
             }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 25a832994..5262c8b46 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -157,7 +157,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                 //     });
                 // }
 
-                if (effectiveAcl === AclAddonly) {
+                if (effectiveAcl === AclAddonly && !getPlaygroundMode()) {
                     added.map(doc => Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc));
                 }
                 else {
@@ -179,8 +179,9 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                         doc.context = this.props.Document;
                     });
                     added.map(add => Doc.AddDocToList(Cast(Doc.UserDoc().myCatalog, Doc, null), "data", add));
-                    targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, ...added]);
-                    targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+                    // targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, ...added]);
+                    (targetDataDoc[this.props.fieldKey] as List<Doc>).push(...added);
+                    if (!getPlaygroundMode()) targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
                 }
             }
         }
-- 
cgit v1.2.3-70-g09d2


From 5d50404b127560d525cab4645fcd3b07367ef5a2 Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Fri, 31 Jul 2020 16:19:46 +0530
Subject: comments

---
 src/client/util/GroupManager.tsx   | 37 +++++++++++++------------------------
 src/client/util/SharingManager.tsx |  8 ++++++--
 src/client/views/MainViewModal.tsx |  2 +-
 src/fields/util.ts                 |  7 +++++--
 4 files changed, 25 insertions(+), 29 deletions(-)

(limited to 'src/client/util/GroupManager.tsx')

diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index a0db9a421..5215ea35f 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -38,8 +38,8 @@ export default class GroupManager extends React.Component<{}> {
     @observable currentGroup: Opt<Doc>; // the currently selected group.
     @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();
-    private currentUserGroups: string[] = [];
+    private createGroupButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // the ref for the group creation button
+    private currentUserGroups: string[] = []; // the list of groups the current user is a member of
     @observable private buttonColour: "#979797" | "black" = "#979797";
     @observable private groupSort: "ascending" | "descending" | "none" = "none";
 
@@ -50,6 +50,9 @@ export default class GroupManager extends React.Component<{}> {
         GroupManager.Instance = this;
     }
 
+    /**
+     * Populates the list of users and groups.
+     */
     componentDidMount() {
         this.populateUsers();
         this.populateGroups();
@@ -63,8 +66,6 @@ export default class GroupManager extends React.Component<{}> {
         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);
@@ -74,11 +75,13 @@ export default class GroupManager extends React.Component<{}> {
                     }
                 });
             }
-            // }
         });
         return Promise.all(evaluating);
     }
 
+    /**
+     * Populates the list of groups the current user is a member of and sets this list to be used in the GetEffectiveAcl in util.ts
+     */
     populateGroups = () => {
         DocListCastAsync(this.GroupManagerDoc?.data).then(groups => {
             groups?.forEach(group => {
@@ -146,25 +149,8 @@ export default class GroupManager extends React.Component<{}> {
     }
 
     /**
-     * @returns a readonly copy of a single group document
+     * Returns an array of the list of members of a given group.
      */
-    getGroupCopy(groupName: string): Doc | undefined {
-        const groupDoc = this.getGroup(groupName);
-        if (groupDoc) {
-            const { members, owners } = groupDoc;
-            return Doc.assign(new Doc, { groupName, members: StrCast(members), owners: StrCast(owners) });
-        }
-        return undefined;
-    }
-    /**
-     * @returns a readonly copy of the list of group documents
-     */
-    getAllGroupsCopy(): Doc[] {
-        return this.getAllGroups().map(({ groupName, owners, members }) =>
-            Doc.assign(new Doc, { groupName: (StrCast(groupName)), owners: (StrCast(owners)), members: (StrCast(members)) })
-        );
-    }
-
     getGroupMembers(group: string | Doc): string[] {
         if (group instanceof Doc) return JSON.parse(StrCast(group.members)) as string[];
         else return JSON.parse(StrCast(this.getGroup(group)!.members)) as string[];
@@ -317,6 +303,9 @@ export default class GroupManager extends React.Component<{}> {
 
     }
 
+    /**
+     * @returns the MainViewModal which allows the user to create groups.
+     */
     private get groupCreationModal() {
         const contents = (
             <div className="group-create">
@@ -416,7 +405,7 @@ export default class GroupManager extends React.Component<{}> {
                     <div
                         className="sort-groups"
                         onClick={action(() => this.groupSort = this.groupSort === "ascending" ? "descending" : this.groupSort === "descending" ? "none" : "ascending")}>
-                        Name {this.groupSort === "ascending" ? "↑" : this.groupSort === "descending" ? "↓" : ""} {/* → */}
+                        Name {this.groupSort === "ascending" ? "↑" : this.groupSort === "descending" ? "↓" : ""}
                     </div>
                     <div className="group-body">
                         {groups.map(group =>
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 4ab4a3c42..64dc491ed 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -81,13 +81,14 @@ export default class SharingManager extends React.Component<{}> {
     public open = (target: DocumentView) => {
         runInAction(() => this.users = []);
         // SelectionManager.DeselectAll();
-        this.populateUsers().then(action(() => {
+        this.populateUsers();
+        runInAction(() => {
             this.targetDocView = target;
             this.targetDoc = target.props.Document;
             DictationOverlay.Instance.hasActiveModal = true;
             this.isOpen = true;
             this.permissions = SharingPermissions.Edit;
-        }));
+        });
 
     }
 
@@ -108,6 +109,9 @@ export default class SharingManager extends React.Component<{}> {
         SharingManager.Instance = this;
     }
 
+    /**
+     * Populates the list of users.
+     */
     componentDidMount() {
         this.populateUsers();
     }
diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx
index 249715511..66ea2dbf8 100644
--- a/src/client/views/MainViewModal.tsx
+++ b/src/client/views/MainViewModal.tsx
@@ -10,7 +10,7 @@ export interface MainViewOverlayProps {
     overlayStyle?: React.CSSProperties;
     dialogueBoxDisplayedOpacity?: number;
     overlayDisplayedOpacity?: number;
-    closeOnExternalClick?: () => void;
+    closeOnExternalClick?: () => void; // the close method of a MainViewModal, triggered if there is a click on the overlay (closing the modal)
 }
 
 @observer
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 500daf0c7..16517f25f 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -184,6 +184,7 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number)
                 }
             }
         }
+        // if we're in playground mode, return AclEdit (or AclAdmin if that's the user's effectiveAcl)
         return playgroundMode && HierarchyMapping.get(effectiveAcl)! < 3 ? AclEdit : effectiveAcl;
     }
     return AclAdmin;
@@ -194,6 +195,7 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number)
  * @param acl the access right being stored (e.g. "Can Edit")
  * @param target the document on which this access right is being set
  * @param inheritingFromCollection whether the target is being assigned rights after being dragged into a collection (and so is inheriting the ACLs from the collection)
+ * inheritingFromCollection is not currently being used but could be used if ACL assignment defaults change
  */
 export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean) {
 
@@ -205,7 +207,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
         ["Admin", 4]
     ]);
 
-    let changed = false;
+    let changed = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym])
     const dataDoc = target[DataSym];
 
     // if it is inheriting from a collection, it only inherits if A) the key doesn't already exist or B) the right being inherited is more restrictive
@@ -241,7 +243,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
         });
     }
 
-    changed && fetchProto(target); // updates aclsym when changes to acls have been made
+    changed && fetchProto(target); // updates target[AclSym] when changes to acls have been made
 }
 
 const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox",
@@ -251,6 +253,7 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
     const effectiveAcl = GetEffectiveAcl(target, in_prop);
     if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin) return true;
 
+    // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't
     if (typeof prop === "string" && prop.startsWith("ACL") && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value))) return true;
     // if (typeof prop === "string" && prop.startsWith("ACL") && !["Can Edit", "Can Add", "Can View", "Not Shared", undefined].includes(value)) return true;
 
-- 
cgit v1.2.3-70-g09d2


From 061a5a676ad9ebd821b2ba17d28fef54d8a773d0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 3 Aug 2020 15:44:39 -0400
Subject: added sharing panel

---
 package.json                         |  2 +-
 src/client/util/CurrentUserUtils.ts  |  6 +++---
 src/client/util/GroupManager.tsx     |  2 +-
 src/client/util/SharingManager.tsx   |  4 ++--
 src/client/views/MainView.tsx        |  9 +++++----
 src/client/views/MainViewNotifs.scss |  2 ++
 src/client/views/MainViewNotifs.tsx  | 29 +++++++++++++++++------------
 src/mobile/AudioUpload.tsx           |  2 +-
 src/mobile/MobileInterface.tsx       |  8 ++++----
 9 files changed, 36 insertions(+), 28 deletions(-)

(limited to 'src/client/util/GroupManager.tsx')

diff --git a/package.json b/package.json
index ddf4c3b24..0a3a63ec3 100644
--- a/package.json
+++ b/package.json
@@ -254,4 +254,4 @@
         "xoauth2": "^1.2.0",
         "xregexp": "^4.3.0"
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index a99d9451b..4b823231e 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -498,11 +498,11 @@ export class CurrentUserUtils {
         title: string, icon: string, click: string,
     }[] {
         return [
+            { title: "Sharing", icon: "users", click: 'scriptContext.selectMenu(self, "Sharing")' },
             { title: "Workspace", icon: "desktop", click: 'scriptContext.selectMenu(self, "Workspace")' },
             { title: "Catalog", icon: "file", click: 'scriptContext.selectMenu(self, "Catalog")' },
             { title: "Archive", icon: "archive", click: 'scriptContext.selectMenu(self, "Archive")' },
             { title: "Import", icon: "upload", click: 'scriptContext.selectMenu(self, "Import")' },
-            { title: "Sharing", icon: "users", click: 'scriptContext.selectMenu(self, "Sharing")' },
             { title: "Tools", icon: "wrench", click: 'scriptContext.selectMenu(self, "Tools")' },
             { title: "Help", icon: "question-circle", click: 'scriptContext.selectMenu(self, "Help")' },
             { title: "Settings", icon: "cog", click: 'scriptContext.selectMenu(self, "Settings")' },
@@ -821,8 +821,8 @@ export class CurrentUserUtils {
 
     // Right sidebar is where mobile uploads are contained
     static setupRightSidebar(doc: Doc) {
-        if (doc.rightSidebarCollection === undefined) {
-            doc.rightSidebarCollection = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Mobile Uploads" }));
+        if (doc["sidebar-sharing"] === undefined) {
+            doc["sidebar-sharing"] = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Mobile Uploads" }));
         }
     }
 
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 5215ea35f..229a846a9 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -68,7 +68,7 @@ export default class GroupManager extends React.Component<{}> {
         const evaluating = raw.map(async user => {
             const userDocument = await DocServer.GetRefField(user.userDocumentId);
             if (userDocument instanceof Doc) {
-                const notificationDoc = await Cast(userDocument.rightSidebarCollection, Doc);
+                const notificationDoc = await Cast(userDocument["sidebar-sharing"], Doc);
                 runInAction(() => {
                     if (notificationDoc instanceof Doc) {
                         this.users.push(user.email);
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 7a0fe4140..9d91ce1ba 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -114,7 +114,7 @@ export default class SharingManager extends React.Component<{}> {
     }
 
     /**
-     * Populates the list of validated users (this.users) by adding registered users which have a rightSidebarCollection.
+     * Populates the list of validated users (this.users) by adding registered users which have a sidebar-sharing.
      */
     populateUsers = async () => {
         runInAction(() => this.users = []);
@@ -125,7 +125,7 @@ export default class SharingManager extends React.Component<{}> {
             if (isCandidate) {
                 const userDocument = await DocServer.GetRefField(user.userDocumentId);
                 if (userDocument instanceof Doc) {
-                    const notificationDoc = await Cast(userDocument.rightSidebarCollection, Doc);
+                    const notificationDoc = await Cast(userDocument["sidebar-sharing"], Doc);
                     runInAction(() => {
                         if (notificationDoc instanceof Doc) {
                             this.users.push({ user, notificationDoc });
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ca5154ef7..66545ea1f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -288,7 +288,7 @@ export class MainView extends React.Component {
         }
         // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized)
         setTimeout(async () => {
-            const col = this.userDoc && await Cast(this.userDoc.rightSidebarCollection, Doc);
+            const col = this.userDoc && await Cast(this.userDoc["sidebar-sharing"], Doc);
             col && Cast(col.data, listSpec(Doc)) && runInAction(() => MainViewNotifs.NotifsCol = col);
         }, 100);
         return true;
@@ -511,7 +511,7 @@ export class MainView extends React.Component {
                 case "Catalog": panelDoc = Doc.UserDoc()["sidebar-catalog"] as Doc ?? undefined; break;
                 case "Archive": panelDoc = Doc.UserDoc()["sidebar-recentlyClosed"] as Doc ?? undefined; break;
                 case "Settings": SettingsManager.Instance.open(); break;
-                case "Sharing": GroupManager.Instance.open(); break;
+                case "Sharing": panelDoc = Doc.UserDoc()["sidebar-sharing"] as Doc ?? undefined; break;
                 case "UserDoc": panelDoc = Doc.UserDoc()["sidebar-userDoc"] as Doc ?? undefined; break;
             }
             this.sidebarContent.proto = panelDoc;
@@ -561,7 +561,8 @@ export class MainView extends React.Component {
                             top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "0"
                         }} />
                         <div className="mainview-libraryHandle-icon">
-                            <FontAwesomeIcon icon="chevron-left" color="black" size="sm" /> </div>
+                            <FontAwesomeIcon icon="chevron-left" color="black" size="sm" />
+                        </div>
                     </div> : null}
                     <div className="mainView-libraryFlyout" style={{
                         //transformOrigin: this._flyoutTranslate ? "" : "left center",
@@ -574,6 +575,7 @@ export class MainView extends React.Component {
                     </div>
                 </div>
                 {this.dockingContent}
+                <MainViewNotifs />
                 {this.showProperties ? (null) :
                     <div className="mainView-propertiesDragger" title="Properties View Dragger" onPointerDown={this.onPropertiesPointerDown}
                         style={{ right: rightFlyout, top: "50%" }}>
@@ -632,7 +634,6 @@ export class MainView extends React.Component {
         if (dockedBtns instanceof Doc) {
             return <div className="mainView-docButtons" ref={this._docBtnRef}
                 style={{ height: !dockedBtns.linearViewIsExpanded ? "42px" : undefined }} >
-                <MainViewNotifs />
                 <CollectionLinearView
                     Document={dockedBtns}
                     DataDoc={undefined}
diff --git a/src/client/views/MainViewNotifs.scss b/src/client/views/MainViewNotifs.scss
index 25ec95643..92d7d6ee3 100644
--- a/src/client/views/MainViewNotifs.scss
+++ b/src/client/views/MainViewNotifs.scss
@@ -1,5 +1,7 @@
 .mainNotifs-container {
     position:absolute;
+    z-index: 1000;
+    top: 12px;
 
     .mainNotifs-badge {
         position: absolute;
diff --git a/src/client/views/MainViewNotifs.tsx b/src/client/views/MainViewNotifs.tsx
index 05f890485..ce47e1cf1 100644
--- a/src/client/views/MainViewNotifs.tsx
+++ b/src/client/views/MainViewNotifs.tsx
@@ -3,28 +3,33 @@ import { observer } from 'mobx-react';
 import "normalize.css";
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
-import { emptyFunction } from '../../Utils';
-import { SetupDrag } from '../util/DragManager';
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../Utils';
+import { SetupDrag, DragManager } from '../util/DragManager';
 import "./MainViewNotifs.scss";
-import { CollectionDockingView } from './collections/CollectionDockingView';
+import { MainView } from './MainView';
 
 
 @observer
 export class MainViewNotifs extends React.Component {
-
     @observable static NotifsCol: Opt<Doc>;
-    openNotifsCol = () => {
-        if (MainViewNotifs.NotifsCol) {
-            CollectionDockingView.AddRightSplit(MainViewNotifs.NotifsCol);
-        }
+    _notifsRef = React.createRef<HTMLDivElement>();
+
+    onPointerDown = (e: React.PointerEvent) => {
+        setupMoveUpEvents(this, e,
+            (e: PointerEvent) => {
+                const dragData = new DragManager.DocumentDragData([MainViewNotifs.NotifsCol!]);
+                DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y);
+                return true;
+            },
+            returnFalse,
+            () => MainViewNotifs.NotifsCol && MainView.Instance.selectMenu(MainViewNotifs.NotifsCol, "Sharing"));
     }
+
     render() {
         const length = MainViewNotifs.NotifsCol ? DocListCast(MainViewNotifs.NotifsCol.data).length : 0;
-        const notifsRef = React.createRef<HTMLDivElement>();
-        const dragNotifs = action(() => MainViewNotifs.NotifsCol!);
-        return <div className="mainNotifs-container" ref={notifsRef}>
+        return <div className="mainNotifs-container" style={{ width: 15, height: 15 }} ref={this._notifsRef}>
             <button className="mainNotifs-badge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}
-                onClick={this.openNotifsCol} onPointerDown={MainViewNotifs.NotifsCol ? SetupDrag(notifsRef, dragNotifs) : emptyFunction}>
+                onPointerDown={this.onPointerDown} >
                 {length}
             </button>
         </div>;
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index b75102e43..8ae504f1b 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -53,7 +53,7 @@ export class AudioUpload extends React.Component {
      * Pushing the audio doc onto Dash Web through the right side bar
      */
     uploadAudio = () => {
-        const audioRightSidebar = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc;
+        const audioRightSidebar = Cast(Doc.UserDoc()["sidebar-sharing"], Doc) as Doc;
         const audioDoc = this._audioCol;
         const data = Cast(audioRightSidebar.data, listSpec(Doc));
         for (let i = 1; i < 8; i++) {
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 3a889b0db..40aa65372 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -436,7 +436,7 @@ export class MobileInterface extends React.Component {
     // DocButton that uses UndoManager and handles the opacity change if CanUndo is true
     @computed get undo() {
         if (this.mainContainer && this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
-            this._activeDoc !== Doc.UserDoc().rightSidebarCollection && this._activeDoc.title !== "WORKSPACES") {
+            this._activeDoc !== Doc.UserDoc()["sidebar-sharing"] && this._activeDoc.title !== "WORKSPACES") {
             return (
                 <div className="docButton"
                     style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanUndo() ? "1" : "0.4", }}
@@ -454,7 +454,7 @@ export class MobileInterface extends React.Component {
     // DocButton that uses UndoManager and handles the opacity change if CanRedo is true
     @computed get redo() {
         if (this.mainContainer && this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
-            this._activeDoc !== Doc.UserDoc().rightSidebarCollection && this._activeDoc.title !== "WORKSPACES") {
+            this._activeDoc !== Doc.UserDoc()["sidebar-sharing"] && this._activeDoc.title !== "WORKSPACES") {
             return (
                 <div className="docButton"
                     style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanRedo() ? "1" : "0.4", }}
@@ -605,7 +605,7 @@ export class MobileInterface extends React.Component {
 
     // Returns the image upload pop up
     @computed get uploadImage() {
-        const doc = !this._homeMenu ? this._activeDoc : Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc;
+        const doc = !this._homeMenu ? this._activeDoc : Cast(Doc.UserDoc()["sidebar-sharing"], Doc) as Doc;
         return <Uploader Document={doc} />;
     }
 
@@ -628,7 +628,7 @@ export class MobileInterface extends React.Component {
      */
     @action
     switchToMobileUploads = () => {
-        const mobileUpload = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc;
+        const mobileUpload = Cast(Doc.UserDoc()["sidebar-sharing"], Doc) as Doc;
         this.switchCurrentView(mobileUpload);
         this._homeMenu = false;
     }
-- 
cgit v1.2.3-70-g09d2

