diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 14 | ||||
-rw-r--r-- | src/client/util/GroupManager.tsx | 5 | ||||
-rw-r--r-- | src/client/util/SharingManager.tsx | 28 | ||||
-rw-r--r-- | src/client/util/SnappingManager.ts | 8 | ||||
-rw-r--r-- | src/client/views/collections/CollectionStackingView.tsx | 1 | ||||
-rw-r--r-- | src/fields/List.ts | 5 | ||||
-rw-r--r-- | src/fields/util.ts | 46 | ||||
-rw-r--r-- | src/server/websocket.ts | 6 |
8 files changed, 61 insertions, 52 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 580c6040e..694982fea 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -8,9 +8,9 @@ import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; -import { BoolCast, Cast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; +import { BoolCast, Cast, NumCast, PromiseValue, StrCast, DateCast } from "../../fields/Types"; import { nullAudio } from "../../fields/URLField"; -import { SharingPermissions, UserGroups } from "../../fields/util"; +import { SharingPermissions } from "../../fields/util"; import { Utils } from "../../Utils"; import { DocServer } from "../DocServer"; import { Docs, DocumentOptions, DocUtils } from "../documents/Documents"; @@ -32,6 +32,7 @@ import { Scripting } from "./Scripting"; import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; import { UndoManager } from "./UndoManager"; +import { SnappingManager } from "./SnappingManager"; export let resolvedPorts: { server: number, socket: number }; @@ -957,8 +958,13 @@ export class CurrentUserUtils { static async updateUserDocument(doc: Doc, sharingDocumentId: string) { if (!doc.globalGroupDatabase) doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument(); - await DocListCastAsync((doc.globalGroupDatabase as Doc).data); - UserGroups.Current; + const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data); + reaction(() => DateCast((doc.globalGroupDatabase as Doc).lastModified), + async () => { + const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data); + const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || []; + SnappingManager.SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]); + }, { fireImmediately: true }); doc.system = true; doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode; doc.title = Doc.CurrentUserEmail; diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 63e2a8024..cc1d45a58 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -13,6 +13,7 @@ import "./GroupManager.scss"; import { GroupMemberView } from "./GroupMemberView"; import { SharingManager, User } from "./SharingManager"; import { listSpec } from "../../fields/Schema"; +import { DateField } from "../../fields/DateField"; /** * Interface for options for the react-select component @@ -144,6 +145,7 @@ export class GroupManager extends React.Component<{}> { */ addGroup(groupDoc: Doc): boolean { if (this.GroupManagerDoc) { + this.GroupManagerDoc.lastModified = new DateField; Doc.AddDocToList(this.GroupManagerDoc, "data", groupDoc); return true; } @@ -158,6 +160,7 @@ 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)); @@ -184,6 +187,7 @@ 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); } } @@ -200,6 +204,7 @@ 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); } } diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index ee397ab4f..16bcd46c8 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -22,6 +22,7 @@ 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; @@ -172,8 +173,8 @@ export class SharingManager extends React.Component<{}> { group.docsShared ? Doc.IndexOf(doc, DocListCast(group.docsShared)) === -1 && (group.docsShared as List<Doc>).push(doc) : group.docsShared = new List<Doc>([doc]); 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 + if (permission !== SharingPermissions.None) 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.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc || doc)); // remove the doc from the list if it already exists }); } }); @@ -189,9 +190,13 @@ export class SharingManager extends React.Component<{}> { const self = this; if (group.docsShared) { if (!user) retry && this.populateUsers().then(() => self.shareWithAddedMember(group, emailId, false)); - else DocListCastAsync(group.docsShared).then(dl => dl?.forEach(doc => { - Doc.AddDocToList(user.sharingDoc, storage, doc); - })); + else { + DocListCastAsync(user.sharingDoc[storage]).then(userdocs => + DocListCastAsync(group.docsShared).then(dl => { + const filtered = dl?.filter(doc => !userdocs?.includes(doc)); + filtered && userdocs?.push(...filtered); + })); + } } } @@ -222,9 +227,12 @@ export class SharingManager extends React.Component<{}> { const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!; if (group.docsShared) { - DocListCastAsync(group.docsShared).then(dl => dl?.forEach(doc => { - Doc.RemoveDocFromList(user.sharingDoc, storage, doc); - })); + DocListCastAsync(user.sharingDoc[storage]).then(userdocs => + DocListCastAsync(group.docsShared).then(dl => { + const remaining = userdocs?.filter(doc => !dl?.includes(doc)) || []; + userdocs?.splice(0, userdocs.length, ...remaining); + }) + ); } } @@ -261,8 +269,8 @@ export class SharingManager extends React.Component<{}> { 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(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)); + if (permission !== SharingPermissions.None) Doc.AddDocToList(sharingDoc, storage, doc); + else GetEffectiveAcl(doc, undefined, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc || doc)); }); } diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts index fc07e8ab4..d067dff6c 100644 --- a/src/client/util/SnappingManager.ts +++ b/src/client/util/SnappingManager.ts @@ -14,6 +14,9 @@ export namespace SnappingManager { this.horizSnapLines = horizLines; this.vertSnapLines = vertLines; } + + @observable cachedGroups: string[] = []; + @action setCachedGroups(groups: string[]) { this.cachedGroups = groups; } } const manager = new Manager(); @@ -25,5 +28,10 @@ export namespace SnappingManager { export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); } export function GetIsDragging() { return manager.IsDragging; } + + /// bcz; argh!! TODO; These do not belong here, but there were include order problems with leaving them in util.ts + // need to investigate further what caused the mobx update problems and move to a better location. + export function SetCachedGroups(groups: string[]) { manager.setCachedGroups(groups); } + export function GetCachedGroups() { return manager.cachedGroups; } } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index daaee67dc..72ce041e1 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -294,7 +294,6 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, const docs = this.childDocList; if (docs) { newDocs.map((doc, i) => { - console.log(doc.title); if (i === 0) { if (targInd === -1) targInd = docs.length; else targInd = docs.indexOf(this.filteredChildren[targInd]); diff --git a/src/fields/List.ts b/src/fields/List.ts index ceb538b2d..a0cbebaf5 100644 --- a/src/fields/List.ts +++ b/src/fields/List.ts @@ -43,7 +43,7 @@ const listHandlers: any = { } } const res = list.__fields.push(...items); - this[Update]({ op: "$addToSet", items }); + this[Update]({ op: "$addToSet", items, length: length + items.length }); return res; }), reverse() { @@ -77,7 +77,8 @@ const listHandlers: any = { } } const res = list.__fields.splice(start, deleteCount, ...items); - this[Update](items.length === 0 && deleteCount ? { op: "$remFromSet", items: removed } : undefined); + this[Update](items.length === 0 && deleteCount ? { op: "$remFromSet", items: removed, length: list.__fields.length } : + items.length && !deleteCount ? { op: "$addToSet", items, length: list.__fields.length } : undefined); return res.map(toRealField); }), unshift(...items: any[]) { diff --git a/src/fields/util.ts b/src/fields/util.ts index 791b98b83..fd409a54e 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -1,5 +1,5 @@ import { UndoManager } from "../client/util/UndoManager"; -import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, CachedUpdates, DataSym, DocListCast, AclAdmin, FieldsSym, HeightSym, WidthSym, fetchProto, AclUnset } from "./Doc"; +import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, CachedUpdates, DataSym, DocListCast, AclAdmin, FieldsSym, HeightSym, WidthSym, fetchProto, AclUnset, DocListCastAsync } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; @@ -8,10 +8,13 @@ import { action, trace, observable, reaction, computed } from "mobx"; import { Parent, OnUpdate, Update, Id, SelfProxy, Self, HandleUpdate, ToString, ToScriptString } from "./FieldSymbols"; import { DocServer } from "../client/DocServer"; import { ComputedField } from "./ScriptField"; -import { ScriptCast, StrCast } from "./Types"; +import { ScriptCast, StrCast, DateCast, Cast, NumCast } from "./Types"; import { returnZero } from "../Utils"; import CursorField from "./CursorField"; import { List } from "./List"; +import { listSpec } from "./Schema"; +import { DateField } from "./DateField"; +import { SnappingManager } from "../client/util/SnappingManager"; function _readOnlySetter(): never { throw new Error("Documents can't be modified in read-only mode"); @@ -22,32 +25,6 @@ export function TraceMobx() { tracing && trace(); } -// the list of groups that the current user is a member of -export class UserGroups { - static computing = false; - static cachedGroups: string[] = []; - static globalGroupDoc: Doc | undefined; - static get Current() { - if (!Doc.UserDoc() || UserGroups.computing) return UserGroups.cachedGroups; - UserGroups.computing = true; - if (!UserGroups.globalGroupDoc) UserGroups.globalGroupDoc = Doc.UserDoc().globalGroupDatabase as Doc; - if (UserGroups.globalGroupDoc) { - const dbgroups = DocListCast(UserGroups.globalGroupDoc.data); - if (dbgroups.length !== UserGroups.cachedGroups.length - 1) { - UserGroups.cachedGroups = [ - "Public", - ...dbgroups?. - filter(group => group instanceof Doc). - map(group => group as Doc). - filter(group => JSON.parse(StrCast(group.members))?.includes(Doc.CurrentUserEmail)). - map(group => StrCast(group.title)) - ]; - } - } - UserGroups.computing = false; - return UserGroups.cachedGroups; - } -} export interface GetterResult { value: FieldResult; @@ -190,7 +167,7 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number, // if the current user is the author of the document / the current user is a member of the admin group const userChecked = user || Doc.CurrentUserEmail; if (userChecked === (target.__fields?.author || target.author)) return AclAdmin; - if (UserGroups.Current?.includes("Admin")) return AclAdmin; + if (SnappingManager.GetCachedGroups().includes("Admin")) return AclAdmin; if (target[AclSym] && Object.keys(target[AclSym]).length) { @@ -211,7 +188,7 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number, // there are issues with storing fields with . in the name, so they are replaced with _ during creation // as a result we need to restore them again during this comparison. const entity = denormalizeEmail(key.substring(4)); // an individual or a group - if (UserGroups.Current?.includes(entity) || userChecked === entity) { + if (SnappingManager.GetCachedGroups().includes(entity) || userChecked === entity) { if (HierarchyMapping.get(value as symbol)! > HierarchyMapping.get(effectiveAcl)!) { effectiveAcl = value as symbol; if (effectiveAcl === AclAdmin) return effectiveAcl; @@ -388,9 +365,12 @@ export function deleteProperty(target: any, prop: string | number | symbol) { export function updateFunction(target: any, prop: any, value: any, receiver: any) { let current = ObjectField.MakeCopy(value); return (diff?: any) => { - const op = diff?.op === "$addToSet" ? { '$addToSet': { ["fields." + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)) } } : - diff?.op === "$remFromSet" ? { '$remFromSet': { ["fields." + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)) } } - : { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } }; + const op = + diff?.op === "$addToSet" ? { '$addToSet': { ["fields." + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)) } } : + diff?.op === "$remFromSet" ? { '$remFromSet': { ["fields." + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)) } } + : { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } }; + !op['$set'] && ((op as any).length = diff.length); + const oldValue = current; const newValue = ObjectField.MakeCopy(value); current = newValue; diff --git a/src/server/websocket.ts b/src/server/websocket.ts index c9b5d1cbf..03795d254 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -277,7 +277,8 @@ export namespace WebSocket { const newListItems = diff.diff.$set[updatefield].fields; const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields || []; diff.diff.$set[updatefield].fields = [...curList, ...newListItems.filter((newItem: any) => !curList.some((curItem: any) => curItem.fieldId ? curItem.fieldId === newItem.fieldId : curItem.heading ? curItem.heading === newItem.heading : false))]; - const sendBack = true;//curList.length !== prelen; + const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length; + delete diff.diff.length; Database.Instance.update(diff.id, diff.diff, () => { if (sendBack) { @@ -295,7 +296,8 @@ export namespace WebSocket { const remListItems = diff.diff.$set[updatefield].fields; const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields || []; diff.diff.$set[updatefield].fields = curList?.filter((curItem: any) => !remListItems.some((remItem: any) => remItem.fieldId ? remItem.fieldId === curItem.fieldId : remItem.heading ? remItem.heading === curItem.heading : false)); - const sendBack = true;//curList.length + remListItems.length !== prelen; + const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length; + delete diff.diff.length; Database.Instance.update(diff.id, diff.diff, () => { if (sendBack) { |