From 17ccb0a1a37c906c0151d3da5e92de3d94205924 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Fri, 24 Jul 2020 01:45:05 +0530 Subject: groups and acl bugfix --- src/fields/util.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/fields') diff --git a/src/fields/util.ts b/src/fields/util.ts index 20b072786..cd1e24826 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -210,7 +210,8 @@ export function setter(target: any, in_prop: string | symbol | number, value: an return true; } - if (typeof prop === "string" && prop.startsWith("ACL") && !["Can Edit", "Can Add", "Can View", "Not Shared", undefined].includes(value)) return true; + if (typeof prop === "string" && prop.startsWith("ACL") && ((target.author && Doc.CurrentUserEmail !== target.author) || !["Can Edit", "Can Add", "Can View", "Not Shared", undefined].includes(value))) return true; + // if (typeof prop === "string" && prop.startsWith("ACL") && !["Can Edit", "Can Add", "Can View", "Not Shared", undefined].includes(value)) return true; if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) { if (!prop.startsWith("_")) { -- cgit v1.2.3-70-g09d2 From 5317e0edac369d32ca84985910819cabe9d04e6a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 23 Jul 2020 22:43:19 -0400 Subject: fixed zipping scriptFields --- src/fields/Doc.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/fields') diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 7e91a7761..e28f6b7d2 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -17,7 +17,7 @@ import { RichTextField } from "./RichTextField"; import { ImageField, VideoField, WebField, AudioField, PdfField } from "./URLField"; import { DateField } from "./DateField"; import { listSpec } from "./Schema"; -import { ComputedField } from "./ScriptField"; +import { ComputedField, ScriptField } from "./ScriptField"; import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types"; import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction, GetEffectiveAcl } from "./util"; import { LinkManager } from "../client/util/LinkManager"; @@ -575,6 +575,7 @@ export namespace Doc { return { fieldId: value[Id], __type: "proxy" }; } } + else if (value instanceof ScriptField) return { script: value.script, __type: "script" }; else if (value instanceof RichTextField) return { Data: value.Data, Text: value.Text, __type: "RichTextField" }; else if (value instanceof ImageField) return { url: value.url.href, __type: "image" }; else if (value instanceof PdfField) return { url: value.url.href, __type: "pdf" }; -- cgit v1.2.3-70-g09d2 From 2f49497e9d74eda97b2327ca4dbcb0a11ac6c6c9 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Fri, 24 Jul 2020 17:11:09 +0530 Subject: added admin acl + renamed LinkCreatedBox to TaskCompletedBox and added custom text functionality + popups on sharing docs and creating groups + relocated SharingPermissions --- src/client/util/GroupManager.tsx | 15 +++++++- src/client/util/SharingManager.tsx | 46 +++++++++++++++---------- src/client/views/DocComponent.tsx | 3 +- src/client/views/MainView.tsx | 4 +-- src/client/views/collections/CollectionView.tsx | 8 ++--- src/client/views/nodes/DocumentLinksButton.tsx | 18 +++++----- src/client/views/nodes/DocumentView.tsx | 17 +++++---- src/client/views/nodes/LinkCreatedBox.scss | 21 ----------- src/client/views/nodes/LinkCreatedBox.tsx | 31 ----------------- src/client/views/nodes/LinkDescriptionPopup.tsx | 4 +-- src/client/views/nodes/TaskCompletedBox.scss | 20 +++++++++++ src/client/views/nodes/TaskCompletedBox.tsx | 32 +++++++++++++++++ src/fields/Doc.ts | 7 ++-- src/fields/util.ts | 28 +++++++++------ 14 files changed, 143 insertions(+), 111 deletions(-) delete mode 100644 src/client/views/nodes/LinkCreatedBox.scss delete mode 100644 src/client/views/nodes/LinkCreatedBox.tsx create mode 100644 src/client/views/nodes/TaskCompletedBox.scss create mode 100644 src/client/views/nodes/TaskCompletedBox.tsx (limited to 'src/fields') diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index a727d21d0..72fba5c1b 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -16,6 +16,7 @@ import { StrCast, Cast } from "../../fields/Types"; import GroupMemberView from "./GroupMemberView"; import { setGroups } from "../../fields/util"; import { DocServer } from "../DocServer"; +import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox"; library.add(fa.faPlus, fa.faTimes, fa.faInfoCircle); @@ -36,11 +37,13 @@ export default class GroupManager extends React.Component<{}> { @observable currentGroup: Opt; // the currently selected group. @observable private createGroupModalOpen: boolean = false; private inputRef: React.RefObject = React.createRef(); // the ref for the input box. + private createGroupButtonRef: React.RefObject = React.createRef(); private currentUserGroups: string[] = []; @observable private buttonColour: "#979797" | "black" = "#979797"; @observable private groupSort: "ascending" | "descending" | "none" = "none"; + constructor(props: Readonly<{}>) { super(props); GroupManager.Instance = this; @@ -303,6 +306,14 @@ export default class GroupManager extends React.Component<{}> { this.selectedUsers = null; this.inputRef.current.value = ""; this.buttonColour = "#979797"; + + const { left, width, top } = this.createGroupButtonRef.current!.getBoundingClientRect(); + TaskCompletionBox.popupX = left - 2 * width; + TaskCompletionBox.popupY = top; + TaskCompletionBox.textDisplayed = "Group created!"; + TaskCompletionBox.taskCompleted = true; + setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2000); + } private get groupCreationModal() { @@ -345,7 +356,9 @@ export default class GroupManager extends React.Component<{}> { }) }} /> - diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 95c1bcda8..00d570670 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -7,8 +7,7 @@ import { InteractionUtils } from '../util/InteractionUtils'; import { List } from '../../fields/List'; import { DateField } from '../../fields/DateField'; import { ScriptField } from '../../fields/ScriptField'; -import { GetEffectiveAcl, getPlaygroundMode } from '../../fields/util'; -import { SharingPermissions } from '../util/SharingManager'; +import { GetEffectiveAcl, getPlaygroundMode, SharingPermissions } from '../../fields/util'; /// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView) diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 225fb2e8e..81f2feee0 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -61,7 +61,7 @@ import { DocumentManager } from '../util/DocumentManager'; import { DocumentLinksButton } from './nodes/DocumentLinksButton'; import { LinkMenu } from './linking/LinkMenu'; import { LinkDocPreview } from './nodes/LinkDocPreview'; -import { LinkCreatedBox } from './nodes/LinkCreatedBox'; +import { TaskCompletionBox } from './nodes/TaskCompletedBox'; import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup'; import FormatShapePane from "./collections/collectionFreeForm/FormatShapePane"; import HypothesisAuthenticationManager from '../apis/HypothesisAuthenticationManager'; @@ -623,7 +623,7 @@ export class MainView extends React.Component { {this.mainContent} - + diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 53fd83f26..4848508be 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -8,7 +8,7 @@ import * as React from 'react'; import Lightbox from 'react-image-lightbox-with-rotate'; import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app import { DateField } from '../../../fields/DateField'; -import { AclAddonly, AclReadonly, DataSym, Doc, DocListCast, Field, Opt, AclEdit, AclSym, AclPrivate } from '../../../fields/Doc'; +import { AclAddonly, AclReadonly, DataSym, Doc, DocListCast, Field, Opt, AclEdit, AclSym, AclPrivate, AclAdmin } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; @@ -17,7 +17,7 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; -import { TraceMobx, GetEffectiveAcl, getPlaygroundMode, distributeAcls } from '../../../fields/util'; +import { TraceMobx, GetEffectiveAcl, getPlaygroundMode, distributeAcls, SharingPermissions } from '../../../fields/util'; import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; @@ -48,7 +48,6 @@ import { CollectionTimeView } from './CollectionTimeView'; import { CollectionTreeView } from "./CollectionTreeView"; import './CollectionView.scss'; import CollectionMenu from './CollectionMenu'; -import { SharingPermissions } from '../../util/SharingManager'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -111,7 +110,8 @@ export class CollectionView extends Touchable { if (linkDoc) { - LinkCreatedBox.popupX = e.screenX; - LinkCreatedBox.popupY = e.screenY - 133; - LinkCreatedBox.linkCreated = true; + TaskCompletionBox.popupX = e.screenX; + TaskCompletionBox.popupY = e.screenY - 133; + TaskCompletionBox.taskCompleted = true; LinkDescriptionPopup.popupX = e.screenX; LinkDescriptionPopup.popupY = e.screenY - 100; LinkDescriptionPopup.descriptionPopup = true; - setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500); + setTimeout(action(() => { TaskCompletionBox.taskCompleted = false; }), 2500); } }); @@ -134,9 +134,9 @@ export class DocumentLinksButton extends React.Component { if (linkDoc) { - LinkCreatedBox.popupX = e.screenX; - LinkCreatedBox.popupY = e.screenY - 133; - LinkCreatedBox.linkCreated = true; + TaskCompletionBox.popupX = e.screenX; + TaskCompletionBox.popupY = e.screenY - 133; + TaskCompletionBox.taskCompleted = true; if (LinkDescriptionPopup.showDescriptions === "ON" || !LinkDescriptionPopup.showDescriptions) { LinkDescriptionPopup.popupX = e.screenX; @@ -144,7 +144,7 @@ export class DocumentLinksButton extends React.Component { LinkCreatedBox.linkCreated = false; }), 2500); + setTimeout(action(() => { TaskCompletionBox.taskCompleted = false; }), 2500); } }); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9e492650b..1d4b61545 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -11,7 +11,7 @@ import { listSpec } from "../../../fields/Schema"; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../fields/Types"; -import { TraceMobx, GetEffectiveAcl } from '../../../fields/util'; +import { TraceMobx, GetEffectiveAcl, SharingPermissions } from '../../../fields/util'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils, emptyPath } from "../../../Utils"; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; @@ -25,7 +25,7 @@ import { InteractionUtils } from '../../util/InteractionUtils'; import { Scripting } from '../../util/Scripting'; import { SearchUtil } from '../../util/SearchUtil'; import { SelectionManager } from "../../util/SelectionManager"; -import SharingManager, { SharingPermissions } from '../../util/SharingManager'; +import SharingManager from '../../util/SharingManager'; import { Transform } from "../../util/Transform"; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { CollectionView, CollectionViewType } from '../collections/CollectionView'; @@ -41,7 +41,7 @@ import { RadialMenu } from './RadialMenu'; import React = require("react"); import { DocumentLinksButton } from './DocumentLinksButton'; import { MobileInterface } from '../../../mobile/MobileInterface'; -import { LinkCreatedBox } from './LinkCreatedBox'; +import { TaskCompletionBox } from './TaskCompletedBox'; import { LinkDescriptionPopup } from './LinkDescriptionPopup'; import { LinkManager } from '../../util/LinkManager'; @@ -629,15 +629,15 @@ export class DocumentView extends DocComponent(Docu const makeLink = action((linkDoc: Doc) => { LinkManager.currentLink = linkDoc; - LinkCreatedBox.popupX = de.x; - LinkCreatedBox.popupY = de.y - 33; - LinkCreatedBox.linkCreated = true; + TaskCompletionBox.popupX = de.x; + TaskCompletionBox.popupY = de.y - 33; + TaskCompletionBox.taskCompleted = true; LinkDescriptionPopup.popupX = de.x; LinkDescriptionPopup.popupY = de.y; LinkDescriptionPopup.descriptionPopup = true; - setTimeout(action(() => LinkCreatedBox.linkCreated = false), 2500); + setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500); }); if (de.complete.annoDragData) { /// this whole section for handling PDF annotations looks weird. Need to rethink this to make it cleaner @@ -818,6 +818,9 @@ export class DocumentView extends DocComponent(Docu !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" }); cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" }); + // to be removed for baseline + cm.addItem({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" }); + // const existingAcls = cm.findByDescription("Privacy..."); // const aclItems: ContextMenuProps[] = existingAcls && "subitems" in existingAcls ? existingAcls.subitems : []; // aclItems.push({ description: "Make Add Only", event: () => this.setAcl(SharingPermissions.Add), icon: "concierge-bell" }); diff --git a/src/client/views/nodes/LinkCreatedBox.scss b/src/client/views/nodes/LinkCreatedBox.scss deleted file mode 100644 index 3cbd38b55..000000000 --- a/src/client/views/nodes/LinkCreatedBox.scss +++ /dev/null @@ -1,21 +0,0 @@ -.linkCreatedBox-fade { - border: 1px solid rgb(100, 100, 100); - - - width: auto; - position: absolute; - - height: auto; - z-index: 10000; - border-radius: 13px; - font-size: 13px; - white-space: nowrap; - - color: rgb(100, 100, 100); - background-color: rgba(250, 250, 250, 0.85); - padding-top: 6.5px; - padding-bottom: 6.5px; - font-weight: bold; - padding-left: 9px; - padding-right: 9px; -} \ No newline at end of file diff --git a/src/client/views/nodes/LinkCreatedBox.tsx b/src/client/views/nodes/LinkCreatedBox.tsx deleted file mode 100644 index 648ae23c8..000000000 --- a/src/client/views/nodes/LinkCreatedBox.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React = require("react"); -import { observer } from "mobx-react"; -import { documentSchema } from "../../../fields/documentSchemas"; -import { makeInterface } from "../../../fields/Schema"; -import "./LinkCreatedBox.scss"; -import { observable, action } from "mobx"; -import { Fade } from "@material-ui/core"; - - -@observer -export class LinkCreatedBox extends React.Component<{}> { - - @observable public static linkCreated: boolean = false; - @observable public static popupX: number = 500; - @observable public static popupY: number = 150; - - @action - public static changeLinkCreated = () => { - LinkCreatedBox.linkCreated = !LinkCreatedBox.linkCreated; - } - - render() { - return -
Link Created
-
; - } -} \ No newline at end of file diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx index 06e8d30d1..d8fe47f4e 100644 --- a/src/client/views/nodes/LinkDescriptionPopup.tsx +++ b/src/client/views/nodes/LinkDescriptionPopup.tsx @@ -4,7 +4,7 @@ import "./LinkDescriptionPopup.scss"; import { observable, action } from "mobx"; import { EditableView } from "../EditableView"; import { LinkManager } from "../../util/LinkManager"; -import { LinkCreatedBox } from "./LinkCreatedBox"; +import { TaskCompletionBox } from "./TaskCompletedBox"; @observer @@ -31,7 +31,7 @@ export class LinkDescriptionPopup extends React.Component<{}> { onClick = (e: PointerEvent) => { if (this.popupRef && !!!this.popupRef.current?.contains(e.target as any)) { LinkDescriptionPopup.descriptionPopup = false; - LinkCreatedBox.linkCreated = false; + TaskCompletionBox.taskCompleted = false; } } diff --git a/src/client/views/nodes/TaskCompletedBox.scss b/src/client/views/nodes/TaskCompletedBox.scss new file mode 100644 index 000000000..80b750b39 --- /dev/null +++ b/src/client/views/nodes/TaskCompletedBox.scss @@ -0,0 +1,20 @@ +.taskCompletedBox-fade { + border: 1px solid rgb(100, 100, 100); + + width: auto; + position: absolute; + + height: auto; + z-index: 10000; + border-radius: 13px; + font-size: 13px; + white-space: nowrap; + + color: rgb(100, 100, 100); + background-color: rgba(250, 250, 250, 0.85); + padding-top: 6.5px; + padding-bottom: 6.5px; + font-weight: bold; + padding-left: 9px; + padding-right: 9px; +} \ No newline at end of file diff --git a/src/client/views/nodes/TaskCompletedBox.tsx b/src/client/views/nodes/TaskCompletedBox.tsx new file mode 100644 index 000000000..1ba2d1713 --- /dev/null +++ b/src/client/views/nodes/TaskCompletedBox.tsx @@ -0,0 +1,32 @@ +import React = require("react"); +import { observer } from "mobx-react"; +import { documentSchema } from "../../../fields/documentSchemas"; +import { makeInterface } from "../../../fields/Schema"; +import "./TaskCompletedBox.scss"; +import { observable, action } from "mobx"; +import { Fade } from "@material-ui/core"; + + +@observer +export class TaskCompletionBox extends React.Component<{}> { + + @observable public static taskCompleted: boolean = false; + @observable public static popupX: number = 500; + @observable public static popupY: number = 150; + @observable public static textDisplayed: string = "Link Created"; + + @action + public static toggleTaskCompleted = () => { + TaskCompletionBox.taskCompleted = !TaskCompletionBox.taskCompleted; + } + + render() { + return +
{TaskCompletionBox.textDisplayed}
+
; + } +} \ No newline at end of file diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 917a6853c..0c816461c 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -19,9 +19,8 @@ import { DateField } from "./DateField"; import { listSpec } from "./Schema"; import { ComputedField } from "./ScriptField"; import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types"; -import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction, GetEffectiveAcl } from "./util"; +import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction, GetEffectiveAcl, SharingPermissions } from "./util"; import { LinkManager } from "../client/util/LinkManager"; -import { SharingPermissions } from "../client/util/SharingManager"; import JSZip = require("jszip"); import { saveAs } from "file-saver"; @@ -103,6 +102,7 @@ export const AclPrivate = Symbol("AclOwnerOnly"); export const AclReadonly = Symbol("AclReadOnly"); export const AclAddonly = Symbol("AclAddonly"); export const AclEdit = Symbol("AclEdit"); +export const AclAdmin = Symbol("AclAdmin"); export const UpdatingFromServer = Symbol("UpdatingFromServer"); const CachedUpdates = Symbol("Cached updates"); @@ -110,7 +110,8 @@ const AclMap = new Map([ [SharingPermissions.None, AclPrivate], [SharingPermissions.View, AclReadonly], [SharingPermissions.Add, AclAddonly], - [SharingPermissions.Edit, AclEdit] + [SharingPermissions.Edit, AclEdit], + [SharingPermissions.Admin, AclAdmin] ]); export function fetchProto(doc: Doc) { diff --git a/src/fields/util.ts b/src/fields/util.ts index cd1e24826..eba398dfb 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, fetchProto, DataSym, DocListCast } from "./Doc"; +import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, fetchProto, DataSym, DocListCast, AclAdmin } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; @@ -9,7 +9,6 @@ import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols"; import { DocServer } from "../client/DocServer"; import { ComputedField } from "./ScriptField"; import { ScriptCast, StrCast } from "./Types"; -import { SharingPermissions } from "../client/util/SharingManager"; function _readOnlySetter(): never { @@ -127,12 +126,20 @@ export function setGroups(groups: string[]) { currentUserGroups = groups; } +export enum SharingPermissions { + Admin = "Admin", + Edit = "Can Edit", + Add = "Can Add", + View = "Can View", + None = "Not Shared" +} + export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number): symbol { if (in_prop === UpdatingFromServer || target[UpdatingFromServer]) return AclEdit; if (target[AclSym] && Object.keys(target[AclSym]).length) { - if (target.__fields?.author === Doc.CurrentUserEmail || target.author === Doc.CurrentUserEmail || currentUserGroups.includes("admin")) return AclEdit; + if (target.__fields?.author === Doc.CurrentUserEmail || target.author === Doc.CurrentUserEmail || currentUserGroups.includes("admin")) return AclAdmin; if (_overrideAcl || (in_prop && DocServer.PlaygroundFields?.includes(in_prop.toString()))) return AclEdit; @@ -143,7 +150,8 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number) [AclPrivate, 0], [AclReadonly, 1], [AclAddonly, 2], - [AclEdit, 3] + [AclEdit, 3], + [AclAdmin, 4] ]); for (const [key, value] of Object.entries(target[AclSym])) { @@ -157,7 +165,7 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number) } return aclPresent ? effectiveAcl : AclEdit; } - return AclEdit; + return AclAdmin; } export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean) { @@ -166,7 +174,8 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc ["Not Shared", 0], ["Can View", 1], ["Can Add", 2], - ["Can Edit", 3] + ["Can Edit", 3], + ["Admin", 4] ]); const dataDoc = target[DataSym]; @@ -206,11 +215,10 @@ const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHe "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"]; export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean { let prop = in_prop; - if (GetEffectiveAcl(target, in_prop) !== AclEdit) { - return true; - } + const effectiveAcl = GetEffectiveAcl(target, in_prop); + if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin) return true; - if (typeof prop === "string" && prop.startsWith("ACL") && ((target.author && Doc.CurrentUserEmail !== target.author) || !["Can Edit", "Can Add", "Can View", "Not Shared", undefined].includes(value))) return true; + if (typeof prop === "string" && prop.startsWith("ACL") && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value))) return true; // if (typeof prop === "string" && prop.startsWith("ACL") && !["Can Edit", "Can Add", "Can View", "Not Shared", undefined].includes(value)) return true; if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) { -- cgit v1.2.3-70-g09d2 From 150d1b3ea263dd0d6d8189d8bf6fd87c5505cfc4 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Fri, 24 Jul 2020 19:15:52 +0530 Subject: fixed instances of AclEdit to include AclAdmin --- src/client/views/DocComponent.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 5 +++-- src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 5 +++-- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 5 +++-- src/fields/util.ts | 7 +++++-- 6 files changed, 17 insertions(+), 11 deletions(-) (limited to 'src/fields') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 00d570670..cfabae8a1 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -150,7 +150,7 @@ export function ViewBoxAnnotatableComponent

