diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 26 | ||||
-rw-r--r-- | src/client/util/GroupManager.tsx | 8 | ||||
-rw-r--r-- | src/client/util/LinkManager.ts | 12 | ||||
-rw-r--r-- | src/client/util/SharingManager.tsx | 9 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 12 | ||||
-rw-r--r-- | src/fields/Doc.ts | 1 | ||||
-rw-r--r-- | src/server/ApiManagers/UserManager.ts | 12 | ||||
-rw-r--r-- | src/server/GarbageCollector.ts | 2 | ||||
-rw-r--r-- | src/server/authentication/AuthenticationManager.ts | 1 | ||||
-rw-r--r-- | src/server/authentication/DashUserModel.ts | 4 |
10 files changed, 57 insertions, 30 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 694982fea..7535d7c24 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1,6 +1,6 @@ import { computed, observable, reaction } from "mobx"; import * as rp from 'request-promise'; -import { DataSym, Doc, DocListCast, DocListCastAsync } from "../../fields/Doc"; +import { DataSym, Doc, DocListCast, DocListCastAsync, AclReadonly } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; @@ -541,9 +541,9 @@ export class CurrentUserUtils { })) as any as Doc; } } - static async setupMenuPanel(doc: Doc, sharingDocumentId: string) { + static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { if (doc.menuStack === undefined) { - await this.setupSharingSidebar(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing + await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId); // 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, @@ -876,7 +876,16 @@ export class CurrentUserUtils { } // Sharing sidebar is where shared documents are contained - static async setupSharingSidebar(doc: Doc, sharingDocumentId: string) { + static async setupSharingSidebar(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { + if (doc.myLinkDatabase === undefined) { + let linkDocs = await DocServer.GetRefField(linkDatabaseId); + if (!linkDocs) { + linkDocs = new Doc(linkDatabaseId, true); + (linkDocs as Doc).data = new List<Doc>([]); + (linkDocs as Doc)["acl-Public"] = SharingPermissions.Add; + } + doc.myLinkDatabase = new PrefetchProxy(linkDocs); + } if (doc.mySharedDocs === undefined) { let sharedDocs = await DocServer.GetRefField(sharingDocumentId + "outer"); if (!sharedDocs) { @@ -956,7 +965,7 @@ export class CurrentUserUtils { return doc.clickFuncs as Doc; } - static async updateUserDocument(doc: Doc, sharingDocumentId: string) { + static async updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { if (!doc.globalGroupDatabase) doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument(); const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data); reaction(() => DateCast((doc.globalGroupDatabase as Doc).lastModified), @@ -996,9 +1005,8 @@ 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 - await this.setupMenuPanel(doc, sharingDocumentId); + await this.setupMenuPanel(doc, sharingDocumentId, linkDatabaseId); if (!doc.globalScriptDatabase) doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument(); - if (!doc.myLinkDatabase) doc.myLinkDatabase = new List([]); setTimeout(() => this.setupDefaultPresentation(doc), 0); // presentation that's initially triggered @@ -1036,10 +1044,10 @@ export class CurrentUserUtils { public static async loadUserDocument(id: string) { this.curr_id = id; await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => { - const { userDocumentId, sharingDocumentId } = JSON.parse(ids); + const { userDocumentId, sharingDocumentId, linkDatabaseId } = JSON.parse(ids); if (userDocumentId !== "guest") { return DocServer.GetRefField(userDocumentId).then(async field => - 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, linkDatabaseId)); } 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 cc1d45a58..6458de0ed 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -145,8 +145,8 @@ export class GroupManager extends React.Component<{}> { */ addGroup(groupDoc: Doc): boolean { if (this.GroupManagerDoc) { - this.GroupManagerDoc.lastModified = new DateField; Doc.AddDocToList(this.GroupManagerDoc, "data", groupDoc); + this.GroupManagerDoc.lastModified = new DateField; return true; } return false; @@ -160,7 +160,6 @@ export class GroupManager extends React.Component<{}> { deleteGroup(group: Doc): boolean { if (group) { if (this.GroupManagerDoc && this.hasEditAccess(group)) { - this.GroupManagerDoc.lastModified = new DateField; Doc.RemoveDocFromList(this.GroupManagerDoc, "data", group); SharingManager.Instance.removeGroup(group); const members = JSON.parse(StrCast(group.members)); @@ -168,6 +167,7 @@ export class GroupManager extends React.Component<{}> { const index = DocListCast(this.GroupManagerDoc.data).findIndex(grp => grp === group); index !== -1 && Cast(this.GroupManagerDoc.data, listSpec(Doc), [])?.splice(index, 1); } + this.GroupManagerDoc.lastModified = new DateField; if (group === this.currentGroup) { this.currentGroup = undefined; } @@ -187,8 +187,8 @@ export class GroupManager extends React.Component<{}> { const memberList = JSON.parse(StrCast(groupDoc.members)); !memberList.includes(email) && memberList.push(email); groupDoc.members = JSON.stringify(memberList); - this.GroupManagerDoc && (this.GroupManagerDoc.lastModified = new DateField); SharingManager.Instance.shareWithAddedMember(groupDoc, email); + this.GroupManagerDoc && (this.GroupManagerDoc.lastModified = new DateField); } } @@ -204,8 +204,8 @@ export class GroupManager extends React.Component<{}> { if (index !== -1) { const user = memberList.splice(index, 1)[0]; groupDoc.members = JSON.stringify(memberList); - this.GroupManagerDoc && (this.GroupManagerDoc.lastModified = new DateField); SharingManager.Instance.removeMember(groupDoc, email); + this.GroupManagerDoc && (this.GroupManagerDoc.lastModified = new DateField); } } } diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 56b6cb8a9..0456b4029 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -36,17 +36,21 @@ export class LinkManager { public getAllLinks(): Doc[] { - const lset = new Set<Doc>(DocListCast(Doc.UserDoc().myLinkDatabase)); - SharingManager.Instance.users.forEach(user => DocListCast(user.sharingDoc.myLinkDatabase).map(lset.add)); + const lset = new Set<Doc>(DocListCast(Doc.LinkDBDoc().data)); + SharingManager.Instance.users.forEach(user => { + DocListCast((user.linkDatabase as Doc)?.data).map(doc => { + lset.add(doc); + }); + }); return Array.from(lset); } public addLink(linkDoc: Doc): boolean { - return Doc.AddDocToList(Doc.UserDoc(), "myLinkDatabase", linkDoc); + return Doc.AddDocToList(Doc.LinkDBDoc(), "data", linkDoc); } public deleteLink(linkDoc: Doc): boolean { - return Doc.RemoveDocFromList(Doc.UserDoc(), "myLinkDatabase", linkDoc); + return Doc.RemoveDocFromList(Doc.LinkDBDoc(), "data", linkDoc); } // finds all links that contain the given anchor diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 16bcd46c8..271face98 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -22,11 +22,11 @@ import "./SharingManager.scss"; import { SelectionManager } from "./SelectionManager"; import { intersection } from "lodash"; import { SearchBox } from "../views/search/SearchBox"; -import { listSpec } from "../../fields/Schema"; export interface User { email: string; sharingDocumentId: string; + linkDatabaseId: string; } /** @@ -53,6 +53,7 @@ const storage = "data"; interface ValidatedUser { user: User; // database minimal info to identify / communicate with a user (email, sharing doc id) sharingDoc: Doc; // document to share/message another user + linkDatabase: Doc; userColor: string; // stored on the sharinDoc, extracted for convenience? } @@ -130,8 +131,10 @@ export class SharingManager extends React.Component<{}> { const isCandidate = user.email !== Doc.CurrentUserEmail; if (isCandidate) { const sharingDoc = await DocServer.GetRefField(user.sharingDocumentId); - if (sharingDoc instanceof Doc) { - sharingDocs.push({ user, sharingDoc, userColor: StrCast(sharingDoc.color) }); + const linkDatabase = await DocServer.GetRefField(user.linkDatabaseId); + if (sharingDoc instanceof Doc && linkDatabase instanceof Doc) { + await DocListCastAsync(linkDatabase.data); + sharingDocs.push({ user, sharingDoc, linkDatabase, userColor: StrCast(sharingDoc.color) }); } } }); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 598657a58..99a009d13 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -289,10 +289,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } } else { - - const json = JSON.parse(Cast(this.dataDoc[this.fieldKey], RichTextField)?.Data!); - json.selection = state.toJSON().selection; - this._editorView.updateState(EditorState.fromJSON(this.config, json)); + const jsonstring = Cast(this.dataDoc[this.fieldKey], RichTextField)?.Data!; + if (jsonstring) { + const json = JSON.parse(jsonstring); + json.selection = state.toJSON().selection; + this._editorView.updateState(EditorState.fromJSON(this.config, json)); + } } } } @@ -1554,7 +1556,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } @computed get sidebarHandle() { - const annotated = DocListCast(this.dataDoc[this.annotationKey]).filter(d => d?.title).length; + const annotated = DocListCast(this.dataDoc[this.annotationKey]).filter(d => d?.author).length; return !this.props.isSelected() && !(annotated && !this.sidebarWidth()) ? (null) : <div className="formattedTextBox-sidebar-handle" style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`, background: annotated ? "lightBlue" : undefined }} diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index d85f0785e..0e0a49876 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -885,6 +885,7 @@ export namespace Doc { export function SetSearchQuery(query: string) { runInAction(() => manager._searchQuery = query); } export function UserDoc(): Doc { return manager._user_doc; } export function SharingDoc(): Doc { return Cast(Doc.UserDoc().mySharedDocs, Doc, null); } + export function LinkDBDoc(): Doc { return Cast(Doc.UserDoc().myLinkDatabase, 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/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts index e5c0f3827..f36506b14 100644 --- a/src/server/ApiManagers/UserManager.ts +++ b/src/server/ApiManagers/UserManager.ts @@ -19,9 +19,9 @@ export default class UserManager extends ApiManager { method: Method.GET, subscription: "/getUsers", secureHandler: async ({ res }) => { - const cursor = await Database.Instance.query({}, { email: 1, sharingDocumentId: 1 }, "users"); + const cursor = await Database.Instance.query({}, { email: 1, linkDatabaseId: 1, sharingDocumentId: 1 }, "users"); const results = await cursor.toArray(); - res.send(results.map(user => ({ email: user.email, sharingDocumentId: user.sharingDocumentId }))); + res.send(results.map(user => ({ email: user.email, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId }))); } }); @@ -47,7 +47,7 @@ export default class UserManager extends ApiManager { register({ method: Method.GET, subscription: "/getUserDocumentIds", - secureHandler: ({ res, user }) => res.send({ userDocumentId: user.userDocumentId, sharingDocumentId: user.sharingDocumentId }) + secureHandler: ({ res, user }) => res.send({ userDocumentId: user.userDocumentId, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId }) }); register({ @@ -58,6 +58,12 @@ export default class UserManager extends ApiManager { register({ method: Method.GET, + subscription: "/getLinkDatabaseId", + secureHandler: ({ res, user }) => res.send(user.linkDatabaseId) + }); + + register({ + method: Method.GET, subscription: "/getCurrentUser", secureHandler: ({ res, user: { _id, email, cacheDocumentIds } }) => res.send(JSON.stringify({ id: _id, email, cacheDocumentIds })), publicHandler: ({ res }) => res.send(JSON.stringify({ id: "__guest__", email: "" })) diff --git a/src/server/GarbageCollector.ts b/src/server/GarbageCollector.ts index 6bd0e5163..7c441e3c0 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), ...users.map(user => user.sharingDocumentId)]]; + const ids: string[] = [...users.map(user => user.userDocumentId), ...users.map(user => user.sharingDocumentId), ...users.map(user => user.linkDatabaseId)]; const visited = new Set<string>(); const files: { [name: string]: string[] } = {}; diff --git a/src/server/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts index 84abd41a2..9eb4a328f 100644 --- a/src/server/authentication/AuthenticationManager.ts +++ b/src/server/authentication/AuthenticationManager.ts @@ -49,6 +49,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => { password, userDocumentId: Utils.GenerateGuid(), sharingDocumentId: Utils.GenerateGuid(), + linkDatabaseId: Utils.GenerateGuid(), cacheDocumentIds: "" } as Partial<DashUserModel>; diff --git a/src/server/authentication/DashUserModel.ts b/src/server/authentication/DashUserModel.ts index 4f2856a78..bee28b96d 100644 --- a/src/server/authentication/DashUserModel.ts +++ b/src/server/authentication/DashUserModel.ts @@ -11,6 +11,7 @@ export type DashUserModel = mongoose.Document & { userDocumentId: string; sharingDocumentId: string; + linkDatabaseId: string; cacheDocumentIds: string; profile: { @@ -39,7 +40,8 @@ const userSchema = new mongoose.Schema({ 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 - cacheDocumentIds: String, + linkDatabaseId: String, + cacheDocumentIds: String, // set of document ids to retreive on startup facebook: String, twitter: String, |