From df25be868115b378e208fc729b75d606be279164 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 1 Oct 2020 10:15:26 -0400 Subject: changed sharing to use __ to replace .'s in emails to avoid problems with storing keys in mongo --- src/client/util/SharingManager.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index bcd7d4056..984583ed5 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -7,7 +7,7 @@ import * as RequestPromise from "request-promise"; import { AclAdmin, AclPrivate, DataSym, Doc, DocListCast, Opt, AclSym, AclAddonly, AclEdit, AclReadonly } from "../../fields/Doc"; import { List } from "../../fields/List"; import { Cast, StrCast } from "../../fields/Types"; -import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx } from "../../fields/util"; +import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx, normalizeEmail } from "../../fields/util"; import { Utils } from "../../Utils"; import { DocServer } from "../DocServer"; import { CollectionView } from "../views/collections/CollectionView"; @@ -154,13 +154,13 @@ export class SharingManager extends React.Component<{}> { setInternalGroupSharing = (group: Doc | { groupName: string }, permission: string, targetDoc?: Doc) => { const target = targetDoc || this.targetDoc!; - const key = StrCast(group.groupName).replace(".", "_"); + const key = normalizeEmail(StrCast(group.groupName)); const acl = `acl-${key}`; const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document); docs.forEach(doc => { - doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`acl-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, doc); + doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc); distributeAcls(acl, permission as SharingPermissions, doc); if (group instanceof Doc) { @@ -246,13 +246,13 @@ export class SharingManager extends React.Component<{}> { setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc?: Doc) => { const { user, notificationDoc } = recipient; const target = targetDoc || this.targetDoc!; - const key = user.email.replace('.', '_'); + const key = normalizeEmail(user.email); const acl = `acl-${key}`; const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document); docs.forEach(doc => { - doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`acl-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, doc); + doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc); distributeAcls(acl, permission as SharingPermissions, doc); if (permission !== SharingPermissions.None) Doc.IndexOf(doc, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, doc); @@ -456,8 +456,8 @@ export class SharingManager extends React.Component<{}> { 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-${user.email.replace('.', '_')}`) : docs[0]?.author !== user.email).map(({ user, notificationDoc, userColor }) => { - const userKey = `acl-${user.email.replace('.', '_')}`; + const userListContents: (JSX.Element | null)[] = users.filter(({ user }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email).map(({ user, notificationDoc, 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-"; @@ -514,7 +514,7 @@ export class SharingManager extends React.Component<{}> { Me
- {targetDoc?.[`acl-${Doc.CurrentUserEmail.replace(".", "_")}`]} + {targetDoc?.[`acl-${Doc.CurrentUserEmailNormalized}`]}
-- cgit v1.2.3-70-g09d2 From 3c44131f75fe0c311363b36e824604c91f1babc8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 1 Oct 2020 11:15:41 -0400 Subject: fixed closed library flyout view to not render a DocumentView. fixed sidebar panels to not be selectable. fixed changing view of sidebar panel to only affect that panel --- src/client/util/CurrentUserUtils.ts | 14 ++--- src/client/util/SearchUtil.ts | 2 +- src/client/views/MainView.tsx | 69 +++++++++++++------------ src/client/views/collections/CollectionMenu.tsx | 3 +- 4 files changed, 46 insertions(+), 42 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 7cc35d67a..d45a2c90a 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -566,7 +566,7 @@ export class CurrentUserUtils { title: "menuItemPanel", childDropAction: "alias", dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), - _backgroundColor: "black", + _backgroundColor: "black", ignoreClick: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, lockedPosition: true, _chromeStatus: "disabled", system: true @@ -729,7 +729,7 @@ export class CurrentUserUtils { if (doc.myTools === undefined) { const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], { - title: "My Tools", _width: 500, _yMargin: 20, lockedPosition: true, _chromeStatus: "disabled", forceActive: true, system: true, _stayInCollection: true, _hideContextMenu: true, + title: "My Tools", _width: 500, _yMargin: 20, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", forceActive: true, system: true, _stayInCollection: true, _hideContextMenu: true, })) as any as Doc; doc.myTools = toolsStack; @@ -743,7 +743,7 @@ export class CurrentUserUtils { doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "My Dashboards", _height: 400, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, + treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, ignoreClick: true, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true })); const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`); @@ -759,7 +759,7 @@ export class CurrentUserUtils { doc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "My Presentations", _height: 100, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, + treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, ignoreClick: true, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true })); const newPresentations = ScriptField.MakeScript(`createNewPresentation()`); @@ -777,7 +777,7 @@ export class CurrentUserUtils { doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "Recently Closed", _height: 500, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, + treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, ignoreClick: true, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true })); const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`); @@ -792,7 +792,7 @@ export class CurrentUserUtils { doc.myFilter = new PrefetchProxy(Docs.Create.FilterDocument({ title: "FilterDoc", _height: 500, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "none", - treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, + treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, ignoreClick: true, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true })); const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`); @@ -808,7 +808,7 @@ export class CurrentUserUtils { doc.treeViewExpandedView = "fields"; doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], { treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, title: "My UserDoc", - treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, + treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, ignoreClick: true, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true })) as any as Doc; } diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 08ad49dcc..79759a71d 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -44,7 +44,7 @@ export namespace SearchUtil { const header = query.match(/_[atnb]?:/) ? replacedQuery : "DEFAULT:" + replacedQuery; replacedQuery = `{!join from=id to=proto_i}* AND ${header}`; } - console.log("Q: " + replacedQuery + " fq: " + options.fq); + //console.log("Q: " + replacedQuery + " fq: " + options.fq); const gotten = await rp.get(rpquery, { qs: { ...options, q: replacedQuery } }); const result: IdSearchResult = gotten.startsWith("<") ? { ids: [], docs: [], numFound: 0, lines: [] } : JSON.parse(gotten); if (!returnDocs) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 5193c3c02..8e30eba2a 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -312,39 +312,40 @@ export class MainView extends React.Component { } @computed get flyout() { - return
-
- "lightgrey"} - /> -
- {this.docButtons} -
; + return !this._flyoutWidth ?
: +
+
+ "lightgrey"} + /> +
+ {this.docButtons} +
; } @computed get menuPanel() { @@ -394,6 +395,8 @@ export class MainView extends React.Component { SearchBox.Instance._searchFullDB = "My Stuff"; SearchBox.Instance.enter(undefined); break; + case "Help": + break; default: this.expandFlyout(button); } diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 51d1d5559..e9cb57fed 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -234,8 +234,9 @@ export class CollectionViewBaseChrome extends React.Component { + const target = this.document !== Doc.UserDoc().sidebar ? this.document : this.document.proto as Doc; //@ts-ignore - this.document._viewType = e.target.selectedOptions[0].value; + target._viewType = e.target.selectedOptions[0].value; } commandChanged = (e: React.ChangeEvent) => { -- cgit v1.2.3-70-g09d2 From 266590937d3fda5dd96729edaa7c9bfac42370d9 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 1 Oct 2020 20:31:39 -0400 Subject: major performance fix by adding a SharingDocument to the user's DB account so that inquiring the users' UserDoc (and everything it referecens) is no longer necessary. --- src/client/DocServer.ts | 10 ++++++ src/client/util/CurrentUserUtils.ts | 39 +++++++++++++--------- src/client/util/GroupManager.tsx | 12 +++---- src/client/util/SettingsManager.tsx | 4 +-- src/client/util/SharingManager.tsx | 37 +++++++++----------- src/client/views/GlobalKeyHandler.ts | 3 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/fields/Doc.ts | 2 ++ src/mobile/AudioUpload.tsx | 2 +- src/mobile/ImageUpload.tsx | 4 +-- src/mobile/MobileInterface.tsx | 8 ++--- src/server/ApiManagers/UserManager.ts | 14 +++++--- src/server/GarbageCollector.ts | 2 +- src/server/authentication/AuthenticationManager.ts | 3 +- src/server/authentication/DashUserModel.ts | 4 ++- 15 files changed, 82 insertions(+), 64 deletions(-) (limited to 'src/client/util') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index d7dfa4498..9683eab45 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -9,6 +9,7 @@ import { GestureOverlay } from './views/GestureOverlay'; import MobileInkOverlay from '../mobile/MobileInkOverlay'; import { runInAction } from 'mobx'; import { ObjectField } from '../fields/ObjectField'; +import { StrCast } from '../fields/Types'; /** * This class encapsulates the transfer and cross-client synchronization of @@ -25,6 +26,15 @@ import { ObjectField } from '../fields/ObjectField'; */ export namespace DocServer { let _cache: { [id: string]: RefField | Promise> } = {}; + + export function PRINT_CACHE() { + const strings: string[] = []; + Array.from(Object.keys(_cache)).forEach(key => { + const doc = _cache[key]; + if (doc instanceof Doc) strings.push(StrCast(doc.author) + " " + StrCast(doc.title) + " " + StrCast(Doc.GetT(doc, "title", "string", true))); + }); + strings.sort().forEach((str, i) => console.log(i.toString() + " " + str)); + } export let _socket: SocketIOClient.Socket; // this client's distinct GUID created at initialization let GUID: string; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index d45a2c90a..c00ad2334 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -404,7 +404,7 @@ export class CurrentUserUtils { const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { title: "header", version: headerViewVersion, target: doc, _height: 70, _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, cloneFieldFilter: new List(["system"]) }, "header"); // text needs to be a space to allow templateText to be created headerTemplate[DataSym].layout = "
" + - " " + + " " + " " + "
"; (headerTemplate.proto as Doc).isTemplateDoc = makeTemplate(headerTemplate.proto as Doc, true, "headerView"); @@ -513,10 +513,7 @@ export class CurrentUserUtils { return doc.myItemCreators as Doc; } - static menuBtnDescriptions(doc: Doc): { - title: string, target: Doc, icon: string, click: string, watchedDocuments?: Doc - }[] { - this.setupSharingSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing + static async menuBtnDescriptions(doc: Doc) { return [ { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' }, { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' }, @@ -540,9 +537,10 @@ export class CurrentUserUtils { })) as any as Doc; } } - static setupMenuPanel(doc: Doc) { + static async setupMenuPanel(doc: Doc, sharingDocumentId: string) { if (doc.menuStack === undefined) { - const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, watchedDocuments }) => + await this.setupSharingSidebar(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing + const menuBtns = (await CurrentUserUtils.menuBtnDescriptions(doc)).map(({ title, target, icon, click, watchedDocuments }) => Docs.Create.FontIconDocument({ icon, iconShape: "square", @@ -874,9 +872,18 @@ export class CurrentUserUtils { } // Sharing sidebar is where shared documents are contained - static setupSharingSidebar(doc: Doc) { + static async setupSharingSidebar(doc: Doc, sharingDocumentId: string) { if (doc.mySharedDocs === undefined) { - doc.mySharedDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, _showTitle: "title", ignoreClick: true, lockedPosition: true })); + let sharedDocs = await DocServer.GetRefField(sharingDocumentId); + if (!sharedDocs) { + sharedDocs = Docs.Create.StackingDocument([], { + title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, _showTitle: "title", ignoreClick: true, lockedPosition: true + }, sharingDocumentId); + } + if (sharedDocs instanceof Doc) { + sharedDocs.userColor = sharedDocs.userColor || "#12121233"; + } + doc.mySharedDocs = new PrefetchProxy(sharedDocs); } } @@ -943,11 +950,10 @@ export class CurrentUserUtils { return doc.clickFuncs as Doc; } - static async updateUserDocument(doc: Doc) { + static async updateUserDocument(doc: Doc, sharingDocumentId: string) { doc.system = true; doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode; doc.title = Doc.CurrentUserEmail; - doc.userColor = doc.userColor || "#12121233"; doc._raiseWhenDragged = true; doc.activeInkPen = doc; doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)"); @@ -976,7 +982,7 @@ export class CurrentUserUtils { this.setupOverlays(doc); // documents in overlay layer this.setupDockedButtons(doc); // the bottom bar of font icons await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels - this.setupMenuPanel(doc); + await this.setupMenuPanel(doc, sharingDocumentId); doc.globalLinkDatabase = Docs.Prototypes.MainLinkDocument(); doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument(); doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument(); @@ -1013,10 +1019,11 @@ export class CurrentUserUtils { public static async loadUserDocument({ id, email }: { id: string, email: string }) { this.curr_id = id; Doc.CurrentUserEmail = email; - await rp.get(Utils.prepend("/getUserDocumentId")).then(id => { - if (id && id !== "guest") { - return DocServer.GetRefField(id).then(async field => - Doc.SetUserDoc(await this.updateUserDocument(field instanceof Doc ? field : new Doc(id, true)))); + await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => { + const { userDocumentId, sharingDocumentId } = JSON.parse(ids) as any; + if (userDocumentId !== "guest") { + return DocServer.GetRefField(userDocumentId).then(async field => + Doc.SetUserDoc(await this.updateUserDocument(field instanceof Doc ? field : new Doc(userDocumentId, true), sharingDocumentId))); } else { throw new Error("There should be a user id! Why does Dash think there isn't one?"); } diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index cb512bca8..70ea48ab8 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -64,14 +64,10 @@ export 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 userDocument = await DocServer.GetRefField(user.userDocumentId); - if (userDocument instanceof Doc) { - const notificationDoc = await Cast(userDocument.mySharedDocs, Doc); - runInAction(() => { - if (notificationDoc instanceof Doc) { - this.users.push(user.email); - } - }); + const userSharingDocument = await DocServer.GetRefField(user.sharingDocumentId); + if (userSharingDocument instanceof Doc) { + const notificationDoc = await Cast(userSharingDocument.mySharedDocs, Doc, null); + runInAction(() => notificationDoc && this.users.push(user.email)); } }); return Promise.all(evaluating).then(() => this.populating = false); diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index cd01fea5a..9934f26d3 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -56,7 +56,7 @@ export class SettingsManager extends React.Component<{}> { @undoBatch changeFontFamily = action((e: React.ChangeEvent) => Doc.UserDoc().fontFamily = (e.currentTarget as any).value); @undoBatch changeFontSize = action((e: React.ChangeEvent) => Doc.UserDoc().fontSize = (e.currentTarget as any).value); @undoBatch switchActiveBackgroundColor = action((color: ColorState) => Doc.UserDoc().activeCollectionBackground = String(color.hex)); - @undoBatch switchUserColor = action((color: ColorState) => Doc.UserDoc().userColor = String(color.hex)); + @undoBatch switchUserColor = action((color: ColorState) => Doc.SharingDoc().userColor = String(color.hex)); @undoBatch playgroundModeToggle = action(() => { this.playgroundMode = !this.playgroundMode; @@ -205,6 +205,6 @@ export class SettingsManager extends React.Component<{}> { isDisplayed={this.isOpen} interactive={true} closeOnExternalClick={this.close} - dialogueBoxStyle={{ width: "600px", background: Cast(Doc.UserDoc().userColor, "string", null) }} />; + dialogueBoxStyle={{ width: "600px", background: Cast(Doc.SharingDoc().userColor, "string", null) }} />; } } \ No newline at end of file diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 984583ed5..0cf6cc87c 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -25,7 +25,7 @@ import { SearchBox } from "../views/search/SearchBox"; export interface User { email: string; - userDocumentId: string; + sharingDocumentId: string; } /** @@ -50,12 +50,11 @@ const storage = "data"; * A user who also has a notificationDoc. */ interface ValidatedUser { - user: User; - notificationDoc: Doc; - userColor: string; + user: User; // database minimal info to identify / communicate with a user (email, sharing doc id) + sharingDoc: Doc; // document to share/message another user + userColor: string; // stored on the sharinDoc, extracted for convenience? } - @observer export class SharingManager extends React.Component<{}> { public static Instance: SharingManager; @@ -119,7 +118,7 @@ export class SharingManager extends React.Component<{}> { } /** - * Populates the list of validated users (this.users) by adding registered users which have a mySharedDocs. + * Populates the list of validated users (this.users) by adding registered users which have a sharingDocument. */ populateUsers = async () => { if (!this.populating) { @@ -130,15 +129,9 @@ export class SharingManager extends React.Component<{}> { 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.mySharedDocs, Doc); - const userColor = StrCast(userDocument.userColor); - runInAction(() => { - if (notificationDoc instanceof Doc) { - this.users.push({ user, notificationDoc, userColor }); - } - }); + const userSharingDoc = await DocServer.GetRefField(user.sharingDocumentId); + if (userSharingDoc instanceof Doc) { + runInAction(() => this.users.push({ user, sharingDoc: userSharingDoc, userColor: StrCast(userSharingDoc.userColor) })); } } }); @@ -170,7 +163,7 @@ export class SharingManager extends React.Component<{}> { // 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).push(doc) : group.docsShared = new List([doc]); - users.forEach(({ user, notificationDoc }) => { + users.forEach(({ user, sharingDoc: notificationDoc }) => { if (permission !== SharingPermissions.None) Doc.IndexOf(doc, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, doc); // add the doc to the notificationDoc if it hasn't already been added else GetEffectiveAcl(doc, undefined, user.email) === AclPrivate && Doc.IndexOf((doc.aliasOf as Doc || doc), DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, (doc.aliasOf as Doc || doc)); // remove the doc from the list if it already exists }); @@ -185,7 +178,7 @@ export class SharingManager extends React.Component<{}> { */ shareWithAddedMember = (group: Doc, emailId: string) => { const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!; - if (group.docsShared) DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc)); + if (group.docsShared) DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.sharingDoc[storage])) === -1 && Doc.AddDocToList(user.sharingDoc, storage, doc)); } /** @@ -216,7 +209,7 @@ export class SharingManager extends React.Component<{}> { if (group.docsShared) { DocListCast(group.docsShared).forEach(doc => { - Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(user.notificationDoc, storage, doc); // remove the doc only if it is in the list + Doc.IndexOf(doc, DocListCast(user.sharingDoc[storage])) !== -1 && Doc.RemoveDocFromList(user.sharingDoc, storage, doc); // remove the doc only if it is in the list }); } } @@ -235,7 +228,7 @@ export class SharingManager extends React.Component<{}> { const members: string[] = JSON.parse(StrCast(group.members)); const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); - users.forEach(({ notificationDoc }) => Doc.RemoveDocFromList(notificationDoc, storage, doc)); + users.forEach(({ sharingDoc: notificationDoc }) => Doc.RemoveDocFromList(notificationDoc, storage, doc)); }); } } @@ -244,7 +237,7 @@ export class SharingManager extends React.Component<{}> { * Shares the document with a user. */ setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc?: Doc) => { - const { user, notificationDoc } = recipient; + const { user, sharingDoc: notificationDoc } = recipient; const target = targetDoc || this.targetDoc!; const key = normalizeEmail(user.email); const acl = `acl-${key}`; @@ -456,7 +449,7 @@ export class SharingManager extends React.Component<{}> { 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, notificationDoc, userColor }) => { + const userListContents: (JSX.Element | null)[] = users.filter(({ user }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email).map(({ user, sharingDoc: notificationDoc, 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-"; @@ -472,7 +465,7 @@ export class SharingManager extends React.Component<{}> { diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 1b2395423..2ea1c464f 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -55,7 +55,8 @@ export class KeyManager { }); public handle = action(async (e: KeyboardEvent) => { - if (e.key.toLowerCase() === "shift") KeyManager.Instance.ShiftPressed = true; + if (e.key.toLowerCase() === "shift" && e.ctrlKey && e.altKey) KeyManager.Instance.ShiftPressed = true; + if (!Doc.UserDoc().noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.PRINT_CACHE(); const keyname = e.key && e.key.toLowerCase(); this.handleGreedy(keyname); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5f99f27b1..ddcf7f6f4 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1049,7 +1049,7 @@ export class DocumentView extends DocComponent(Docu const titleView = (!this.ShowTitle ? (null) :
users.user.email === this.dataDoc.author)?.userColor || (this.rootDoc.type === DocumentType.RTF ? StrCast(Doc.UserDoc().userColor) : "rgba(0,0,0,0.4)"), + background: SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.userColor || (this.rootDoc.type === DocumentType.RTF ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)"), pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : undefined, }}> manager._searchQuery = query); } export function UserDoc(): Doc { return manager._user_doc; } + export function SharingDoc(): Doc { return Cast(Doc.UserDoc().mySharedDocs, Doc, null); } export function SetSelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; } export function GetSelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; } diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx index 36663c85f..ebc8bc8a7 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().mySharedDocs, Doc) as Doc; + const audioRightSidebar = Cast(Doc.SharingDoc(), Doc, null); const audioDoc = this._audioCol; const data = Cast(audioRightSidebar.data, listSpec(Doc)); for (let i = 1; i < 8; i++) { diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index 1ee4c7815..65f9e40ff 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -63,11 +63,11 @@ export class Uploader extends React.Component { doc = Docs.Create.ImageDocument(path, { _nativeWidth: defaultNativeImageDim, _width: 400, title: name }); } this.setOpacity(4, "1"); // Slab 4 - const res = await rp.get(Utils.prepend("/getUserDocumentId")); + const res = await rp.get(Utils.prepend("/getUserDocumentIds")); if (!res) { throw new Error("No user id returned"); } - const field = await DocServer.GetRefField(res); + const field = await DocServer.GetRefField(JSON.parse(res).userDocumentId); let pending: Opt; if (field instanceof Doc) { pending = col; diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 72914e62b..a42d85b56 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -440,7 +440,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().mySharedDocs && this._activeDoc.title !== "WORKSPACES") { + this._activeDoc !== Doc.SharingDoc() && this._activeDoc.title !== "WORKSPACES") { return (
; } @@ -632,7 +632,7 @@ export class MobileInterface extends React.Component { */ @action switchToMobileUploads = () => { - const mobileUpload = Cast(Doc.UserDoc().mySharedDocs, Doc) as Doc; + const mobileUpload = Cast(Doc.SharingDoc(), Doc) as Doc; this.switchCurrentView(mobileUpload); this._homeMenu = false; } diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts index 0d1d8f218..c9ffaff4c 100644 --- a/src/server/ApiManagers/UserManager.ts +++ b/src/server/ApiManagers/UserManager.ts @@ -19,16 +19,22 @@ export default class UserManager extends ApiManager { method: Method.GET, subscription: "/getUsers", secureHandler: async ({ res }) => { - const cursor = await Database.Instance.query({}, { email: 1, userDocumentId: 1 }, "users"); + const cursor = await Database.Instance.query({}, { email: 1, sharingDocumentId: 1 }, "users"); const results = await cursor.toArray(); - res.send(results.map(user => ({ email: user.email, userDocumentId: user.userDocumentId }))); + res.send(results.map(user => ({ email: user.email, sharingDocumentId: user.sharingDocumentId }))); } }); register({ method: Method.GET, - subscription: "/getUserDocumentId", - secureHandler: ({ res, user }) => res.send(user.userDocumentId) + subscription: "/getUserDocumentIds", + secureHandler: ({ res, user }) => res.send({ userDocumentId: user.userDocumentId, sharingDocumentId: user.sharingDocumentId }) + }); + + register({ + method: Method.GET, + subscription: "/getSharingDocumentId", + secureHandler: ({ res, user }) => res.send(user.sharingDocumentId) }); register({ diff --git a/src/server/GarbageCollector.ts b/src/server/GarbageCollector.ts index a9a3b0481..6bd0e5163 100644 --- a/src/server/GarbageCollector.ts +++ b/src/server/GarbageCollector.ts @@ -65,7 +65,7 @@ async function GarbageCollect(full: boolean = true) { // await new Promise(res => setTimeout(res, 3000)); const cursor = await Database.Instance.query({}, { userDocumentId: 1 }, 'users'); const users = await cursor.toArray(); - const ids: string[] = users.map(user => user.userDocumentId); + const ids: string[] = [...users.map(user => user.userDocumentId), ...users.map(user => user.sharingDocumentId)]]; const visited = new Set(); const files: { [name: string]: string[] } = {}; diff --git a/src/server/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts index 00f1fe44e..36363e3cf 100644 --- a/src/server/authentication/AuthenticationManager.ts +++ b/src/server/authentication/AuthenticationManager.ts @@ -47,7 +47,8 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => { const model = { email, password, - userDocumentId: Utils.GenerateGuid() + userDocumentId: Utils.GenerateGuid(), + sharingDocumentId: Utils.GenerateGuid() } as Partial; const user = new User(model); diff --git a/src/server/authentication/DashUserModel.ts b/src/server/authentication/DashUserModel.ts index 51d920a8f..0bdc25644 100644 --- a/src/server/authentication/DashUserModel.ts +++ b/src/server/authentication/DashUserModel.ts @@ -10,6 +10,7 @@ export type DashUserModel = mongoose.Document & { passwordResetExpires?: Date, userDocumentId: string; + sharingDocumentId: string; profile: { name: string, @@ -35,7 +36,8 @@ const userSchema = new mongoose.Schema({ passwordResetToken: String, passwordResetExpires: Date, - userDocumentId: String, + userDocumentId: String, // id that identifies a document which hosts all of a user's account data + sharingDocumentId: String, // id that identifies a document that stores documents shared to a user, their user color, and any additional info needed to communicate between users facebook: String, twitter: String, -- cgit v1.2.3-70-g09d2 From ae10a9c7e6188236023f790c1ff6abb0e211311e Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 1 Oct 2020 20:49:31 -0400 Subject: from last --- src/client/util/CurrentUserUtils.ts | 2 +- src/fields/Doc.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c00ad2334..add595cd1 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1023,7 +1023,7 @@ export class CurrentUserUtils { const { userDocumentId, sharingDocumentId } = JSON.parse(ids) as any; if (userDocumentId !== "guest") { return DocServer.GetRefField(userDocumentId).then(async field => - Doc.SetUserDoc(await this.updateUserDocument(field instanceof Doc ? field : new Doc(userDocumentId, true), sharingDocumentId))); + await this.updateUserDocument(Doc.SetUserDoc(field instanceof Doc ? field : new Doc(userDocumentId, true)), sharingDocumentId)); } else { throw new Error("There should be a user id! Why does Dash think there isn't one?"); } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index d9c641973..a8a5ba9bd 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -888,7 +888,7 @@ export namespace Doc { export function SetSelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; } export function GetSelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; } - export function SetUserDoc(doc: Doc) { manager._user_doc = doc; } + export function SetUserDoc(doc: Doc) { return (manager._user_doc = doc); } export function IsSearchMatch(doc: Doc) { return computedFn(function IsSearchMatch(doc: Doc) { -- cgit v1.2.3-70-g09d2 From e5a889f5fb285d70ea75d07d889aeb8c45b4c393 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 1 Oct 2020 20:51:40 -0400 Subject: fixed warnings --- src/client/util/CurrentUserUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index add595cd1..cff07d48e 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1020,10 +1020,10 @@ export class CurrentUserUtils { this.curr_id = id; Doc.CurrentUserEmail = email; await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => { - const { userDocumentId, sharingDocumentId } = JSON.parse(ids) as any; + const { userDocumentId, sharingDocumentId } = JSON.parse(ids); if (userDocumentId !== "guest") { return DocServer.GetRefField(userDocumentId).then(async field => - await this.updateUserDocument(Doc.SetUserDoc(field instanceof Doc ? field : new Doc(userDocumentId, true)), sharingDocumentId)); + this.updateUserDocument(Doc.SetUserDoc(field instanceof Doc ? field : new Doc(userDocumentId, true)), sharingDocumentId)); } else { throw new Error("There should be a user id! Why does Dash think there isn't one?"); } -- cgit v1.2.3-70-g09d2 From 3c7f85dbcddca0ffacd82c2d39e88bba8c4b3f3a Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 1 Oct 2020 23:04:31 -0400 Subject: various fixes to new sharing / email normalization --- src/client/documents/Documents.ts | 12 ++++----- src/client/util/CurrentUserUtils.ts | 9 ++++--- src/client/util/GroupManager.tsx | 2 +- src/client/util/SharingManager.tsx | 31 +++++++++++----------- src/client/views/DocComponent.tsx | 4 +-- src/client/views/PropertiesView.tsx | 11 ++------ src/client/views/collections/CollectionMenu.tsx | 4 +-- src/client/views/collections/CollectionView.tsx | 4 +-- .../views/nodes/formattedText/RichTextRules.ts | 8 +++--- 9 files changed, 40 insertions(+), 45 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 47fa7067d..7ee8267f8 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -574,7 +574,7 @@ export namespace Docs { * only when creating a DockDocument from the current user's already existing * main document. */ - export function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data") { + export function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data", protoId?: string) { const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys, "^_"); protoProps.system = delegateProps.system; @@ -590,7 +590,7 @@ export namespace Docs { protoProps.isPrototype = true; - const dataDoc = MakeDataDelegate(proto, protoProps, data, fieldKey); + const dataDoc = MakeDataDelegate(proto, protoProps, data, fieldKey, protoId); const viewDoc = Doc.MakeDelegate(dataDoc, delegId); // so that the list of annotations is already initialised, prevents issues in addonly. @@ -620,8 +620,8 @@ export namespace Docs { * @param options initial values to apply to this new delegate * @param value the data to store in this new delegate */ - function MakeDataDelegate(proto: Doc, options: DocumentOptions, value?: D, fieldKey: string = "data") { - const deleg = Doc.MakeDelegate(proto); + function MakeDataDelegate(proto: Doc, options: DocumentOptions, value?: D, fieldKey: string = "data", id: string | undefined = undefined) { + const deleg = Doc.MakeDelegate(proto, id); if (value !== undefined) { deleg[fieldKey] = value; } @@ -807,8 +807,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", dontRegisterChildViews: true, ...options, _viewType: CollectionViewType.Tree }, id); } - export function StackingDocument(documents: Array, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Stacking }, id); + export function StackingDocument(documents: Array, options: DocumentOptions, id?: string, protoId?: string) { + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Stacking }, id, undefined, protoId); } export function MulticolumnDocument(documents: Array, options: DocumentOptions) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index cff07d48e..ec550c15a 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -30,6 +30,7 @@ import { Scripting } from "./Scripting"; import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; import { UndoManager } from "./UndoManager"; +import { SharingPermissions } from "../../fields/util"; const headerViewVersion = "0.1"; @@ -874,11 +875,13 @@ export class CurrentUserUtils { // Sharing sidebar is where shared documents are contained static async setupSharingSidebar(doc: Doc, sharingDocumentId: string) { if (doc.mySharedDocs === undefined) { - let sharedDocs = await DocServer.GetRefField(sharingDocumentId); + let sharedDocs = await DocServer.GetRefField(sharingDocumentId + "outer"); if (!sharedDocs) { sharedDocs = Docs.Create.StackingDocument([], { - title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, _showTitle: "title", ignoreClick: true, lockedPosition: true - }, sharingDocumentId); + title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, + _showTitle: "title", ignoreClick: true, lockedPosition: true, + }, sharingDocumentId + "outer", sharingDocumentId); + (sharedDocs as Doc)["acl-Public"] = Doc.GetProto(sharedDocs as Doc)["acl-Public"] = SharingPermissions.Add; } if (sharedDocs instanceof Doc) { sharedDocs.userColor = sharedDocs.userColor || "#12121233"; diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 70ea48ab8..e81c95d83 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -66,7 +66,7 @@ export class GroupManager extends React.Component<{}> { const evaluating = raw.map(async user => { const userSharingDocument = await DocServer.GetRefField(user.sharingDocumentId); if (userSharingDocument instanceof Doc) { - const notificationDoc = await Cast(userSharingDocument.mySharedDocs, Doc, null); + const notificationDoc = await Cast(userSharingDocument.data, Doc, null); runInAction(() => notificationDoc && this.users.push(user.email)); } }); diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 0cf6cc87c..c67ec2db5 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -47,7 +47,7 @@ const groupType = "!groupType/"; const storage = "data"; /** - * A user who also has a notificationDoc. + * 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) @@ -59,7 +59,7 @@ interface ValidatedUser { export class SharingManager extends React.Component<{}> { public static Instance: SharingManager; @observable private isOpen = false; // whether the SharingManager modal is open or not - @observable public users: ValidatedUser[] = []; // the list of users with notificationDocs + @observable public users: ValidatedUser[] = []; // the list of users with sharing docs @observable private targetDoc: Doc | undefined; // the document being shared @observable private targetDocView: DocumentView | undefined; // the DocumentView of the document being shared // @observable private copied = false; @@ -163,9 +163,9 @@ export class SharingManager extends React.Component<{}> { // 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).push(doc) : group.docsShared = new List([doc]); - users.forEach(({ user, sharingDoc: notificationDoc }) => { - if (permission !== SharingPermissions.None) Doc.IndexOf(doc, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, doc); // add the doc to the notificationDoc if it hasn't already been added - else GetEffectiveAcl(doc, undefined, user.email) === AclPrivate && Doc.IndexOf((doc.aliasOf as Doc || doc), DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, (doc.aliasOf as Doc || doc)); // remove the doc from the list if it already exists + users.forEach(({ user, sharingDoc }) => { + if (permission !== SharingPermissions.None) Doc.IndexOf(doc, DocListCast(sharingDoc[storage])) === -1 && Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added + else GetEffectiveAcl(doc, undefined, user.email) === AclPrivate && Doc.IndexOf((doc.aliasOf as Doc || doc), DocListCast(sharingDoc[storage])) !== -1 && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc || doc)); // remove the doc from the list if it already exists }); } }); @@ -228,7 +228,7 @@ export class SharingManager extends React.Component<{}> { const members: string[] = JSON.parse(StrCast(group.members)); const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); - users.forEach(({ sharingDoc: notificationDoc }) => Doc.RemoveDocFromList(notificationDoc, storage, doc)); + users.forEach(({ sharingDoc }) => Doc.RemoveDocFromList(sharingDoc, storage, doc)); }); } } @@ -237,19 +237,18 @@ export class SharingManager extends React.Component<{}> { * Shares the document with a user. */ setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc?: Doc) => { - const { user, sharingDoc: notificationDoc } = recipient; + const { user, sharingDoc } = recipient; const target = targetDoc || this.targetDoc!; - const key = normalizeEmail(user.email); - const acl = `acl-${key}`; + const acl = `acl-${normalizeEmail(user.email)}`; + const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`; const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document); - docs.forEach(doc => { - doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc); + doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc); distributeAcls(acl, permission as SharingPermissions, doc); - if (permission !== SharingPermissions.None) Doc.IndexOf(doc, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, doc); - else GetEffectiveAcl(doc, undefined, user.email) === AclPrivate && Doc.IndexOf((doc.aliasOf as Doc || doc), DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, (doc.aliasOf as Doc || doc)); + if (permission !== SharingPermissions.None) Doc.IndexOf(doc, DocListCast(sharingDoc[storage])) === -1 && Doc.AddDocToList(sharingDoc, storage, doc); + else GetEffectiveAcl(doc, undefined, user.email) === AclPrivate && Doc.IndexOf((doc.aliasOf as Doc || doc), DocListCast(sharingDoc[storage])) !== -1 && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc || doc)); }); } @@ -449,7 +448,7 @@ export class SharingManager extends React.Component<{}> { 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, sharingDoc: notificationDoc, userColor }) => { + const userListContents: (JSX.Element | null)[] = users.filter(({ user }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email).map(({ user, 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-"; @@ -465,7 +464,7 @@ export class SharingManager extends React.Component<{}> { @@ -516,7 +515,7 @@ export class SharingManager extends React.Component<{}> { // the list of groups shared with - const groupListMap: (Doc | { groupName: string })[] = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`acl-${StrCast(groupName).replace('.', '_')}`) : true); + const groupListMap: (Doc | { groupName: string })[] = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(groupName))}`) : true); groupListMap.unshift({ groupName: "Public" }, { groupName: "Override" }); const groupListContents = groupListMap.map(group => { const groupKey = `acl-${StrCast(group.groupName)}`; diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 893b74d75..b3fbe418b 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -7,7 +7,7 @@ import { InteractionUtils } from '../util/InteractionUtils'; import { List } from '../../fields/List'; import { DateField } from '../../fields/DateField'; import { ScriptField } from '../../fields/ScriptField'; -import { GetEffectiveAcl, SharingPermissions, distributeAcls } from '../../fields/util'; +import { GetEffectiveAcl, SharingPermissions, distributeAcls, denormalizeEmail } from '../../fields/util'; /// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView) @@ -166,7 +166,7 @@ export function ViewBoxAnnotatableComponent

{ for (const [key, value] of Object.entries(this.props.Document[AclSym])) { - if (d.author === key.substring(4).replace("_", ".") && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true); + if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true); //else if (this.props.Document[key] === SharingPermissions.Admin) distributeAcls(key, SharingPermissions.Add, d, true); // else distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true); } diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index b30691ff5..f3241e8d9 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -10,7 +10,7 @@ import { Id } from "../../fields/FieldSymbols"; import { InkField } from "../../fields/InkField"; import { ComputedField } from "../../fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../fields/Types"; -import { GetEffectiveAcl, SharingPermissions } from "../../fields/util"; +import { GetEffectiveAcl, SharingPermissions, denormalizeEmail } from "../../fields/util"; import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne } from "../../Utils"; import { DocumentType } from "../documents/DocumentTypes"; import { DocumentManager } from "../util/DocumentManager"; @@ -409,7 +409,7 @@ export class PropertiesView extends React.Component { // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => { if (commonKeys.length) { for (const key of commonKeys) { - const name = key.substring(4).replace("_", "."); + const name = denormalizeEmail(key.substring(4)); const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key] : doc?.[DataSym]?.[AclSym]?.[key] === docs[0]?.[DataSym]?.[AclSym]?.[key]); if (name !== Doc.CurrentUserEmail && name !== target.author && name !== "Public" && name !== "Override"/* && sidebarUsersDisplayed![name] !== false*/) { tableEntries.push(this.sharingItem(name, showAdmin, uniform ? AclMap.get(this.layoutDocAcls ? target[AclSym][key] : target[DataSym][AclSym][key])! : "-multiple-")); @@ -417,13 +417,6 @@ export class PropertiesView extends React.Component { } } - // if (Doc.UserDoc().sidebarUsersDisplayed) { - // for (const [name, value] of Object.entries(sidebarUsersDisplayed!)) { - // if (value === true && !this.selectedDoc![`acl-${name.substring(8).replace(".", "_")}`]) tableEntries.push(this.sharingItem(name.substring(8), effectiveAcl, SharingPermissions.None)); - // } - // } - // }) - const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author); // shifts the current user, owner, public to the top of the doc. tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-")); diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index e9cb57fed..09ff3bb0c 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -894,14 +894,14 @@ export class CollectionStackingViewChrome extends React.Component key.indexOf("title") >= 0 || key.indexOf("author") >= 0 || key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 || - (key[0].toUpperCase() === key[0] && key.substring(0, 3) !== "acl" && key[0] !== "_")); + (key[0].toUpperCase() === key[0] && key[0] !== "_")); return keys.filter(key => key.toLowerCase().indexOf(val) > -1); } else { const keys = new Set(); docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key))); const noviceKeys = Array.from(keys).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 || key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 || - (key[0]?.toUpperCase() === key[0] && key.substring(0, 3) !== "acl" && key[0] !== "_")); + (key[0]?.toUpperCase() === key[0] && key[0] !== "_")); return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1); } } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 522f46280..2bdc8e2f3 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -10,7 +10,7 @@ import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; -import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; +import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx, normalizeEmail, denormalizeEmail } from '../../../fields/util'; import { returnFalse, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; @@ -143,7 +143,7 @@ export class CollectionView extends Touchable { for (const [key, value] of Object.entries(this.props.Document[AclSym])) { - if (d.author === key.substring(4).replace("_", ".") && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true); + if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true); //else if (this.props.Document[key] === SharingPermissions.Admin) distributeAcls(key, SharingPermissions.Add, d, true); //else distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true); } diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 921c0e128..72b91c955 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -3,7 +3,8 @@ import { NodeSelection, TextSelection } from "prosemirror-state"; import { DataSym, Doc } from "../../../../fields/Doc"; import { Id } from "../../../../fields/FieldSymbols"; import { ComputedField } from "../../../../fields/ScriptField"; -import { Cast, NumCast, StrCast } from "../../../../fields/Types"; +import { NumCast, StrCast } from "../../../../fields/Types"; +import { normalizeEmail } from "../../../../fields/util"; import { returnFalse, Utils } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; import { Docs, DocUtils } from "../../../documents/Documents"; @@ -11,7 +12,6 @@ import { FormattedTextBox } from "./FormattedTextBox"; import { wrappingInputRule } from "./prosemirrorPatches"; import { RichTextMenu } from "./RichTextMenu"; import { schema } from "./schema_rts"; -import { List } from "../../../../fields/List"; export class RichTextRules { public Document: Doc; @@ -271,7 +271,7 @@ export class RichTextRules { (state, match, start, end) => { const fieldKey = match[1]; const rawdocid = match[3]?.substring(1); - const docid = rawdocid ? (!rawdocid.includes("@") ? Doc.CurrentUserEmail + "@" + rawdocid : rawdocid).replace(".", "_") : undefined; + const docid = rawdocid ? (!rawdocid.includes("@") ? normalizeEmail(Doc.CurrentUserEmail) + "@" + rawdocid : rawdocid) : undefined; const value = match[2]?.substring(1); if (!fieldKey) { const linkId = Utils.GenerateGuid(); @@ -304,7 +304,7 @@ export class RichTextRules { const fieldKey = match[1] || ""; const fieldParam = match[2]?.replace("…", "...") || ""; const rawdocid = match[3]?.substring(1); - const docid = rawdocid ? (!rawdocid.includes("@") ? Doc.CurrentUserEmail + "@" + rawdocid : rawdocid).replace(".", "_") : undefined; + const docid = rawdocid ? (!rawdocid.includes("@") ? normalizeEmail(Doc.CurrentUserEmail) + "@" + rawdocid : rawdocid) : undefined; if (!fieldKey && !docid) return state.tr; docid && DocServer.GetRefField(docid).then(docx => { if (!(docx instanceof Doc && docx)) { -- cgit v1.2.3-70-g09d2