{ - if (GetEffectiveAcl(this.props.Document) === AclEdit || getPlaygroundMode()) { + const effectiveAcl = GetEffectiveAcl(this.props.Document); + if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin || getPlaygroundMode()) { const docs = doc instanceof Doc ? [doc] : doc as Doc[]; const targetDataDoc = this.props.Document[DataSym]; const value = DocListCast(targetDataDoc[this.props.fieldKey]); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 84719b2c9..abe9ccd5e 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -1,6 +1,6 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, Opt, DocListCast, DataSym, AclEdit, AclAddonly } from "../../../../fields/Doc"; +import { Doc, Opt, DocListCast, DataSym, AclEdit, AclAddonly, AclAdmin } from "../../../../fields/Doc"; import { GetEffectiveAcl, getPlaygroundMode } from "../../../../fields/util"; import { InkData, InkField, InkTool } from "../../../../fields/InkField"; import { List } from "../../../../fields/List"; @@ -281,7 +281,7 @@ export class MarqueeView extends React.Component(Docu } moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" }); } - GetEffectiveAcl(this.props.Document) === AclEdit && moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" }); + const effectiveAcl = GetEffectiveAcl(this.props.Document); + (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) && moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" }); moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "external-link-alt" }); !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" }); cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 38fa66d65..2ac53ce6a 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -13,7 +13,7 @@ import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from " import { ReplaceStep } from 'prosemirror-transform'; import { EditorView } from "prosemirror-view"; import { DateField } from '../../../../fields/DateField'; -import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, AclEdit } from "../../../../fields/Doc"; +import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, AclEdit, AclAdmin } from "../../../../fields/Doc"; import { documentSchema } from '../../../../fields/documentSchemas'; import applyDevTools = require("prosemirror-dev-tools"); import { removeMarkWithAttrs } from "./prosemirrorPatches"; @@ -233,7 +233,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template const json = JSON.stringify(state.toJSON()); let unchanged = true; - if (GetEffectiveAcl(this.dataDoc) === AclEdit) { + const effectiveAcl = GetEffectiveAcl(this.dataDoc); + if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) { this._applyingChange = true; (curText !== Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text) && (this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()))); diff --git a/src/fields/util.ts b/src/fields/util.ts index eba398dfb..a8b284658 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -67,11 +67,14 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number delete curValue[Parent]; delete curValue[OnUpdate]; } + + const effectiveAcl = GetEffectiveAcl(target); + const writeMode = DocServer.getFieldWriteMode(prop as string); const fromServer = target[UpdatingFromServer]; const sameAuthor = fromServer || (receiver.author === Doc.CurrentUserEmail); - const writeToDoc = sameAuthor || GetEffectiveAcl(target) === AclEdit || (writeMode !== DocServer.WriteMode.LiveReadonly); - const writeToServer = (sameAuthor || GetEffectiveAcl(target) === AclEdit || writeMode === DocServer.WriteMode.Default) && !playgroundMode; + const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (writeMode !== DocServer.WriteMode.LiveReadonly); + const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode === DocServer.WriteMode.Default) && !playgroundMode; if (writeToDoc) { if (value === undefined) { -- cgit v1.2.3-70-g09d2 From 760c51c493ef190aa5283944b083b55f7c057e6c Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 24 Jul 2020 23:59:09 -0400 Subject: several siggnificant changes to how acls work with makin documents updated interactively when permissions chane. --- src/client/DocServer.ts | 80 +++++++++++++--------- .../collectionFreeForm/CollectionFreeFormView.tsx | 5 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 2 +- src/fields/Doc.ts | 48 +++++++++---- src/fields/Schema.ts | 2 +- src/fields/util.ts | 10 ++- 6 files changed, 95 insertions(+), 52 deletions(-) (limited to 'src/fields') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index bac324c77..8ded43468 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,13 +1,14 @@ import * as io from 'socket.io-client'; import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "./../server/Message"; -import { Opt, Doc, fetchProto } from '../fields/Doc'; +import { Opt, Doc, fetchProto, FieldsSym } from '../fields/Doc'; import { Utils, emptyFunction } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; import { RefField } from '../fields/RefField'; -import { Id, HandleUpdate } from '../fields/FieldSymbols'; +import { Id, HandleUpdate, Parent } from '../fields/FieldSymbols'; import GestureOverlay from './views/GestureOverlay'; import MobileInkOverlay from '../mobile/MobileInkOverlay'; import { runInAction } from 'mobx'; +import { ObjectField } from '../fields/ObjectField'; /** * This class encapsulates the transfer and cross-client synchronization of @@ -207,12 +208,12 @@ export namespace DocServer { * the server if the document has not been cached. * @param id the id of the requested document */ - const _GetRefFieldImpl = (id: string): Promise> => { + const _GetRefFieldImpl = (id: string, force: boolean = false): Promise> => { // an initial pass through the cache to determine whether the document needs to be fetched, // is already in the process of being fetched or already exists in the // cache const cached = _cache[id]; - if (cached === undefined) { + if (cached === undefined || force) { // NOT CACHED => we'll have to send a request to the server // synchronously, we emit a single callback to the server requesting the serialized (i.e. represented by a string) @@ -226,7 +227,16 @@ export namespace DocServer { const deserializeField = getSerializedField.then(async fieldJson => { // deserialize const field = await SerializationHelper.Deserialize(fieldJson); - if (field !== undefined) { + if (force && field instanceof Doc && cached instanceof Doc) { + Array.from(Object.keys(field)).forEach(key => { + const fieldval = field[key]; + if (fieldval instanceof ObjectField) { + fieldval[Parent] = undefined; + } + cached[key] = field[key]; + }); + } + else if (field !== undefined) { _cache[id] = field; } else { delete _cache[id]; @@ -238,8 +248,8 @@ export namespace DocServer { }); // here, indicate that the document associated with this id is currently // being retrieved and cached - _cache[id] = deserializeField; - return deserializeField; + !force && (_cache[id] = deserializeField); + return force ? cached as any : deserializeField; } else if (cached instanceof Promise) { // BEING RETRIEVED AND CACHED => some other caller previously (likely recently) called GetRefField(s), // and requested the document I'm looking for. Shouldn't fetch again, just @@ -260,11 +270,11 @@ export namespace DocServer { } }; - let _GetRefField: (id: string) => Promise> = errorFunc; + let _GetRefField: (id: string, force: boolean) => Promise> = errorFunc; let _GetCachedRefField: (id: string) => Opt = errorFunc; - export function GetRefField(id: string): Promise> { - return _GetRefField(id); + export function GetRefField(id: string, force = false): Promise> { + return _GetRefField(id, force); } export function GetCachedRefField(id: string): Opt { return _GetCachedRefField(id); @@ -330,29 +340,35 @@ export namespace DocServer { const proms: Promise[] = []; runInAction(() => { for (const field of fields) { - if (field !== undefined && field !== null) { + if (field !== undefined && field !== null && !_cache[field.id]) { // deserialize - const prom = SerializationHelper.Deserialize(field).then(deserialized => { - fieldMap[field.id] = deserialized; - - //overwrite or delete any promises (that we inserted as flags - // to indicate that the field was in the process of being fetched). Now everything - // should be an actual value within or entirely absent from the cache. - if (deserialized !== undefined) { - _cache[field.id] = deserialized; - } else { - delete _cache[field.id]; - } - return deserialized; - }); - // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache) - // we set the value at the field's id to a promise that will resolve to the field. - // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method). - // The mapping in the .then call ensures that when other callers await these promises, they'll - // get the resolved field - _cache[field.id] = prom; - // adds to a list of promises that will be awaited asynchronously - proms.push(prom); + const cached = _cache[field.id]; + if (!cached) { + const prom = SerializationHelper.Deserialize(field).then(deserialized => { + fieldMap[field.id] = deserialized; + + //overwrite or delete any promises (that we inserted as flags + // to indicate that the field was in the process of being fetched). Now everything + // should be an actual value within or entirely absent from the cache. + if (deserialized !== undefined) { + _cache[field.id] = deserialized; + } else { + delete _cache[field.id]; + } + return deserialized; + }); + // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache) + // we set the value at the field's id to a promise that will resolve to the field. + // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method). + // The mapping in the .then call ensures that when other callers await these promises, they'll + // get the resolved field + _cache[field.id] = prom; + + // adds to a list of promises that will be awaited asynchronously + proms.push(prom); + } else if (cached instanceof Promise) { + proms.push(cached as any); + } } } }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 979b21321..38c1edde2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -944,7 +944,8 @@ export class CollectionFreeFormView extends CollectionSubView this.props.active(outsideReaction) || this.backgroundActive ? true : false; getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps { return { - ...this.props, + pinToPres: this.props.pinToPres, + whenActiveChanged: this.props.whenActiveChanged, NativeHeight: returnZero, NativeWidth: returnZero, fitToBox: false, @@ -1145,7 +1146,7 @@ export class CollectionFreeFormView extends CollectionSubView this.doLayoutComputation, + this._layoutComputeReaction = reaction(() => { TraceMobx(); return this.doLayoutComputation }, (elements) => this._layoutElements = elements || [], { fireImmediately: true, name: "doLayout" }); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 2ac53ce6a..53234d819 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -690,7 +690,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp ); this._disposers.editorState = reaction( () => { - if (this.dataDoc[this.props.fieldKey + "-noTemplate"] || !this.layoutDoc[this.props.fieldKey + "-textTemplate"]) { + if (this.dataDoc?.[this.props.fieldKey + "-noTemplate"] || !this.layoutDoc[this.props.fieldKey + "-textTemplate"]) { return Cast(this.dataDoc[this.props.fieldKey], RichTextField, null)?.Data; } return Cast(this.layoutDoc[this.props.fieldKey + "-textTemplate"], RichTextField, null)?.Data; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index c9231753f..9020dcc84 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -104,7 +104,7 @@ export const AclAddonly = Symbol("AclAddonly"); export const AclEdit = Symbol("AclEdit"); export const AclAdmin = Symbol("AclAdmin"); export const UpdatingFromServer = Symbol("UpdatingFromServer"); -const CachedUpdates = Symbol("Cached updates"); +export const CachedUpdates = Symbol("Cached updates"); const AclMap = new Map([ [SharingPermissions.None, AclPrivate], @@ -115,15 +115,15 @@ const AclMap = new Map([ ]); export function fetchProto(doc: Doc) { - // if (doc.author !== Doc.CurrentUserEmail) { - untracked(() => { - const permissions: { [key: string]: symbol } = {}; + const permissions: { [key: string]: symbol } = {}; - Object.keys(doc).filter(key => key.startsWith("ACL")).forEach(key => permissions[key] = AclMap.get(StrCast(doc[key]))!); + Object.keys(doc).filter(key => key.startsWith("ACL")).forEach(key => permissions[key] = AclMap.get(StrCast(doc[key]))!); - if (Object.keys(permissions).length) doc[AclSym] = permissions; - }); - // } + if (Object.keys(permissions).length) doc[AclSym] = permissions; + + if (GetEffectiveAcl(doc) === AclPrivate) { + runInAction(() => doc[FieldsSym](true)); + } if (doc.proto instanceof Promise) { doc.proto.then(fetchProto); @@ -143,7 +143,7 @@ export class Doc extends RefField { has: (target, key) => GetEffectiveAcl(target) !== AclPrivate && key in target.__fields, ownKeys: target => { const obj = {} as any; - if (GetEffectiveAcl(target) !== AclPrivate) Object.assign(obj, target.___fields); + if (GetEffectiveAcl(target) !== AclPrivate) Object.assign(obj, target.___fieldKeys); runInAction(() => obj.__LAYOUT__ = target.__LAYOUT__); return Object.keys(obj); }, @@ -151,11 +151,11 @@ export class Doc extends RefField { if (prop.toString() === "__LAYOUT__") { return Reflect.getOwnPropertyDescriptor(target, prop); } - if (prop in target.__fields) { + if (prop in target.__fieldKeys) { return { configurable: true,//TODO Should configurable be true? enumerable: true, - value: target.__fields[prop] + value: 0//() => target.__fields[prop]) }; } return Reflect.getOwnPropertyDescriptor(target, prop); @@ -179,15 +179,23 @@ export class Doc extends RefField { this.___fields = value; for (const key in value) { const field = value[key]; + field && (this.__fieldKeys[key] = true); if (!(field instanceof ObjectField)) continue; field[Parent] = this[Self]; field[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]); } } + private get __fieldKeys() { return this.___fieldKeys; } + private set __fieldKeys(value) { + this.___fieldKeys = value; + } @observable private ___fields: any = {}; + @observable + private ___fieldKeys: any = {}; + private [UpdatingFromServer]: boolean = false; private [Update] = (diff: any) => { @@ -196,7 +204,14 @@ export class Doc extends RefField { private [Self] = this; private [SelfProxy]: any; - public [FieldsSym] = () => this.___fields; + public [FieldsSym] = (clear?: boolean) => { + if (clear) { + this.___fields = {}; + this.___fieldKeys = {}; + } + return this.___fields; + } + @observable public [AclSym]: { [key: string]: symbol }; public [WidthSym] = () => NumCast(this[SelfProxy]._width); public [HeightSym] = () => NumCast(this[SelfProxy]._height); @@ -237,11 +252,18 @@ export class Doc extends RefField { const fKey = key.substring(7); const fn = async () => { const value = await SerializationHelper.Deserialize(set[key]); + const prev = GetEffectiveAcl(this); this[UpdatingFromServer] = true; this[fKey] = value; + if (fKey.startsWith("ACL")) { + fetchProto(this); + } this[UpdatingFromServer] = false; + if (prev === AclPrivate && GetEffectiveAcl(this) !== AclPrivate) { + DocServer.GetRefField(this[Id], true); + } }; - if (sameAuthor || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) { + if (sameAuthor || fKey.startsWith("ACL") || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) { delete this[CachedUpdates][fKey]; await fn(); } else { diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts index 98ef3e087..c6a8d6ae9 100644 --- a/src/fields/Schema.ts +++ b/src/fields/Schema.ts @@ -31,7 +31,7 @@ export function makeInterface(...schemas: T): InterfaceFu } const proto = new Proxy({}, { get(target: any, prop, receiver) { - const field = receiver.doc[prop]; + const field = receiver.doc?.[prop]; if (prop in schema) { const desc = prop === "proto" ? Doc : (schema as any)[prop]; // bcz: proto doesn't appear in schemas ... maybe it should? if (typeof desc === "object" && "defaultVal" in desc && "type" in desc) {//defaultSpec diff --git a/src/fields/util.ts b/src/fields/util.ts index 2b970d342..e0b1843a1 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -1,11 +1,11 @@ import { UndoManager } from "../client/util/UndoManager"; -import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, fetchProto, DataSym, DocListCast, AclAdmin } from "./Doc"; +import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, CachedUpdates, DataSym, DocListCast, AclAdmin, FieldsSym } 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 { Parent, OnUpdate, Update, Id, SelfProxy, Self, HandleUpdate } from "./FieldSymbols"; import { DocServer } from "../client/DocServer"; import { ComputedField } from "./ScriptField"; import { ScriptCast, StrCast } from "./Types"; @@ -77,8 +77,10 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number if (writeToDoc) { if (value === undefined) { + delete target.__fieldKeys[prop]; delete target.__fields[prop]; } else { + target.__fieldKeys[prop] = true; target.__fields[prop] = value; } //if (typeof value === "object" && !(value instanceof ObjectField)) debugger; @@ -137,7 +139,7 @@ export enum SharingPermissions { } export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number): symbol { - if (in_prop === UpdatingFromServer || target[UpdatingFromServer]) return AclEdit; + if (in_prop === UpdatingFromServer || target[UpdatingFromServer]) return AclAdmin; if (target[AclSym] && Object.keys(target[AclSym]).length) { @@ -241,6 +243,8 @@ export function setter(target: any, in_prop: string | symbol | number, value: an export function getter(target: any, in_prop: string | symbol | number, receiver: any): any { let prop = in_prop; + + if (in_prop === FieldsSym || in_prop === Id || in_prop === HandleUpdate || in_prop === CachedUpdates) return target.__fields[prop] || target[prop]; if (in_prop === AclSym) return _overrideAcl ? undefined : target[AclSym]; if (GetEffectiveAcl(target) === AclPrivate && !_overrideAcl) return undefined; if (prop === LayoutSym) { -- cgit v1.2.3-70-g09d2 From 99e0b01dbe41e56cfdb748b53f03fe71d73a80b7 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 25 Jul 2020 10:54:12 -0400 Subject: pushpin annotations can follow links to multple targets at once. chaned text boxes so that they can be made isBackround .fixed addin/remoin documents from freeform collections. --- src/client/util/DocumentManager.ts | 44 +++++++++++----------- .../collectionFreeForm/CollectionFreeFormView.tsx | 3 ++ .../views/nodes/formattedText/FormattedTextBox.tsx | 2 +- src/fields/Doc.ts | 1 - 4 files changed, 27 insertions(+), 23 deletions(-) (limited to 'src/fields') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 51b50878d..523dbfca0 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -152,18 +152,18 @@ export class DocumentManager { const first = getFirstDocView(annotatedDoc); if (first) { annotatedDoc = first.props.Document; - if (docView) { - docView.props.focus(annotatedDoc, false); - } + docView?.props.focus(annotatedDoc, false); } } if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight? - if (originatingDoc?.isPushpin) docView.props.Document.hidden = !docView.props.Document.hidden; + if (originatingDoc?.isPushpin) { + docView.props.Document.hidden = !docView.props.Document.hidden; + } else { docView.props.Document.hidden && (docView.props.Document.hidden = undefined); docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish); + highlight(); } - highlight(); } else { const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined; const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc)) ? docContext : undefined; @@ -218,25 +218,27 @@ export class DocumentManager { const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0); const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView; const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : (traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs); - const linkDoc = linkDocList.length && linkDocList[0]; - if (linkDoc) { - const target = (doc === linkDoc.anchor1 ? linkDoc.anchor2 : doc === linkDoc.anchor2 ? linkDoc.anchor1 : - (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc; - const targetTimecode = (doc === linkDoc.anchor1 ? Cast(linkDoc.anchor2_timecode, "number") : - doc === linkDoc.anchor2 ? Cast(linkDoc.anchor1_timecode, "number") : - (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? Cast(linkDoc.anchor2_timecode, "number") : Cast(linkDoc.anchor1_timecode, "number"))); - if (target) { - const containerDoc = (await Cast(target.annotationOn, Doc)) || target; - containerDoc.currentTimecode = targetTimecode; - const targetContext = await target?.context as Doc; - const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined; - DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "onRight"), finished), targetNavContext, linkDoc, undefined, doc, finished); + const followLinks = linkDocList.length ? (doc.isPushpin ? linkDocList : [linkDocList[0]]) : []; + followLinks.forEach(async linkDoc => { + if (linkDoc) { + const target = (doc === linkDoc.anchor1 ? linkDoc.anchor2 : doc === linkDoc.anchor2 ? linkDoc.anchor1 : + (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc; + const targetTimecode = (doc === linkDoc.anchor1 ? Cast(linkDoc.anchor2_timecode, "number") : + doc === linkDoc.anchor2 ? Cast(linkDoc.anchor1_timecode, "number") : + (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) ? Cast(linkDoc.anchor2_timecode, "number") : Cast(linkDoc.anchor1_timecode, "number"))); + if (target) { + const containerDoc = (await Cast(target.annotationOn, Doc)) || target; + containerDoc.currentTimecode = targetTimecode; + const targetContext = await target?.context as Doc; + const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined; + DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "onRight"), finished), targetNavContext, linkDoc, undefined, doc, finished); + } else { + finished?.(); + } } else { finished?.(); } - } else { - finished?.(); - } + }); } } Scripting.addGlobal(function DocFocus(doc: any) { DocumentManager.Instance.getDocumentViews(Doc.GetProto(doc)).map(view => view.props.focus(doc, true)); }); \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 38c1edde2..079add4b0 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -944,6 +944,9 @@ export class CollectionFreeFormView extends CollectionSubView this.props.active(outsideReaction) || this.backgroundActive ? true : false; getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps { return { + addDocument: this.props.addDocument, + removeDocument: this.props.removeDocument, + moveDocument: this.props.moveDocument, pinToPres: this.props.pinToPres, whenActiveChanged: this.props.whenActiveChanged, NativeHeight: returnZero, diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 53234d819..966aaa895 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1406,7 +1406,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp

diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 9020dcc84..3ea53a9e0 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -916,7 +916,6 @@ export namespace Doc { return doc; } export function UnBrushDoc(doc: Doc) { - if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc; brushManager.BrushedDoc.delete(doc); brushManager.BrushedDoc.delete(Doc.GetProto(doc)); -- cgit v1.2.3-70-g09d2 From 5454385b6fabbcafee4d797829fb9ceaa8a71ed1 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 25 Jul 2020 20:14:16 -0400 Subject: made buttons turn into fonticons on iconify. droppin fonticons onto linear views keeps them as buttons. droppin buttons on linear view makes them fonticons. fixed ColletionMenu scripts to work with templates by usin self instead of this. fixed pointer events with text boxes for filterview & normal. fixed fieldKey deletin and addin --- src/client/util/CurrentUserUtils.ts | 12 +++++++++-- src/client/util/DragManager.ts | 7 +++---- src/client/util/DropConverter.ts | 2 +- src/client/views/MainView.tsx | 4 ++-- src/client/views/collections/CollectionMenu.tsx | 23 ++++++++++++++-------- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/FontIconBox.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 ++-- src/fields/util.ts | 4 ++-- 9 files changed, 37 insertions(+), 23 deletions(-) (limited to 'src/fields') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b47cfb33a..6d752832a 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -346,6 +346,14 @@ export class CurrentUserUtils { iconRtfView.isTemplateDoc = makeTemplate(iconRtfView, true, "icon_" + DocumentType.RTF); doc["template-icon-view-rtf"] = new PrefetchProxy(iconRtfView); } + if (doc["template-icon-view-button"] === undefined) { + const iconBtnView = Docs.Create.FontIconDocument({ + title: "icon_" + DocumentType.BUTTON, _nativeHeight: 30, _nativeWidth: 30, + _width: 30, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") + }); + iconBtnView.isTemplateDoc = makeTemplate(iconBtnView, true, "icon_" + DocumentType.BUTTON); + doc["template-icon-view-button"] = new PrefetchProxy(iconBtnView); + } if (doc["template-icon-view-img"] === undefined) { const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { title: "data", _width: 50, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") @@ -359,11 +367,11 @@ export class CurrentUserUtils { doc["template-icon-view-col"] = new PrefetchProxy(iconColView); } if (doc["template-icons"] === undefined) { - doc["template-icons"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, + doc["template-icons"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc, doc["template-icon-view-pdf"] as Doc], { title: "icon templates", _height: 75 })); } else { const templateIconsDoc = Cast(doc["template-icons"], Doc, null); - const requiredTypes = [doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, + const requiredTypes = [doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc]; DocListCastAsync(templateIconsDoc.data).then(async curIcons => { await Promise.all(curIcons!); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 837f0b1db..4b1860b5c 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -224,11 +224,10 @@ export namespace DragManager { } // drag a button template and drop a new button - export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) { + export function + StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) { const finishDrag = (e: DragCompleteEvent) => { - const bd = params.length > Object.keys(vars).length ? - Docs.Create.ButtonDocument({ toolTip: title, z: 1, _width: 150, _height: 50, title, onClick: ScriptField.MakeScript(script) }) : - Docs.Create.FontIconDocument({ toolTip: title, z: 1, _nativeWidth: 30, _nativeHeight: 30, _width: 30, _height: 30, title, onClick: ScriptField.MakeScript(script) }); + const bd = Docs.Create.ButtonDocument({ toolTip: title, z: 1, _width: 150, _height: 50, title, onClick: ScriptField.MakeScript(script) }); params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc))); // copy all "captured" arguments into document parameterfields initialize?.(bd); Doc.GetProto(bd)["onClick-paramFieldKeys"] = new List(params); diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index f9837298d..d0acf14c3 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -57,7 +57,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { data?.draggedDocuments.map((doc, i) => { let dbox = doc; // bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant - if (doc.type === DocumentType.FONTICON) { + if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes("FontIconBox")) { dbox = Doc.MakeAlias(doc); } else if (!doc.onDragStart && !doc.isButtonBar) { const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 81f2feee0..ae3f05fb7 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -8,7 +8,7 @@ import { faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTimesCircle, faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight, faHeading, faRulerCombined, faFillDrip, faLink, faUnlink, faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, - faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faMap + faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faMap, faUser } from '@fortawesome/free-solid-svg-icons'; import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -150,7 +150,7 @@ export class MainView extends React.Component { faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTrashAlt, faAngleRight, faBell, faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight, faHeading, faRulerCombined, faFillDrip, faLink, faUnlink, faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, - faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faMap); + faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faMap, faUser); this.initEventListeners(); this.initAuthenticationRouters(); } diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 992c1f600..81c349b26 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { FontAwesomeIcon, FontAwesomeIconProps } from "@fortawesome/react-fontawesome"; import { action, computed, observable, reaction, runInAction, Lambda } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, Opt } from "../../../fields/Doc"; +import { Doc, DocListCast, Opt, Field } from "../../../fields/Doc"; import { BoolCast, Cast, StrCast, NumCast } from "../../../fields/Types"; import AntimodeMenu from "../AntimodeMenu"; import "./CollectionMenu.scss"; @@ -24,6 +24,7 @@ import { Document } from "../../../fields/documentSchemas"; import { SelectionManager } from "../../util/SelectionManager"; import { DocumentView } from "../nodes/DocumentView"; import { ColorState } from "react-color"; +import { ObjectField } from "../../../fields/ObjectField"; @observer export default class CollectionMenu extends AntimodeMenu { @@ -72,42 +73,48 @@ export class CollectionViewBaseChrome extends React.Component source.length && (this.target.childLayoutTemplate = Doc.getDocTemplate(source?.[0]))), initialize: emptyFunction, }; _narrativeCommand = { params: ["target", "source"], title: "child click view", - script: "this.target.childClickedOpenTemplateView = getDocTemplate(this.source?.[0])", + script: "self.target.childClickedOpenTemplateView = getDocTemplate(self.source?.[0])", immediate: undoBatch((source: Doc[]) => source.length && (this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0]))), initialize: emptyFunction, }; _contentCommand = { params: ["target", "source"], title: "clear content", - script: "getProto(this.target).data = copyField(this.source);", + script: "getProto(self.target).data = copyField(self.source);", immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List(source)), // Doc.aliasDocs(source), initialize: emptyFunction, }; _viewCommand = { params: ["target"], title: "bookmark view", - script: "this.target._panX = this['target-panX']; this.target._panY = this['target-panY']; this.target._viewScale = this['target-viewScale'];", + script: "self.target._panX = self['target-panX']; self.target._panY = self['target-panY']; self.target._viewScale = self['target-viewScale'];", immediate: undoBatch((source: Doc[]) => { this.target._panX = 0; this.target._panY = 0; this.target._viewScale = 1; }), initialize: (button: Doc) => { button['target-panX'] = this.target._panX; button['target-panY'] = this.target._panY; button['target-viewScale'] = this.target._viewScale; }, }; _clusterCommand = { params: ["target"], title: "fit content", - script: "this.target._fitToBox = !this.target._fitToBox;", + script: "self.target._fitToBox = !self.target._fitToBox;", immediate: undoBatch((source: Doc[]) => this.target._fitToBox = !this.target._fitToBox), initialize: emptyFunction }; _fitContentCommand = { params: ["target"], title: "toggle clusters", - script: "this.target.useClusters = !this.target.useClusters;", + script: "self.target.useClusters = !self.target.useClusters;", immediate: undoBatch((source: Doc[]) => this.target.useClusters = !this.target.useClusters), initialize: emptyFunction }; + _saveFilterCommand = { + params: ["target"], title: "save filter", + script: "self.target._docFilters = copyField(self['target-docFilters']);", + immediate: undoBatch((source: Doc[]) => this.target._docFilters = undefined), + initialize: (button: Doc) => { button['target-docFilters'] = this.target._docFilters instanceof ObjectField && ObjectField.MakeCopy(this.target._docFilters as any as ObjectField); }, + }; - _freeform_commands = [this._viewCommand, this._fitContentCommand, this._clusterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; + _freeform_commands = [this._viewCommand, this._saveFilterCommand, this._fitContentCommand, this._clusterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; _stacking_commands = [this._contentCommand, this._templateCommand]; _masonry_commands = [this._contentCommand, this._templateCommand]; _schema_commands = [this._templateCommand, this._narrativeCommand]; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 8cadafccd..df4576243 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -294,7 +294,7 @@ export class DocumentView extends DocComponent(Docu let stopPropagate = true; let preventDefault = true; !this.props.Document.isBackground && this.props.bringToFront(this.props.Document); - if (this._doubleTap && this.props.renderDepth && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click + if (this._doubleTap && this.props.renderDepth) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click if (!(e.nativeEvent as any).formattedHandled) { if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself const func = () => this.onDoubleClickHandler.script.run({ diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index ab34e13b0..2611d2ca7 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -67,7 +67,7 @@ export class FontIconBox extends DocComponent( background: StrCast(refLayout._backgroundColor, StrCast(refLayout.backgroundColor)), boxShadow: this.layoutDoc.ischecked ? `4px 4px 12px black` : undefined }}> - + {!this.rootDoc.title ? (null) :
{StrCast(this.rootDoc.label, StrCast(this.rootDoc.title).substring(0, 6))}
} ; return !this.layoutDoc.toolTip ? button : diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 966aaa895..6b6fc5da2 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1401,12 +1401,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp onDoubleClick={this.onDoubleClick} >
diff --git a/src/fields/util.ts b/src/fields/util.ts index e0b1843a1..47da7d7bf 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -77,10 +77,10 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number if (writeToDoc) { if (value === undefined) { - delete target.__fieldKeys[prop]; + target.__fieldKeys && (delete target.__fieldKeys[prop]); delete target.__fields[prop]; } else { - target.__fieldKeys[prop] = true; + target.__fieldKeys && (target.__fieldKeys[prop] = true); target.__fields[prop] = value; } //if (typeof value === "object" && !(value instanceof ObjectField)) debugger; -- cgit v1.2.3-70-g09d2 From 5fb6e6348b73c81c4f7900f819fc55fcffe2f661 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 26 Jul 2020 10:15:59 -0400 Subject: fixed copyField() to copy non-ObjectFields. fixed docFilters button to work with no filter --- src/client/views/collections/CollectionMenu.tsx | 2 +- src/fields/Doc.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/fields') diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 81c349b26..24be69050 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -111,7 +111,7 @@ export class CollectionViewBaseChrome extends React.Component this.target._docFilters = undefined), - initialize: (button: Doc) => { button['target-docFilters'] = this.target._docFilters instanceof ObjectField && ObjectField.MakeCopy(this.target._docFilters as any as ObjectField); }, + initialize: (button: Doc) => { button['target-docFilters'] = this.target._docFilters instanceof ObjectField ? ObjectField.MakeCopy(this.target._docFilters as any as ObjectField) : ""; }, }; _freeform_commands = [this._viewCommand, this._saveFilterCommand, this._fitContentCommand, this._clusterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 3ea53a9e0..87acb2ea7 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1235,7 +1235,7 @@ Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); }); Scripting.addGlobal(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); }); Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); }); Scripting.addGlobal(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); }); -Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); }); +Scripting.addGlobal(function copyField(field: any) { return field instanceof ObjectField ? ObjectField.MakeCopy(field) : field; }); Scripting.addGlobal(function aliasDocs(field: any) { return Doc.aliasDocs(field); }); Scripting.addGlobal(function docList(field: any) { return DocListCast(field); }); Scripting.addGlobal(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); }); -- cgit v1.2.3-70-g09d2 From 72778bb3de8cb53c7303f6575f839314c6c331b3 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 26 Jul 2020 10:55:41 -0400 Subject: fixes for retrievin heiht/width syms when ACLs are private. --- src/fields/Schema.ts | 2 +- src/fields/util.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/fields') diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts index c6a8d6ae9..23ac50f74 100644 --- a/src/fields/Schema.ts +++ b/src/fields/Schema.ts @@ -52,7 +52,7 @@ export function makeInterface(...schemas: T): InterfaceFu return field; }, set(target: any, prop, value, receiver) { - receiver.doc[prop] = value; + receiver.doc && (receiver.doc[prop] = value); // receiver.doc may be undefined as the result of a change in ACLs return true; } }); diff --git a/src/fields/util.ts b/src/fields/util.ts index 47da7d7bf..a62795e64 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 } from "./Doc"; +import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, CachedUpdates, DataSym, DocListCast, AclAdmin, FieldsSym, HeightSym, WidthSym } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; @@ -9,6 +9,7 @@ import { Parent, OnUpdate, Update, Id, SelfProxy, Self, HandleUpdate } from "./F import { DocServer } from "../client/DocServer"; import { ComputedField } from "./ScriptField"; import { ScriptCast, StrCast } from "./Types"; +import { returnZero } from "../Utils"; function _readOnlySetter(): never { @@ -246,7 +247,7 @@ export function getter(target: any, in_prop: string | symbol | number, receiver: if (in_prop === FieldsSym || in_prop === Id || in_prop === HandleUpdate || in_prop === CachedUpdates) return target.__fields[prop] || target[prop]; if (in_prop === AclSym) return _overrideAcl ? undefined : target[AclSym]; - if (GetEffectiveAcl(target) === AclPrivate && !_overrideAcl) return undefined; + if (GetEffectiveAcl(target) === AclPrivate && !_overrideAcl) return prop === HeightSym || prop === WidthSym ? returnZero : undefined; if (prop === LayoutSym) { return target.__LAYOUT__; } -- cgit v1.2.3-70-g09d2 From 622ba99eff99f9af2e03c3a5f8a47e9ce95cdf7e Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 27 Jul 2020 23:46:22 -0400 Subject: removed pdf menu pin button. tried to improve performance by computin links in document view. --- src/client/views/DocumentButtonBar.tsx | 4 ++-- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/nodes/DocumentLinksButton.tsx | 24 +++++++++++++----------- src/client/views/nodes/DocumentView.tsx | 15 +++++++++------ src/client/views/pdf/PDFMenu.tsx | 7 ------- src/fields/documentSchemas.ts | 1 + 6 files changed, 26 insertions(+), 27 deletions(-) (limited to 'src/fields') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index c99034d81..6b85616c2 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -276,10 +276,10 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV const considerPush = isText && this.considerGoogleDocsPush; return
- +
{DocumentLinksButton.StartLink ?
- +
: null}
{this.templateButton} diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 9a4df926c..4257ec3c9 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -88,7 +88,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse(); var [sptX, sptY] = transform.transformPoint(0, 0); let [bptX, bptY] = transform.transformPoint(documentView.props.PanelWidth(), documentView.props.PanelHeight()); - if (documentView.props.Document.type === DocumentType.LINK) { + if (StrCast(Doc.Layout(documentView.props.Document).layout).includes("LinkAnchorBox")) { const docuBox = documentView.ContentDiv.getElementsByClassName("linkAnchorBox-cont"); if (docuBox.length) { const rect = docuBox[0].getBoundingClientRect(); diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index c9d23ff3a..ddfa77967 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -1,19 +1,19 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Tooltip } from "@material-ui/core"; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast } from "../../../fields/Doc"; -import { emptyFunction, setupMoveUpEvents, returnFalse } from "../../../Utils"; +import { Doc } from "../../../fields/Doc"; +import { TraceMobx } from "../../../fields/util"; +import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils"; +import { DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; -import { UndoManager, undoBatch } from "../../util/UndoManager"; +import { LinkManager } from "../../util/LinkManager"; +import { undoBatch, UndoManager } from "../../util/UndoManager"; import './DocumentLinksButton.scss'; import { DocumentView } from "./DocumentView"; -import React = require("react"); -import { DocUtils } from "../../documents/Documents"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { LinkDocPreview } from "./LinkDocPreview"; -import { TaskCompletionBox } from "./TaskCompletedBox"; import { LinkDescriptionPopup } from "./LinkDescriptionPopup"; -import { LinkManager } from "../../util/LinkManager"; -import { Tooltip } from "@material-ui/core"; +import { TaskCompletionBox } from "./TaskCompletedBox"; +import React = require("react"); const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -24,6 +24,7 @@ interface DocumentLinksButtonProps { AlwaysOn?: boolean; InMenu?: boolean; StartLink?: boolean; + links: Doc[]; } @observer export class DocumentLinksButton extends React.Component { @@ -158,7 +159,8 @@ export class DocumentLinksButton extends React.Component(Docu const alias = Doc.MakeAlias(this.props.Document); DocUtils.makeCustomViewClicked(alias, undefined, "onClick"); this.props.addDocTab(alias, "onRight"); - } else if (this.props.Document.links && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) { - DocListCast(this.props.Document.links).length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey); + } else if (this.allLinks && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) { + this.allLinks.length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey); } else { if ((this.layoutDoc.onDragStart || (this.props.Document.rootDocument)) && !(e.ctrlKey || e.button > 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTEmplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template @@ -683,7 +683,7 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action makeIntoPortal = async () => { - const portalLink = DocListCast(this.Document.links).find(d => d.anchor1 === this.props.Document); + const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document); if (!portalLink) { const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), title: StrCast(this.props.Document.title) + ".portal" }); DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to"); @@ -1038,6 +1038,7 @@ export class DocumentView extends DocComponent(Docu return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false; } childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling()); + @computed.struct get linkOffset() { return [-15, 0]; } @computed get contents() { TraceMobx(); return (
@@ -1081,7 +1082,7 @@ export class DocumentView extends DocComponent(Docu {this.layoutDoc.hideAllLinks ? (null) : this.allAnchors} {/* {this.allAnchors} */} {this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.isLinkButton || this.layoutDoc.hideLinkButton || this.props.dontRegisterView ? (null) : - } + }
); } @@ -1104,13 +1105,15 @@ export class DocumentView extends DocComponent(Docu anchorPanelWidth = () => this.props.PanelWidth() || 1; anchorPanelHeight = () => this.props.PanelHeight() || 1; - @computed get allAnchors() { + @computed.struct get directLinks() { return LinkManager.Instance.getAllDirectLinks(this.Document); } + @computed.struct get allLinks() { return DocListCast(this.Document.links); } + @computed.struct get allAnchors() { TraceMobx(); if (this.props.LayoutTemplateString?.includes("LinkAnchorBox")) return null; return (this.props.treeViewDoc && this.props.LayoutTemplateString) || // render nothing for: tree view anchor dots this.layoutDoc.presBox || // presentationbox nodes this.props.dontRegisterView ? (null) : // view that are not registered - DocUtils.FilterDocs(LinkManager.Instance.getAllDirectLinks(this.Document), this.props.docFilters(), []).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) => + DocUtils.FilterDocs(this.directLinks, this.props.docFilters(), []).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) => { - this.Pinned = !this.Pinned; - !this.Pinned && (this.Highlighting = false); - }); - @action highlightClicked = (e: React.MouseEvent) => { if (!this.Highlight(this.highlightColor) && this.Pinned) { @@ -161,8 +156,6 @@ export default class PDFMenu extends AntimodeMenu { this.highlighter, , - , ] : [ , diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index ddffb56c3..8cf8f47b7 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -90,6 +90,7 @@ export const documentSchema = createSchema({ followLinkLocation: "string",// flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab, ) hideLinkButton: "boolean", // whether the blue link counter button should be hidden hideAllLinks: "boolean", // whether all individual blue anchor dots should be hidden + linkDisplay: "boolean", // whether a link connection should be shown between link anchor endpoints. isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations isLinkButton: "boolean", // whether document functions as a link follow button to follow the first link on the document when clicked isBackground: "boolean", // whether document is a background element and ignores input events (can only select with marquee) -- cgit v1.2.3-70-g09d2 From 7693ca298392cb0551032b83b688366b1956bdd6 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 28 Jul 2020 10:52:02 -0400 Subject: cleaned up some font stuff. cleaned up DocumentView a bit --- src/client/documents/Documents.ts | 1 + src/client/util/SharingManager.tsx | 5 +- src/client/views/DocComponent.tsx | 6 +- src/client/views/GestureOverlay.tsx | 2 +- src/client/views/MainView.tsx | 38 +- src/client/views/Touchable.tsx | 2 +- .../views/collections/CollectionLinearView.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 8 +- src/client/views/collections/CollectionView.tsx | 7 +- .../CollectionFreeFormLayoutEngines.tsx | 24 -- .../collectionFreeForm/CollectionFreeFormView.tsx | 5 - src/client/views/nodes/ComparisonBox.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 409 ++++----------------- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/ScriptingBox.tsx | 2 +- src/fields/Doc.ts | 5 + 16 files changed, 110 insertions(+), 410 deletions(-) (limited to 'src/fields') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a1c66c2fe..2cd781a53 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1053,6 +1053,7 @@ export namespace DocUtils { } }); batch.end(); + return doc; } export function findTemplate(templateName: string, type: string, signature: string) { let docLayoutTemplate: Opt; diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 452a58d21..0d8b33fbe 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -8,7 +8,6 @@ import * as RequestPromise from "request-promise"; import { Utils } from "../../Utils"; import "./SharingManager.scss"; import { observer } from "mobx-react"; -import { library } from '@fortawesome/fontawesome-svg-core'; import * as fa from '@fortawesome/free-solid-svg-icons'; import { DocumentView } from "../views/nodes/DocumentView"; import { SelectionManager } from "./SelectionManager"; @@ -23,8 +22,6 @@ import { List } from "../../fields/List"; import { distributeAcls, SharingPermissions } from "../../fields/util"; import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox"; -library.add(fa.faCopy, fa.faTimes); - export interface User { email: string; userDocumentId: string; @@ -447,7 +444,7 @@ export default class SharingManager extends React.Component<{}> {

Share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))}

- +
{this.targetDoc?.author !== Doc.CurrentUserEmail ? null : diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 9fd406407..4c82149e2 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -26,7 +26,7 @@ export function DocComponent

(schemaCtor: (doc: D // This is the data part of a document -- ie, the data that is constant across all views of the document @computed get dataDoc() { return this.props.Document[DataSym] as Doc; } - protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; + protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; } return Component; } @@ -59,7 +59,7 @@ export function ViewBoxBaseComponent

(schemaCtor: lookupField = (field: string) => ScriptCast(this.layoutDoc.lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field, container: this.props.ContainingCollectionDoc }).result; active = (outsideReaction?: boolean) => !this.props.Document.isBackground && (this.props.rootSelected(outsideReaction) || this.props.isSelected(outsideReaction) || this.props.renderDepth === 0 || this.layoutDoc.forceActive);// && !Doc.SelectedTool(); // bcz: inking state shouldn't affect static tools - protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; + protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; } return Component; } @@ -114,7 +114,7 @@ export function ViewBoxAnnotatableComponent

= new Map(); private _holdTimer: NodeJS.Timeout | undefined; - protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; + protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; constructor(props: Readonly<{}>) { super(props); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 9fad612ee..33cd43678 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,18 +1,6 @@ import { library } from '@fortawesome/fontawesome-svg-core'; - -import { - faHireAHelper -} from '@fortawesome/free-brands-svg-icons'; -import { - faTasks, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus, - faTerminal, faToggleOn, faFile as fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard, - faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, - faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, - faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTimesCircle, - faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight, - faHeading, faRulerCombined, faFillDrip, faLink, faUnlink, faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, - faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faMap, faUser -} from '@fortawesome/free-solid-svg-icons'; +import { faHireAHelper } from '@fortawesome/free-brands-svg-icons'; +import * as fa from '@fortawesome/free-solid-svg-icons'; import { ANTIMODEMENU_HEIGHT } from './globalCssVariables.scss'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; @@ -146,14 +134,20 @@ export class MainView extends React.Component { } } - library.add(faTasks, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus, - faTerminal, faToggleOn, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faTimesCircle, faWindowMaximize, faAddressCard, fileSolid, - faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, - faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, - faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faTrashAlt, faAngleRight, faBell, - faThumbtack, faTree, faTv, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faArrowsAlt, faQuoteLeft, faSortAmountDown, faAlignLeft, faAlignCenter, faAlignRight, - faHeading, faRulerCombined, faFillDrip, faLink, faUnlink, faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSuperscript, faSubscript, faIndent, faEyeDropper, - faPaintRoller, faBars, faBrush, faShapes, faEllipsisH, faHandPaper, faMap, faUser, faHireAHelper); + library.add(fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, + fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, + fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard, + fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight, + fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow, + fa.faSearch, fa.faFileDownload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft, + fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faClipboard, + fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt, + fa.faFileAudio, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer, + fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, + fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, + fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined, + fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faChevronLeft, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript, + fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper); this.initEventListeners(); this.initAuthenticationRouters(); } diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx index c4cae7e8d..bb9e108cb 100644 --- a/src/client/views/Touchable.tsx +++ b/src/client/views/Touchable.tsx @@ -12,7 +12,7 @@ export abstract class Touchable extends React.Component { private holdEndDisposer?: InteractionUtils.MultiTouchEventDisposer; - protected abstract multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; + protected abstract _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; protected _touchDrag: boolean = false; protected prevPoints: Map = new Map(); diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index 407524353..3b31947f7 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -176,7 +176,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { }} onPointerDown={e => e.stopPropagation()} > - Creating link from: {DocumentLinksButton.StartLink.title} + Creating link from: {DocumentLinksButton.StartLink.props.Document.title}

{LinkDescriptionPopup.showDescriptions ? "Turn off description pop-up" : diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index d9d9c0eb8..99acfdcc2 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -55,17 +55,17 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: class CollectionSubView extends DocComponent(schemaCtor) { private dropDisposer?: DragManager.DragDropDisposer; private gestureDisposer?: GestureUtils.GestureEventDisposer; - protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; + protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; protected _mainCont?: HTMLDivElement; protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this.dropDisposer?.(); this.gestureDisposer?.(); - this.multiTouchDisposer?.(); + this._multiTouchDisposer?.(); if (ele) { this._mainCont = ele; this.dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc, this.onInternalPreDrop.bind(this)); this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this)); - this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this)); + this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this)); } } protected CreateDropTarget(ele: HTMLDivElement) { //used in schema view @@ -74,7 +74,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: componentWillUnmount() { this.gestureDisposer?.(); - this.multiTouchDisposer?.(); + this._multiTouchDisposer?.(); } @computed get dataDoc() { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 2c17254ed..e7a504e61 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -27,12 +27,10 @@ import { InteractionUtils } from '../../util/InteractionUtils'; import { UndoManager } from '../../util/UndoManager'; import { ContextMenu } from "../ContextMenu"; import { FieldView, FieldViewProps } from '../nodes/FieldView'; -import { ScriptBox } from '../ScriptBox'; import { Touchable } from '../Touchable'; import { CollectionCarousel3DView } from './CollectionCarousel3DView'; import { CollectionCarouselView } from './CollectionCarouselView'; import { CollectionDockingView } from "./CollectionDockingView"; -import { AddCustomFreeFormLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import { CollectionGridView } from './collectionGrid/CollectionGridView'; import { CollectionLinearView } from './CollectionLinearView'; @@ -105,7 +103,7 @@ export class CollectionView extends Touchable([ [AclPrivate, SharingPermissions.None], @@ -289,9 +287,6 @@ export class CollectionView extends Touchable func(CollectionViewType.Time), icon: "columns" }); subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" }); subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" }); - if (addExtras && this.props.Document._viewType === CollectionViewType.Freeform) { - subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) }); - } addExtras && subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" }); const existingVm = ContextMenu.Instance.findByDescription(category); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 183ed3119..b00074cc6 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -434,27 +434,3 @@ function normalizeResults( payload: gname.payload }))); } - -export function AddCustomFreeFormLayout(doc: Doc, dataKey: string): () => void { - return () => { - const addOverlay = (key: "arrangeScript" | "arrangeInit", options: OverlayElementOptions, params?: Record, requiredType?: string) => { - let overlayDisposer: () => void = emptyFunction; // filled in below after we have a reference to the scriptingBox - const scriptField = Cast(doc[key], ScriptField); - const scriptingBox = overlayDisposer()} // don't get rid of the function wrapper-- we don't want to use the current value of overlayDiposer, but the one set below - onSave={(text, onError) => { - const script = CompileScript(text, { params, requiredType, typecheck: false }); - if (!script.compiled) { - onError(script.errors.map(error => error.messageText).join("\n")); - } else { - doc[key] = new ScriptField(script); - overlayDisposer(); - } - }} />; - overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, options); - }; - addOverlay("arrangeInit", { x: 400, y: 100, width: 400, height: 300, title: "Layout Initialization" }, { collection: "Doc", docs: "Doc[]" }, undefined); - addOverlay("arrangeScript", { x: 400, y: 500, width: 400, height: 300, title: "Layout Script" }, { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}"); - }; -} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 792f3af5f..57336131a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -57,7 +57,6 @@ export const panZoomSchema = createSchema({ currentTimecode: "number", displayTimecode: "number", currentFrame: "number", - arrangeScript: ScriptField, arrangeInit: ScriptField, useClusters: "boolean", fitToBox: "boolean", @@ -1005,10 +1004,6 @@ export class CollectionFreeFormView extends CollectionSubView(ComparisonDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ComparisonBox, fieldKey); } - protected multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; + protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined]; @observable _animating = ""; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 3b4dc3741..c47edefd6 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,31 +1,31 @@ -import { library } from '@fortawesome/fontawesome-svg-core'; -import * as fa from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, HeightSym, Opt, WidthSym, DataSym, AclPrivate, AclEdit, AclAdmin } from "../../../fields/Doc"; +import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { Document } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { listSpec } from "../../../fields/Schema"; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../fields/Types"; -import { TraceMobx, GetEffectiveAcl, SharingPermissions } from '../../../fields/util'; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; +import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; +import { MobileInterface } from '../../../mobile/MobileInterface'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; -import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils, emptyPath } from "../../../Utils"; +import { emptyFunction, emptyPath, OmitKeys, returnOne, returnTransparent, Utils } from "../../../Utils"; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { ClientRecommender } from '../../ClientRecommender'; import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from "../../util/DocumentManager"; -import { SnappingManager } from '../../util/SnappingManager'; import { DragManager, dropActionType } from "../../util/DragManager"; import { InteractionUtils } from '../../util/InteractionUtils'; +import { LinkManager } from '../../util/LinkManager'; import { Scripting } from '../../util/Scripting'; import { SearchUtil } from '../../util/SearchUtil'; import { SelectionManager } from "../../util/SelectionManager"; import SharingManager from '../../util/SharingManager'; +import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from "../../util/Transform"; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { CollectionView, CollectionViewType } from '../collections/CollectionView'; @@ -35,20 +35,13 @@ import { DocComponent } from "../DocComponent"; import { EditableView } from '../EditableView'; import { KeyphraseQueryView } from '../KeyphraseQueryView'; import { DocumentContentsView } from "./DocumentContentsView"; +import { DocumentLinksButton } from './DocumentLinksButton'; import "./DocumentView.scss"; import { LinkAnchorBox } from './LinkAnchorBox'; +import { LinkDescriptionPopup } from './LinkDescriptionPopup'; import { RadialMenu } from './RadialMenu'; -import React = require("react"); -import { DocumentLinksButton } from './DocumentLinksButton'; -import { MobileInterface } from '../../../mobile/MobileInterface'; import { TaskCompletionBox } from './TaskCompletedBox'; -import { LinkDescriptionPopup } from './LinkDescriptionPopup'; -import { LinkManager } from '../../util/LinkManager'; -import CollectionMenu from '../collections/CollectionMenu'; - -library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight, - fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale, - fa.faCopy, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard, fa.faQuestion); +import React = require("react"); export type DocFocusFunc = () => boolean; @@ -105,24 +98,25 @@ export interface DocumentViewProps { @observer export class DocumentView extends DocComponent(Document) { + @observable _animateScalingTo = 0; private _downX: number = 0; private _downY: number = 0; + private _firstX: number = -1; + private _firstY: number = -1; private _lastTap: number = 0; private _doubleTap = false; private _mainCont = React.createRef(); private _dropDisposer?: DragManager.DragDropDisposer; private _showKPQuery: boolean = false; private _queries: string = ""; - private _gestureEventDisposer?: GestureUtils.GestureEventDisposer; private _titleRef = React.createRef(); + private _gestureEventDisposer?: GestureUtils.GestureEventDisposer; + private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer; + protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; - protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; - private holdDisposer?: InteractionUtils.MultiTouchEventDisposer; - - public get title() { return this.props.Document.title; } public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive public get ContentDiv() { return this._mainCont.current; } - get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); } + private get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); } @computed get topMost() { return this.props.renderDepth === 0; } @computed get freezeDimensions() { return this.props.FreezeDimensions; } @computed get nativeWidth() { return NumCast(this.layoutDoc._nativeWidth, this.props.NativeWidth() || (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); } @@ -136,9 +130,6 @@ export class DocumentView extends DocComponent(Docu onClickFunc = () => this.onClickHandler; onDoubleClickFunc = () => this.onDoubleClickHandler; - private _firstX: number = -1; - private _firstY: number = -1; - handle1PointerHoldStart = (e: Event, me: InteractionUtils.MultiTouchEvent): any => { this.removeMoveListeners(); this.removeEndListeners(); @@ -152,11 +143,9 @@ export class DocumentView extends DocComponent(Docu this._firstX = pt.pageX; this._firstY = pt.pageY; } - } handle1PointerHoldMove = (e: Event, me: InteractionUtils.MultiTouchEvent): void => { - const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1]; if (this._firstX === -1 || this._firstY === -1) { @@ -200,7 +189,7 @@ export class DocumentView extends DocComponent(Docu componentDidMount() { this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document)); this._mainCont.current && (this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this))); - this._mainCont.current && (this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this))); + this._mainCont.current && (this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this))); // this._mainCont.current && (this.holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this))); if (!this.props.dontRegisterView) { @@ -212,13 +201,13 @@ export class DocumentView extends DocComponent(Docu componentDidUpdate() { this._dropDisposer?.(); this._gestureEventDisposer?.(); - this.multiTouchDisposer?.(); - this.holdDisposer?.(); + this._multiTouchDisposer?.(); + this._holdDisposer?.(); if (this._mainCont.current) { this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this), this.props.Document); this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this)); - this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this)); - this.holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this)); + this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this)); + this._holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this)); } } @@ -226,8 +215,8 @@ export class DocumentView extends DocComponent(Docu componentWillUnmount() { this._dropDisposer?.(); this._gestureEventDisposer?.(); - this.multiTouchDisposer?.(); - this.holdDisposer?.(); + this._multiTouchDisposer?.(); + this._holdDisposer?.(); Doc.UnBrushDoc(this.props.Document); if (!this.props.dontRegisterView) { const index = DocumentManager.Instance.DocumentViews.indexOf(this); @@ -242,7 +231,7 @@ export class DocumentView extends DocComponent(Docu dragData.offset = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); dragData.dropAction = dropAction; dragData.removeDocument = this.props.removeDocument; - dragData.moveDocument = this.props.moveDocument;// this.layoutDoc.onDragStart ? undefined : this.props.moveDocument; + dragData.moveDocument = this.props.moveDocument; dragData.dragDivName = this.props.dragDivName; dragData.treeViewDoc = this.props.treeViewDoc; DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.layoutDoc.onDragStart }); @@ -254,7 +243,6 @@ export class DocumentView extends DocComponent(Docu const de = new DragManager.DocumentDragData([topDoc]); de.dragDivName = topDocView.props.dragDivName; de.moveDocument = topDocView.props.moveDocument; - undoBatch(action(() => topDoc.z = topDoc.z ? 0 : 1))(); setTimeout(() => { const newDocView = DocumentManager.Instance.getDocumentView(topDoc); if (newDocView) { @@ -263,6 +251,7 @@ export class DocumentView extends DocComponent(Docu DragManager.StartDocumentDrag([contentDiv], de, x, y, { offsetX: x - xf.left, offsetY: y - xf.top, hideSource: true }); } }, 0); + UndoManager.RunInBatch(action(() => topDoc.z = topDoc.z ? 0 : 1), "float"); } onKeyDown = (e: React.KeyboardEvent) => { @@ -301,41 +290,39 @@ export class DocumentView extends DocComponent(Docu const func = () => this.onDoubleClickHandler.script.run({ this: this.layoutDoc, self: this.rootDoc, - thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey + thisContainer: this.props.ContainingCollectionDoc, + shiftKey: e.shiftKey }, console.log); func(); } else { UndoManager.RunInBatch(() => { + let fullScreenDoc = this.props.Document; if (StrCast(this.props.Document.layoutKey) !== "layout_fullScreen" && this.props.Document.layout_fullScreen) { - const fullScreenAlias = Doc.MakeAlias(this.props.Document); - fullScreenAlias.layoutKey = "layout_fullScreen"; - this.props.addDocTab(fullScreenAlias, "inTab"); - } else { - this.props.addDocTab(this.props.Document, "inTab"); + fullScreenDoc = Doc.MakeAlias(this.props.Document); + fullScreenDoc.layoutKey = "layout_fullScreen"; } + this.props.addDocTab(fullScreenDoc, "inTab"); }, "double tap"); SelectionManager.DeselectAll(); Doc.UnBrushDoc(this.props.Document); } } } else if (this.onClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself - //SelectionManager.DeselectAll(); const func = () => this.onClickHandler.script.run({ this: this.layoutDoc, self: this.rootDoc, - thisContainer: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey + thisContainer: this.props.ContainingCollectionDoc, + shiftKey: e.shiftKey }, console.log); if (this.props.Document !== Doc.UserDoc()["dockedBtn-undo"] && this.props.Document !== Doc.UserDoc()["dockedBtn-redo"]) { UndoManager.RunInBatch(func, "on click"); } else func(); } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself - const alias = Doc.MakeAlias(this.props.Document); - DocUtils.makeCustomViewClicked(alias, undefined, "onClick"); - this.props.addDocTab(alias, "onRight"); + this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "onRight"); } else if (this.allLinks && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) { this.allLinks.length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey); } else { - if ((this.layoutDoc.onDragStart || (this.props.Document.rootDocument)) && !(e.ctrlKey || e.button > 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTEmplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part + if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template } else { SelectionManager.SelectDoc(this, e.ctrlKey || e.shiftKey); @@ -407,7 +394,6 @@ export class DocumentView extends DocComponent(Docu } e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers e.preventDefault(); - } } @@ -445,12 +431,6 @@ export class DocumentView extends DocComponent(Docu const oldPoint2 = this.prevPoints.get(pt2.identifier); const pinching = InteractionUtils.Pinning(pt1, pt2, oldPoint1!, oldPoint2!); if (pinching !== 0 && oldPoint1 && oldPoint2) { - // let dX = (Math.min(pt1.clientX, pt2.clientX) - Math.min(oldPoint1.clientX, oldPoint2.clientX)); - // let dY = (Math.min(pt1.clientY, pt2.clientY) - Math.min(oldPoint1.clientY, oldPoint2.clientY)); - // let dX = Math.sign(Math.abs(pt1.clientX - oldPoint1.clientX) - Math.abs(pt2.clientX - oldPoint2.clientX)); - // let dY = Math.sign(Math.abs(pt1.clientY - oldPoint1.clientY) - Math.abs(pt2.clientY - oldPoint2.clientY)); - // let dW = -dX; - // let dH = -dY; const dW = (Math.abs(pt1.clientX - pt2.clientX) - Math.abs(oldPoint1.clientX - oldPoint2.clientX)); const dH = (Math.abs(pt1.clientY - pt2.clientY) - Math.abs(oldPoint1.clientY - oldPoint2.clientY)); const dX = -1 * Math.sign(dW); @@ -533,7 +513,6 @@ export class DocumentView extends DocComponent(Docu } onPointerMove = (e: PointerEvent): void => { - if ((e as any).formattedHandled) { e.stopPropagation(); return; } if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) return; if (e.cancelBubble && this.active) { @@ -554,17 +533,15 @@ export class DocumentView extends DocComponent(Docu onPointerUp = (e: PointerEvent): void => { this.cleanUpInteractions(); + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); if (this.onPointerUpHandler?.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { this.onPointerUpHandler.script.run({ self: this.rootDoc, this: this.layoutDoc }, console.log); - document.removeEventListener("pointerup", this.onPointerUp); - return; + } else { + this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2); + this._lastTap = Date.now(); } - - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2); - this._lastTap = Date.now(); } onGesture = (e: Event, ge: GestureUtils.GestureEvent) => { @@ -586,54 +563,20 @@ export class DocumentView extends DocComponent(Docu } } - @undoBatch - togglePushpin = () => { + @undoBatch @action + toggleFollowLink = (location: Opt, zoom: boolean, setPushpin: boolean): void => { this.Document.ignoreClick = false; - if (this.Document.isLinkButton || this.Document.isPushpin || this.onClickHandler || this.Document.ignoreClick) { - this.Document.isPushpin = this.Document.isLinkButton = false; - this.Document.onClick = this.layoutDoc.onClick = undefined; + this.Document.isLinkButton = !this.Document.isLinkButton; + setPushpin && (this.Document.isPushpin = this.Document.isLinkButton); + if (this.Document.isLinkButton && !this.onClickHandler) { + this.Document.followLinkZoom = zoom; + this.Document.followLinkLocation = location; } else { - this.Document.isPushpin = this.Document.isLinkButton = true; - this.Document.followLinkZoom = false; - this.Document.followLinkLocation = undefined; - } - } - - @undoBatch - toggleLinkButtonBehavior = (): void => { - this.Document.ignoreClick = false; - if (this.Document.isLinkButton || this.onClickHandler || this.Document.ignoreClick) { - this.Document.isLinkButton = false; this.Document.onClick = this.layoutDoc.onClick = undefined; - } else { - this.Document.isLinkButton = true; - this.Document.followLinkZoom = false; - this.Document.followLinkLocation = undefined; - } - } - - @undoBatch - toggleFollowInPlace = (): void => { - this.Document.ignoreClick = false; - this.Document.isLinkButton = !this.Document.isLinkButton; - if (this.Document.isLinkButton) { - this.Document.followLinkZoom = true; - this.Document.followLinkLocation = "inPlace"; } } - @undoBatch - toggleFollowOnRight = (): void => { - this.Document.ignoreClick = false; - this.Document.isLinkButton = !this.Document.isLinkButton; - if (this.Document.isLinkButton) { - this.Document.followLinkZoom = false; - this.Document.followLinkLocation = "onRight"; - } - } - - @undoBatch - @action + @undoBatch @action drop = async (e: Event, de: DragManager.DropEvent) => { if (this.props.Document === Doc.UserDoc().activeWorkspace) { alert("linking to document tabs not yet supported. Drop link on document content."); @@ -663,11 +606,10 @@ export class DocumentView extends DocComponent(Docu } if (de.complete.linkDragData) { e.stopPropagation(); - if (de.complete.linkDragData.linkSourceDocument !== this.props.Document) { - const linkDoc = DocUtils.MakeLink({ doc: de.complete.linkDragData.linkSourceDocument }, - { doc: this.props.Document }, `link`); - de.complete.linkDragData.linkSourceDocument !== this.props.Document && - (de.complete.linkDragData.linkDocument = linkDoc); // TODODO this is where in text links get passed + const linkSource = de.complete.linkDragData.linkSourceDocument; + if (linkSource !== this.props.Document) { + const linkDoc = DocUtils.MakeLink({ doc: linkSource }, { doc: this.props.Document }, `link`); + linkSource !== this.props.Document && (de.complete.linkDragData.linkDocument = linkDoc); // TODODO this is where in text links get passed linkDoc && makeLink(linkDoc); } @@ -680,6 +622,12 @@ export class DocumentView extends DocComponent(Docu Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.PanelWidth(), this.props.PanelHeight()); } + @undoBatch + @action + toggleLockPosition = (): void => { + this.Document.lockedPosition = this.Document.lockedPosition ? undefined : true; + } + @undoBatch @action makeIntoPortal = async () => { @@ -694,9 +642,9 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action - toggleBackground = (temporary: boolean): void => { - this.Document._overflow = temporary ? "visible" : "hidden"; - this.Document.isBackground = !temporary ? !this.Document.isBackground : (this.Document.isBackground ? undefined : true); + toggleBackground = () => { + this.Document.isBackground = (this.Document.isBackground ? undefined : true); + this.Document._overflow = this.Document.isBackground ? "visible" : undefined; if (this.Document.isBackground) { this.props.bringToFront(this.props.Document, true); this.props.Document[DataSym][Doc.LayoutFieldKey(this.Document) + "-nativeWidth"] = this.Document[WidthSym](); @@ -704,41 +652,6 @@ export class DocumentView extends DocComponent(Docu } } - @undoBatch - @action - toggleLockPosition = (): void => { - this.Document.lockedPosition = this.Document.lockedPosition ? undefined : true; - } - - @undoBatch - @action - setAcl = (acl: SharingPermissions) => { - this.dataDoc.ACL = this.props.Document.ACL = acl; - DocListCast(this.dataDoc[Doc.LayoutFieldKey(this.dataDoc)]).map(d => { - if (d.author === Doc.CurrentUserEmail) d.ACL = acl; - const data = d[DataSym]; - if (data && data.author === Doc.CurrentUserEmail) data.ACL = acl; - }); - } - - @undoBatch - @action - testAcl = (acl: SharingPermissions) => { - this.dataDoc.author = this.props.Document.author = "ADMIN"; - this.dataDoc.ACL = this.props.Document.ACL = acl; - DocListCast(this.dataDoc[Doc.LayoutFieldKey(this.dataDoc)]).map(d => { - if (d.author === Doc.CurrentUserEmail) { - d.author = "ADMIN"; - d.ACL = acl; - } - const data = d[DataSym]; - if (data && data.author === Doc.CurrentUserEmail) { - data.author = "ADMIN"; - data.ACL = acl; - } - }); - } - @action onContextMenu = async (e: React.MouseEvent | Touch): Promise => { // the touch onContextMenu is button 0, the pointer onContextMenu is button 2 @@ -785,10 +698,10 @@ export class DocumentView extends DocComponent(Docu onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" }); onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "window-restore" }); onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" }); - onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: this.toggleFollowInPlace, icon: "concierge-bell" }); - onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link on Right", event: this.toggleFollowOnRight, icon: "concierge-bell" }); - onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: this.toggleLinkButtonBehavior, icon: "concierge-bell" }); - onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: this.togglePushpin, icon: "snowflake" }); + onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "concierge-bell" }); + !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("onRight", false, false), icon: "concierge-bell" }); + onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "concierge-bell" }); + onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "snowflake" }); onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" }); !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, addDivider: true, subitems: onClicks, icon: "hand-point-right" }); @@ -802,16 +715,7 @@ export class DocumentView extends DocComponent(Docu const more = cm.findByDescription("More..."); const moreItems = more && "subitems" in more ? more.subitems : []; - moreItems.push({ - description: "Download document", icon: "download", event: async () => { - Doc.Zip(this.props.Document); - // const a = document.createElement("a"); - // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`); - // a.href = url; - // a.download = `DocExport-${this.props.Document[Id]}.zip`; - // a.click(); - } - }); + moreItems.push({ description: "Download document", icon: "download", event: async () => Doc.Zip(this.props.Document) }); if (!Doc.UserDoc().noviceMode) { moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" }); moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" }); @@ -836,179 +740,13 @@ export class DocumentView extends DocComponent(Docu helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" }); cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" }); - // const existingAcls = cm.findByDescription("Privacy..."); - // const aclItems: ContextMenuProps[] = existingAcls && "subitems" in existingAcls ? existingAcls.subitems : []; - // aclItems.push({ description: "Make Add Only", event: () => this.setAcl(SharingPermissions.Add), icon: "concierge-bell" }); - // aclItems.push({ description: "Make Read Only", event: () => this.setAcl(SharingPermissions.View), icon: "concierge-bell" }); - // aclItems.push({ description: "Make Private", event: () => this.setAcl(SharingPermissions.None), icon: "concierge-bell" }); - // aclItems.push({ description: "Make Editable", event: () => this.setAcl(SharingPermissions.Edit), icon: "concierge-bell" }); - // aclItems.push({ description: "Test Private", event: () => this.testAcl(SharingPermissions.None), icon: "concierge-bell" }); - // aclItems.push({ description: "Test Readonly", event: () => this.testAcl(SharingPermissions.View), icon: "concierge-bell" }); - // !existingAcls && cm.addItem({ description: "Privacy...", subitems: aclItems, icon: "question" }); - - // cm.addItem({ description: `${getPlaygroundMode() ? "Disable" : "Enable"} playground mode`, event: togglePlaygroundMode, icon: "concierge-bell" }); - - // const recommender_subitems: ContextMenuProps[] = []; - - // recommender_subitems.push({ - // description: "Internal recommendations", - // event: () => this.recommender(), - // icon: "brain" - // }); - - // const ext_recommender_subitems: ContextMenuProps[] = []; - - // ext_recommender_subitems.push({ - // description: "arXiv", - // event: () => this.externalRecommendation("arxiv"), - // icon: "brain" - // }); - // ext_recommender_subitems.push({ - // description: "Bing", - // event: () => this.externalRecommendation("bing"), - // icon: "brain" - // }); - - // recommender_subitems.push({ - // description: "External recommendations", - // subitems: ext_recommender_subitems, - // icon: "brain" - // }); - - - //moreItems.push({ description: "Recommender System", subitems: recommender_subitems, icon: "brain" }); - //moreItems.push({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" }); - //moreItems.push({ description: "Undo Debug Test", event: () => UndoManager.TraceOpenBatches(), icon: "exclamation" }); - - // runInAction(() => { - // const setWriteMode = (mode: DocServer.WriteMode) => { - // DocServer.AclsMode = mode; - // const mode1 = mode; - // const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground; - // DocServer.setFieldWriteMode("x", mode1); - // DocServer.setFieldWriteMode("y", mode1); - // DocServer.setFieldWriteMode("_width", mode1); - // DocServer.setFieldWriteMode("_height", mode1); - - // DocServer.setFieldWriteMode("_panX", mode2); - // DocServer.setFieldWriteMode("_panY", mode2); - // DocServer.setFieldWriteMode("scale", mode2); - // DocServer.setFieldWriteMode("_viewType", mode2); - // }; - // const aclsMenu: ContextMenuProps[] = []; - // aclsMenu.push({ description: "Default (write/read all)", event: () => setWriteMode(DocServer.WriteMode.Default), icon: DocServer.AclsMode === DocServer.WriteMode.Default ? "check" : "exclamation" }); - // aclsMenu.push({ description: "Playground (write own/no read)", event: () => setWriteMode(DocServer.WriteMode.Playground), icon: DocServer.AclsMode === DocServer.WriteMode.Playground ? "check" : "exclamation" }); - // aclsMenu.push({ description: "Live Playground (write own/read others)", event: () => setWriteMode(DocServer.WriteMode.LivePlayground), icon: DocServer.AclsMode === DocServer.WriteMode.LivePlayground ? "check" : "exclamation" }); - // aclsMenu.push({ description: "Live Readonly (no write/read others)", event: () => setWriteMode(DocServer.WriteMode.LiveReadonly), icon: DocServer.AclsMode === DocServer.WriteMode.LiveReadonly ? "check" : "exclamation" }); - // cm.addItem({ description: "Collaboration ...", subitems: aclsMenu, icon: "share" }); - // }); runInAction(() => { if (!this.topMost && !(e instanceof Touch)) { - // DocumentViews should stop propagation of this event - e.stopPropagation(); + e.stopPropagation(); // DocumentViews should stop propagation of this event } cm.displayMenu(e.pageX - 15, e.pageY - 15); !SelectionManager.IsSelected(this, true) && SelectionManager.SelectDoc(this, false); }); - const path = this.props.LibraryPath.reduce((p: string, d: Doc) => p + "/" + (Doc.AreProtosEqual(d, (Doc.UserDoc()["tabs-button-library"] as Doc).sourcePanel as Doc) ? "" : d.title), ""); - const item = ({ - description: `path: ${path}`, event: () => { - if (this.props.LibraryPath !== emptyPath) { - this.props.LibraryPath.map(lp => Doc.GetProto(lp).treeViewOpen = lp.treeViewOpen = true); - Doc.linkFollowHighlight(this.props.Document); - } else { - Doc.AddDocToList(Doc.GetProto(Doc.UserDoc().myCatalog as Doc), "data", this.props.Document[DataSym]); - } - }, icon: "check" - }); - //cm.addItem(item); - } - - recommender = async () => { - if (!ClientRecommender.Instance) new ClientRecommender({ title: "Client Recommender" }); - const documents: Doc[] = []; - const allDocs = await SearchUtil.GetAllDocs(); - // clears internal representation of documents as vectors - ClientRecommender.Instance.reset_docs(); - //ClientRecommender.Instance.arxivrequest("electrons"); - await Promise.all(allDocs.map((doc: Doc) => { - let isMainDoc: boolean = false; - const dataDoc = Doc.GetProto(doc); - if (doc.type === DocumentType.RTF) { - if (dataDoc === Doc.GetProto(this.props.Document)) { - isMainDoc = true; - } - if (!documents.includes(dataDoc)) { - documents.push(dataDoc); - const extdoc = doc.data_ext as Doc; - return ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc, true, "", isMainDoc); - } - } - if (doc.type === DocumentType.IMG) { - if (dataDoc === Doc.GetProto(this.props.Document)) { - isMainDoc = true; - } - if (!documents.includes(dataDoc)) { - documents.push(dataDoc); - const extdoc = doc.data_ext as Doc; - return ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc, true, "", isMainDoc, true); - } - } - })); - const doclist = ClientRecommender.Instance.computeSimilarities("cosine"); - const recDocs: { preview: Doc, score: number }[] = []; - // tslint:disable-next-line: prefer-for-of - for (let i = 0; i < doclist.length; i++) { - recDocs.push({ preview: doclist[i].actualDoc, score: doclist[i].score }); - } - - const data = recDocs.map(unit => { - unit.preview.score = unit.score; - return unit.preview; - }); - - console.log(recDocs.map(doc => doc.score)); - - const title = `Showing ${data.length} recommendations for "${StrCast(this.props.Document.title)}"`; - const recommendations = Docs.Create.RecommendationsDocument(data, { title }); - recommendations.documentIconHeight = 150; - recommendations.sourceDoc = this.props.Document; - recommendations.sourceDocContext = this.props.ContainingCollectionView!.props.Document; - this.props.addDocTab(recommendations, "onRight"); - - // RecommendationsBox.Instance.displayRecommendations(e.pageX + 100, e.pageY); - } - - @action - externalRecommendation = async (api: string) => { - if (!ClientRecommender.Instance) new ClientRecommender({ title: "Client Recommender" }); - ClientRecommender.Instance.reset_docs(); - const doc = Doc.GetDataDoc(this.props.Document); - const extdoc = doc.data_ext as Doc; - const recs_and_kps = await ClientRecommender.Instance.extractText(doc, extdoc ? extdoc : doc, false, api); - let recs: any; - let kps: any; - if (recs_and_kps) { - recs = recs_and_kps.recs; - kps = recs_and_kps.keyterms; - } - else { - console.log("recommender system failed :("); - return; - } - console.log("ibm keyterms: ", kps.toString()); - const headers = [new SchemaHeaderField("title"), new SchemaHeaderField("href")]; - const bodies: Doc[] = []; - const titles = recs.title_vals; - const urls = recs.url_vals; - for (let i = 0; i < 5; i++) { - const body = Docs.Create.FreeformDocument([], { title: titles[i] }); - body.href = urls[i]; - bodies.push(body); - } - this.props.addDocTab(Docs.Create.SchemaDocument(headers, bodies, { title: `Showing External Recommendations for "${StrCast(doc.title)}"` }), "onRight"); - this._showKPQuery = true; - this._queries = kps.toString(); } // does Document set a layout prop @@ -1088,7 +826,7 @@ export class DocumentView extends DocComponent(Docu } // used to decide whether a link anchor view should be created or not. - // if it's a tempoarl link (currently just for Audio), then the audioBox will display the anchor and we don't want to display it here. + // if it's a temporal link (currently just for Audio), then the audioBox will display the anchor and we don't want to display it here. // would be good to generalize this some way. isNonTemporalLink = (linkDoc: Doc) => { const anchor = Cast(Doc.AreProtosEqual(this.props.Document, Cast(linkDoc.anchor1, Doc) as Doc) ? linkDoc.anchor1 : linkDoc.anchor2, Doc) as Doc; @@ -1096,7 +834,6 @@ export class DocumentView extends DocComponent(Docu return anchor.type === DocumentType.AUDIO && NumCast(ept) ? false : true; } - @observable _link: Opt; // see DocumentButtonBar for explanation of how this works makeLink = () => this._link; // pass the link placeholde to child views so they can react to make a specialized anchor. This is essentially a function call to the descendants since the value of the _link variable will immediately get set back to undefined. @@ -1186,7 +923,7 @@ export class DocumentView extends DocComponent(Docu DocUtils.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined); } } - @observable _animateScalingTo = 0; + switchViews = action((custom: boolean, view: string) => { this._animateScalingTo = 0.1; // shrink doc setTimeout(action(() => { @@ -1200,7 +937,7 @@ export class DocumentView extends DocComponent(Docu return (this.Document.isBackground !== undefined || this.isSelected(false)) && ((this.Document.type === DocumentType.COL && this.Document._viewType !== CollectionViewType.Pile) || this.Document.type === DocumentType.IMG) && this.props.renderDepth > 0 && !this.props.treeViewDoc ? -
this.toggleBackground(true)}> +
: (null); @@ -1224,7 +961,8 @@ export class DocumentView extends DocComponent(Docu const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"]; let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc._viewType !== CollectionViewType.Linear && this.props.Document.type !== DocumentType.INK; highlighting = highlighting && this.props.focus !== emptyFunction; // bcz: hack to turn off highlighting onsidebar panel documents. need to flag a document as not highlightable in a more direct way - return
(Docu this.innards} {this.renderLock()}
; - { this._showKPQuery ? : undefined; } } } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 5f689624c..d668d332b 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -66,7 +66,7 @@ const uploadIcons = { @observer export class ImageBox extends ViewBoxAnnotatableComponent(ImageDocument) { - protected multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; + protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); } private _imgRef: React.RefObject = React.createRef(); private _dropDisposer?: DragManager.DragDropDisposer; diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index bc43cd473..1a5edc1d9 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -32,7 +32,7 @@ const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema); export class ScriptingBox extends ViewBoxAnnotatableComponent(ScriptingDocument) { private dropDisposer?: DragManager.DragDropDisposer; - protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined; + protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined; public static LayoutString(fieldStr: string) { return FieldView.LayoutString(ScriptingBox, fieldStr); } private _overlayDisposer?: () => void; private _caretPos = 0; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 87acb2ea7..267defa4b 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -587,6 +587,11 @@ export namespace Doc { } export async function Zip(doc: Doc) { + // const a = document.createElement("a"); + // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`); + // a.href = url; + // a.download = `DocExport-${this.props.Document[Id]}.zip`; + // a.click(); const { clone, map } = await Doc.MakeClone(doc, true); function replacer(key: any, value: any) { if (["cloneOf", "context", "cursors"].includes(key)) return undefined; -- cgit v1.2.3-70-g09d2 From 57c07152904762c790e3a3bb2003d389983990d8 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 29 Jul 2020 09:46:31 -0400 Subject: streamlined settin link follow tarets a bit by addin a follow link draggable UI button --- src/client/views/collections/CollectionMenu.tsx | 19 ++++++++++++++++++- src/fields/ScriptField.ts | 4 ++++ 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'src/fields') diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 47670eb26..3eda2e2f2 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -25,6 +25,7 @@ import { SelectionManager } from "../../util/SelectionManager"; import { DocumentView } from "../nodes/DocumentView"; import { ColorState } from "react-color"; import { ObjectField } from "../../../fields/ObjectField"; +import { ScriptField } from "../../../fields/ScriptField"; @observer export default class CollectionMenu extends AntimodeMenu { @@ -116,6 +117,22 @@ export class CollectionViewBaseChrome extends React.Component { }), initialize: emptyFunction, }; + _openLinkInCommand = { + params: ["target", "container"], title: "link follow target", + script: `{ if (self.container?.length) { + getProto(self.target).linkContainer = self.container[0]; + getProto(self.target).isLinkButton = true; + getProto(self.target).onClick = makeScript("getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])"); + }}`, + immediate: undoBatch((container: Doc[]) => { + if (container.length) { + Doc.GetProto(this.target).linkContainer = container[0]; + Doc.GetProto(this.target).isLinkButton = true; + Doc.GetProto(this.target).onClick = ScriptField.MakeScript("getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])"); + } + }), + initialize: emptyFunction, + }; _viewCommand = { params: ["target"], title: "bookmark view", script: "self.target._panX = self['target-panX']; self.target._panY = self['target-panY']; self.target._viewScale = self['target-viewScale'];", @@ -145,7 +162,7 @@ export class CollectionViewBaseChrome extends React.Component (i <= index && x !== undefined) || p === undefined ? x : p, undefined as any); }, "returns the value at a given index of a list", "(list: any[], index: number)"); +Scripting.addGlobal(function makeScript(script: string) { + return ScriptField.MakeScript(script); +}, "returns the value at a given index of a list", "(list: any[], index: number)"); + export namespace ComputedField { let useComputed = true; export function DisableComputedFields() { -- cgit v1.2.3-70-g09d2