diff options
author | bobzel <zzzman@gmail.com> | 2022-07-08 12:37:06 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2022-07-08 12:37:06 -0400 |
commit | 5628b585fa6356d66cf2e7454be20e3b847ad22e (patch) | |
tree | 7836fd781c3cd82c00965d22c6bdf325414ef490 /src/fields/util.ts | |
parent | 146f8622d5bac2edc6b09f57c173bd057dfbcfad (diff) |
fixes for drawing ink on pdf/image/etc. fixes for showing contextMenu. moved gestureOverlay into main dashboard area to avoid drawing on UI widgets. more code cleanup to put things in reasonable places and avoid importing too much stuff.
Diffstat (limited to 'src/fields/util.ts')
-rw-r--r-- | src/fields/util.ts | 304 |
1 files changed, 171 insertions, 133 deletions
diff --git a/src/fields/util.ts b/src/fields/util.ts index 8fb35981b..cbb560114 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -1,20 +1,41 @@ -import { UndoManager } from "../client/util/UndoManager"; -import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAugment, AclSym, DataSym, DocListCast, AclAdmin, HeightSym, WidthSym, updateCachedAcls, AclUnset, DocListCastAsync, ForceServerWrite, Initializing, AclSelfEdit } from "./Doc"; -import { SerializationHelper } from "../client/util/SerializationHelper"; -import { ProxyField, PrefetchProxy } from "./Proxy"; -import { RefField } from "./RefField"; -import { ObjectField } from "./ObjectField"; -import { action, trace, } from "mobx"; -import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols"; -import { DocServer } from "../client/DocServer"; -import { ComputedField } from "./ScriptField"; -import { ScriptCast, StrCast } from "./Types"; -import { returnZero } from "../Utils"; -import CursorField from "./CursorField"; -import { List } from "./List"; -import { SnappingManager } from "../client/util/SnappingManager"; -import { computedFn } from "mobx-utils"; -import { RichTextField } from "./RichTextField"; +import { UndoManager } from '../client/util/UndoManager'; +import { + Doc, + FieldResult, + UpdatingFromServer, + LayoutSym, + AclPrivate, + AclEdit, + AclReadonly, + AclAugment, + AclSym, + DataSym, + DocListCast, + AclAdmin, + HeightSym, + WidthSym, + updateCachedAcls, + AclUnset, + DocListCastAsync, + ForceServerWrite, + Initializing, + AclSelfEdit, +} from './Doc'; +import { SerializationHelper } from '../client/util/SerializationHelper'; +import { ProxyField, PrefetchProxy } from './Proxy'; +import { RefField } from './RefField'; +import { ObjectField } from './ObjectField'; +import { action, observable, runInAction, trace } from 'mobx'; +import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from './FieldSymbols'; +import { DocServer } from '../client/DocServer'; +import { ComputedField } from './ScriptField'; +import { ScriptCast, StrCast } from './Types'; +import { returnZero } from '../Utils'; +import CursorField from './CursorField'; +import { List } from './List'; +import { SnappingManager } from '../client/util/SnappingManager'; +import { computedFn } from 'mobx-utils'; +import { RichTextField } from './RichTextField'; function _readOnlySetter(): never { throw new Error("Documents can't be modified in read-only mode"); @@ -44,7 +65,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number return true; } - if (typeof prop === "symbol") { + if (typeof prop === 'symbol') { target[prop] = value; return true; } @@ -76,15 +97,13 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number const writeMode = DocServer.getFieldWriteMode(prop as string); const fromServer = target[UpdatingFromServer]; - const sameAuthor = fromServer || (receiver.author === Doc.CurrentUserEmail); - const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (writeMode !== DocServer.WriteMode.LiveReadonly); - const writeToServer = - (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && (value instanceof RichTextField))) && - !DocServer.Control.isReadOnly(); + const sameAuthor = fromServer || receiver.author === Doc.CurrentUserEmail; + const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode !== DocServer.WriteMode.LiveReadonly; + const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && value instanceof RichTextField)) && !DocServer.Control.isReadOnly(); if (writeToDoc) { if (value === undefined) { - target.__fieldKeys && (delete target.__fieldKeys[prop]); + target.__fieldKeys && delete target.__fieldKeys[prop]; delete target.__fields[prop]; } else { target.__fieldKeys && (target.__fieldKeys[prop] = true); @@ -96,16 +115,17 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number //if (typeof value === "object" && !(value instanceof ObjectField)) debugger; if (writeToServer) { - if (value === undefined) target[Update]({ '$unset': { ["fields." + prop]: "" } }); - else target[Update]({ '$set': { ["fields." + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) : (value === undefined ? null : value) } }); + if (value === undefined) target[Update]({ $unset: { ['fields.' + prop]: '' } }); + else target[Update]({ $set: { ['fields.' + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) : value === undefined ? null : value } }); } else { DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue); } - !receiver[Initializing] && (!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) && + !receiver[Initializing] && + (!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) && UndoManager.AddEvent({ - redo: () => receiver[prop] = value, - undo: () => receiver[prop] = curValue, - prop: prop?.toString() + redo: () => (receiver[prop] = value), + undo: () => (receiver[prop] = curValue), + prop: prop?.toString(), }); return true; } @@ -136,7 +156,6 @@ export function denormalizeEmail(email: string) { // playgroundMode = !playgroundMode; // } - /** * Copies parent's acl fields to the child */ @@ -145,35 +164,37 @@ export function inheritParentAcls(parent: Doc, child: Doc) { const dataDoc = parent[DataSym]; for (const key of Object.keys(dataDoc)) { // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private. - const permission = (key === "acl-Public" && Doc.defaultAclPrivate) ? AclPrivate : dataDoc[key]; - key.startsWith("acl") && distributeAcls(key, permission, child); + const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key]; + key.startsWith('acl') && distributeAcls(key, permission, child); } } /** * These are the various levels of access a user can have to a document. - * + * * Admin: a user with admin access to a document can remove/edit that document, add/remove/edit annotations (depending on permissions), as well as change others' access rights to that document. - * + * * Edit: a user with edit access to a document can remove/edit that document, add/remove/edit annotations (depending on permissions), but not change any access rights to that document. - * + * * Add: a user with add access to a document can augment documents/annotations to that document but cannot edit or delete anything. - * + * * View: a user with view access to a document can only view it - they cannot add/remove/edit anything. - * + * * None: the document is not shared with that user. */ export enum SharingPermissions { - Admin = "Admin", - Edit = "Edit", - SelfEdit = "Self Edit", - Augment = "Augment", - View = "View", - None = "Not Shared" + Admin = 'Admin', + Edit = 'Edit', + SelfEdit = 'Self Edit', + Augment = 'Augment', + View = 'View', + None = 'Not Shared', } // return acl from cache or cache the acl and return. -const getEffectiveAclCache = computedFn(function (target: any, user?: string) { return getEffectiveAcl(target, user); }, true); +const getEffectiveAclCache = computedFn(function (target: any, user?: string) { + return getEffectiveAcl(target, user); +}, true); /** * Calculates the effective access right to a document for the current user. @@ -183,33 +204,47 @@ export function GetEffectiveAcl(target: any, user?: string): symbol { if (target[UpdatingFromServer]) return AclAdmin; // authored documents are private until an ACL is set. if (!target[AclSym] && target.author && target.author !== Doc.CurrentUserEmail) return AclPrivate; - return getEffectiveAclCache(target, user);// all changes received from the server must be processed as Admin. return this directly so that the acls aren't cached (UpdatingFromServer is not observable) + return getEffectiveAclCache(target, user); // all changes received from the server must be processed as Admin. return this directly so that the acls aren't cached (UpdatingFromServer is not observable) } function getPropAcl(target: any, prop: string | symbol | number) { - if (prop === UpdatingFromServer || prop === Initializing || target[UpdatingFromServer] || prop === AclSym) return AclAdmin; // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent + if (prop === UpdatingFromServer || prop === Initializing || target[UpdatingFromServer] || prop === AclSym) return AclAdmin; // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent if (prop && DocServer.IsPlaygroundField(prop.toString())) return AclEdit; // playground props are always editable return GetEffectiveAcl(target); } let HierarchyMapping: Map<symbol, number> | undefined; +let cachedGroups = observable([] as string[]); +/// 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. +const getCachedGroupByNameCache = computedFn(function (name: string) { + return cachedGroups.includes(name); +}, true); +export function GetCachedGroupByName(name: string) { + return getCachedGroupByNameCache(name); +} +export function SetCachedGroups(groups: string[]) { + runInAction(() => cachedGroups.push(...groups)); +} function getEffectiveAcl(target: any, user?: string): symbol { const targetAcls = target[AclSym]; - const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group - const targetAuthor = (target.__fields?.author || target.author); // target may be a Doc of Proxy, so check __fields.author and .author + const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group + const targetAuthor = target.__fields?.author || target.author; // target may be a Doc of Proxy, so check __fields.author and .author if (userChecked === targetAuthor || !targetAuthor) return AclAdmin; - if (SnappingManager.GetCachedGroupByName("Admin")) return AclAdmin; + if (GetCachedGroupByName('Admin')) return AclAdmin; if (targetAcls && Object.keys(targetAcls).length) { - HierarchyMapping = HierarchyMapping || new Map<symbol, number>([ - [AclPrivate, 0], - [AclReadonly, 1], - [AclAugment, 2], - [AclSelfEdit, 2.5], - [AclEdit, 3], - [AclAdmin, 4] - ]); + HierarchyMapping = + HierarchyMapping || + new Map<symbol, number>([ + [AclPrivate, 0], + [AclReadonly, 1], + [AclAugment, 2], + [AclSelfEdit, 2.5], + [AclEdit, 3], + [AclAdmin, 4], + ]); let effectiveAcl = AclPrivate; for (const [key, value] of Object.entries(targetAcls)) { @@ -217,14 +252,14 @@ function getEffectiveAcl(target: any, user?: string): symbol { // as a result we need to restore them again during this comparison. const entity = denormalizeEmail(key.substring(4)); // an individual or a group if (HierarchyMapping.get(value as symbol)! > HierarchyMapping.get(effectiveAcl)!) { - if (SnappingManager.GetCachedGroupByName(entity) || userChecked === entity) { + if (GetCachedGroupByName(entity) || userChecked === entity) { effectiveAcl = value as symbol; } } } // if there's an overriding acl set through the properties panel or sharing menu, that's what's returned if the user isn't an admin of the document - const override = targetAcls["acl-Override"]; + const override = targetAcls['acl-Override']; if (override !== AclUnset && override !== undefined) effectiveAcl = override; // if we're in playground mode, return AclEdit (or AclAdmin if that's the user's effectiveAcl) @@ -246,12 +281,12 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc visited.push(target); const HierarchyMapping = new Map<string, number>([ - ["Not Shared", 0], - ["Can View", 1], - ["Can Augment", 2], - ["Self Edit", 2.5], - ["Can Edit", 3], - ["Admin", 4] + ['Not Shared', 0], + ['Can View', 1], + ['Can Augment', 2], + ['Self Edit', 2.5], + ['Can Edit', 3], + ['Admin', 4], ]); let layoutDocChanged = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym]) @@ -271,7 +306,6 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc } if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) { - if (GetEffectiveAcl(dataDoc) === AclAdmin) { dataDoc[key] = acl; dataDocChanged = true; @@ -282,7 +316,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc links.forEach(link => distributeAcls(key, acl, link, inheritingFromCollection, visited)); // maps over the children of the document - DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + (isDashboard ? "-all" : "")]).map(d => { + DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + (isDashboard ? '-all' : '')]).map(d => { distributeAcls(key, acl, d, inheritingFromCollection, visited); // } const data = d[DataSym]; @@ -292,7 +326,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc }); // maps over the annotations of the document - DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + "-annotations"]).map(d => { + DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '-annotations']).map(d => { distributeAcls(key, acl, d, inheritingFromCollection, visited); // } const data = d[DataSym]; @@ -311,11 +345,11 @@ export function setter(target: any, in_prop: string | symbol | number, value: an const effectiveAcl = getPropAcl(target, prop); if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) return true; // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't - if (typeof prop === "string" && prop.startsWith("acl") && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined, "None"].includes(value))) return true; + if (typeof prop === 'string' && prop.startsWith('acl') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined, 'None'].includes(value))) return true; // if (typeof prop === "string" && prop.startsWith("acl") && !["Can Edit", "Can Augment", "Can View", "Not Shared", undefined].includes(value)) return true; - if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && prop.startsWith("_")) { - if (!prop.startsWith("__")) prop = prop.substring(1); + if (typeof prop === 'string' && prop !== '__id' && prop !== '__fields' && prop.startsWith('_')) { + if (!prop.startsWith('__')) prop = prop.substring(1); if (target.__LAYOUT__) { target.__LAYOUT__[prop] = value; return true; @@ -331,17 +365,18 @@ export function getter(target: any, in_prop: string | symbol | number, receiver: let prop = in_prop; if (in_prop === AclSym) return target[AclSym]; - if (in_prop === "toString" || (in_prop !== HeightSym && in_prop !== WidthSym && in_prop !== LayoutSym && typeof prop === "symbol")) return target.__fields[prop] || target[prop]; + if (in_prop === 'toString' || (in_prop !== HeightSym && in_prop !== WidthSym && in_prop !== LayoutSym && typeof prop === 'symbol')) return target.__fields[prop] || target[prop]; if (GetEffectiveAcl(target) === AclPrivate) return prop === HeightSym || prop === WidthSym ? returnZero : undefined; if (prop === LayoutSym) return target.__LAYOUT__; - if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && prop.startsWith("_")) { - if (!prop.startsWith("__")) prop = prop.substring(1); + if (typeof prop === 'string' && prop !== '__id' && prop !== '__fields' && prop.startsWith('_')) { + if (!prop.startsWith('__')) prop = prop.substring(1); if (target.__LAYOUT__) return target.__LAYOUT__[prop]; } - if (prop === "then") {//If we're being awaited + if (prop === 'then') { + //If we're being awaited return undefined; } - if (typeof prop === "symbol") { + if (typeof prop === 'symbol') { return target.__fields[prop] || target[prop]; } if (SerializationHelper.IsSerializing()) { @@ -362,22 +397,21 @@ function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreP field = res.value; } } - if (field === undefined && !ignoreProto && prop !== "proto") { - const proto = getFieldImpl(target, "proto", receiver, true);//TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters + if (field === undefined && !ignoreProto && prop !== 'proto') { + const proto = getFieldImpl(target, 'proto', receiver, true); //TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters if (proto instanceof Doc && GetEffectiveAcl(proto) !== AclPrivate) { return getFieldImpl(proto[Self], prop, receiver, ignoreProto); } return undefined; } return field; - } export function getField(target: any, prop: string | number, ignoreProto: boolean = false): any { return getFieldImpl(target, prop, undefined, ignoreProto); } export function deleteProperty(target: any, prop: string | number | symbol) { - if (typeof prop === "symbol") { + if (typeof prop === 'symbol') { delete target[prop]; return true; } @@ -389,64 +423,68 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any let lastValue = 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) } }; + 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 prevValue = ObjectField.MakeCopy(lastValue as List<any>); lastValue = ObjectField.MakeCopy(value); const newValue = ObjectField.MakeCopy(value); - if (!(value instanceof CursorField) && !(value?.some?.((v: any) => v instanceof CursorField))) { - !receiver[UpdatingFromServer] && UndoManager.AddEvent( - diff?.op === "$addToSet" ? - { - redo: () => { - receiver[prop].push(...diff.items.map((item: any) => item.value ? item.value() : item)); - lastValue = ObjectField.MakeCopy(receiver[prop]); - }, - undo: action(() => { - // console.log("undo $add: " + prop, diff.items) // bcz: uncomment to log undo - diff.items.forEach((item: any) => { - const ind = receiver[prop].indexOf(item.value ? item.value() : item); - ind !== -1 && receiver[prop].splice(ind, 1); - }); - lastValue = ObjectField.MakeCopy(receiver[prop]); - }), - prop: "" - } : - diff?.op === "$remFromSet" ? - { - redo: action(() => { - diff.items.forEach((item: any) => { - const ind = receiver[prop].indexOf(item.value ? item.value() : item); - ind !== -1 && receiver[prop].splice(ind, 1); - }); - lastValue = ObjectField.MakeCopy(receiver[prop]); - }), - undo: () => { - // console.log("undo $rem: " + prop, diff.items) // bcz: uncomment to log undo - diff.items.forEach((item: any) => { - const ind = (prevValue as List<any>).indexOf(item.value ? item.value() : item); - ind !== -1 && receiver[prop].indexOf(item.value ? item.value() : item) === -1 && receiver[prop].splice(ind, 0, item); - }); - lastValue = ObjectField.MakeCopy(receiver[prop]); - }, - prop: "" - } + if (!(value instanceof CursorField) && !value?.some?.((v: any) => v instanceof CursorField)) { + !receiver[UpdatingFromServer] && + UndoManager.AddEvent( + diff?.op === '$addToSet' + ? { + redo: () => { + receiver[prop].push(...diff.items.map((item: any) => (item.value ? item.value() : item))); + lastValue = ObjectField.MakeCopy(receiver[prop]); + }, + undo: action(() => { + // console.log("undo $add: " + prop, diff.items) // bcz: uncomment to log undo + diff.items.forEach((item: any) => { + const ind = receiver[prop].indexOf(item.value ? item.value() : item); + ind !== -1 && receiver[prop].splice(ind, 1); + }); + lastValue = ObjectField.MakeCopy(receiver[prop]); + }), + prop: '', + } + : diff?.op === '$remFromSet' + ? { + redo: action(() => { + diff.items.forEach((item: any) => { + const ind = receiver[prop].indexOf(item.value ? item.value() : item); + ind !== -1 && receiver[prop].splice(ind, 1); + }); + lastValue = ObjectField.MakeCopy(receiver[prop]); + }), + undo: () => { + // console.log("undo $rem: " + prop, diff.items) // bcz: uncomment to log undo + diff.items.forEach((item: any) => { + const ind = (prevValue as List<any>).indexOf(item.value ? item.value() : item); + ind !== -1 && receiver[prop].indexOf(item.value ? item.value() : item) === -1 && receiver[prop].splice(ind, 0, item); + }); + lastValue = ObjectField.MakeCopy(receiver[prop]); + }, + prop: '', + } : { - redo: () => { - receiver[prop] = ObjectField.MakeCopy(newValue as List<any>); - lastValue = ObjectField.MakeCopy(receiver[prop]); - }, - undo: () => { - // console.log("undo list: " + prop, receiver[prop]) // bcz: uncomment to log undo - receiver[prop] = ObjectField.MakeCopy(prevValue as List<any>); - lastValue = ObjectField.MakeCopy(receiver[prop]); - }, - prop: "" - }); + redo: () => { + receiver[prop] = ObjectField.MakeCopy(newValue as List<any>); + lastValue = ObjectField.MakeCopy(receiver[prop]); + }, + undo: () => { + // console.log("undo list: " + prop, receiver[prop]) // bcz: uncomment to log undo + receiver[prop] = ObjectField.MakeCopy(prevValue as List<any>); + lastValue = ObjectField.MakeCopy(receiver[prop]); + }, + prop: '', + } + ); } target[Update](op); }; -}
\ No newline at end of file +} |