aboutsummaryrefslogtreecommitdiff
path: root/src/client/documents/Documents.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/documents/Documents.ts')
-rw-r--r--src/client/documents/Documents.ts90
1 files changed, 64 insertions, 26 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2d02742c5..2d8a897a5 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,7 +1,8 @@
import { runInAction } from "mobx";
import { basename, extname } from "path";
import { DateField } from "../../fields/DateField";
-import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, AclAddonly } from "../../fields/Doc";
+import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../fields/Doc";
+import { Id } from "../../fields/FieldSymbols";
import { HtmlField } from "../../fields/HtmlField";
import { InkField } from "../../fields/InkField";
import { List } from "../../fields/List";
@@ -11,6 +12,7 @@ import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
import { ComputedField, ScriptField } from "../../fields/ScriptField";
import { Cast, NumCast, StrCast } from "../../fields/Types";
import { AudioField, ImageField, PdfField, VideoField, WebField, YoutubeField } from "../../fields/URLField";
+import { SharingPermissions } from "../../fields/util";
import { MessageStore } from "../../server/Message";
import { Upload } from "../../server/SharedMediaTypes";
import { OmitKeys, Utils } from "../../Utils";
@@ -33,6 +35,7 @@ import { AudioBox } from "../views/nodes/AudioBox";
import { ColorBox } from "../views/nodes/ColorBox";
import { ComparisonBox } from "../views/nodes/ComparisonBox";
import { DocHolderBox } from "../views/nodes/DocHolderBox";
+import { FilterBox } from "../views/nodes/FilterBox";
import { FontIconBox } from "../views/nodes/FontIconBox";
import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
import { ImageBox } from "../views/nodes/ImageBox";
@@ -50,9 +53,6 @@ import { PresElementBox } from "../views/presentationview/PresElementBox";
import { SearchBox } from "../views/search/SearchBox";
import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
import { DocumentType } from "./DocumentTypes";
-import { FilterBox } from "../views/nodes/FilterBox";
-import { SharingPermissions } from "../../fields/util";
-import { SharingManager } from "../util/SharingManager";
const path = require('path');
const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", ""));
@@ -61,6 +61,7 @@ export interface DocumentOptions {
_autoHeight?: boolean;
_headerHeight?: number; // height of header of custom notes
_headerFontSize?: number; // font size of header of custom notes
+ _headerPointerEvents?: string; // types of events the header of a custom text document can consume
_panX?: number;
_panY?: number;
_width?: number;
@@ -208,6 +209,8 @@ export interface DocumentOptions {
treeViewOutlineMode?: boolean; // whether slide should function as a text outline
treeViewLockExpandedView?: boolean; // whether the expanded view can be changed
treeViewDefaultExpandedView?: string; // default expanded view
+ sidebarColor?: string; // background color of text sidebar
+ sidebarViewType?: string; // collection type of text sidebar
limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents
// [key: string]: Opt<Field>;
pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown
@@ -234,6 +237,8 @@ class EmptyBox {
export namespace Docs {
+ export let newAccount: boolean = false;
+
export namespace Prototypes {
type LayoutSource = { LayoutString: (key: string) => string };
@@ -242,6 +247,7 @@ export namespace Docs {
view: LayoutSource,
dataField: string
},
+ data?: any,
options?: Partial<DocumentOptions>
};
type TemplateMap = Map<DocumentType, PrototypeTemplate>;
@@ -389,7 +395,7 @@ export namespace Docs {
// non-guid string ids for each document prototype
const prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix);
// fetch the actual prototype documents from the server
- const actualProtos = await DocServer.GetRefFields(prototypeIds);
+ const actualProtos = Docs.newAccount ? {} : await DocServer.GetRefFields(prototypeIds);
// update this object to include any default values: DocumentOptions for all prototypes
prototypeIds.map(id => {
@@ -462,6 +468,7 @@ export namespace Docs {
const options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) };
options.layout = layout.view?.LayoutString(layout.dataField);
const doc = Doc.assign(new Doc(prototypeId, true), { system: true, layoutKey: "layout", ...options });
+ doc.data = template.data;
doc.layout_keyValue = KeyValueBox.LayoutString("");
return doc;
}
@@ -572,7 +579,7 @@ export namespace Docs {
* only when creating a DockDocument from the current user's already existing
* main document.
*/
- export function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data") {
+ export function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data", protoId?: string) {
const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys, "^_");
protoProps.system = delegateProps.system;
@@ -588,7 +595,7 @@ export namespace Docs {
protoProps.isPrototype = true;
- const dataDoc = MakeDataDelegate(proto, protoProps, data, fieldKey);
+ const dataDoc = MakeDataDelegate(proto, protoProps, data, fieldKey, protoId);
const viewDoc = Doc.MakeDelegate(dataDoc, delegId);
// so that the list of annotations is already initialised, prevents issues in addonly.
@@ -603,6 +610,7 @@ export namespace Docs {
viewDoc.type !== DocumentType.LINK && DocUtils.MakeLinkToActiveAudio(viewDoc);
viewDoc["acl-Public"] = dataDoc["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add;
+ viewDoc["acl-Override"] = dataDoc["acl-Override"] = "None";
return Doc.assign(viewDoc, delegateProps, true);
}
@@ -617,8 +625,8 @@ export namespace Docs {
* @param options initial values to apply to this new delegate
* @param value the data to store in this new delegate
*/
- function MakeDataDelegate<D extends Field>(proto: Doc, options: DocumentOptions, value?: D, fieldKey: string = "data") {
- const deleg = Doc.MakeDelegate(proto);
+ function MakeDataDelegate<D extends Field>(proto: Doc, options: DocumentOptions, value?: D, fieldKey: string = "data", id: string | undefined = undefined) {
+ const deleg = Doc.MakeDelegate(proto, id);
if (value !== undefined) {
deleg[fieldKey] = value;
}
@@ -720,8 +728,8 @@ export namespace Docs {
LinkManager.Instance.addLink(doc);
- Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(self)");
- Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(self)");
+ source.doc.links === undefined && (Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(self)"));
+ target.doc.links === undefined && (Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(self)"));
return doc;
}
@@ -747,6 +755,8 @@ export namespace Docs {
I.author = Doc.CurrentUserEmail;
I.rotation = 0;
I.data = new InkField(points);
+ I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add;
+ I["acl-Override"] = "None";
return I;
}
@@ -771,7 +781,7 @@ export namespace Docs {
}
export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", dontRegisterChildViews: true, ...options, _viewType: CollectionViewType.Freeform }, id);
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Freeform }, id);
}
export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -802,8 +812,8 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", dontRegisterChildViews: true, ...options, _viewType: CollectionViewType.Tree }, id);
}
- export function StackingDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", dontRegisterChildViews: true, ...options, _viewType: CollectionViewType.Stacking }, id);
+ export function StackingDocument(documents: Array<Doc>, options: DocumentOptions, id?: string, protoId?: string) {
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Stacking }, id, undefined, protoId);
}
export function MulticolumnDocument(documents: Array<Doc>, options: DocumentOptions) {
@@ -815,7 +825,7 @@ export namespace Docs {
export function MasonryDocument(documents: Array<Doc>, options: DocumentOptions) {
- return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", dontRegisterChildViews: true, ...options, _viewType: CollectionViewType.Masonry });
+ return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", ...options, _viewType: CollectionViewType.Masonry });
}
export function LabelDocument(options?: DocumentOptions) {
@@ -886,17 +896,44 @@ export namespace Docs {
}
export namespace DocUtils {
+ export function Excluded(d: Doc, docFilters: string[]) {
+ const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields
+ docFilters.forEach(filter => {
+ const fields = filter.split(":");
+ const key = fields[0];
+ const value = fields[1];
+ const modifiers = fields[2];
+ if (!filterFacets[key]) {
+ filterFacets[key] = {};
+ }
+ filterFacets[key][value] = modifiers;
+ });
+
+ if (d.z) return false;
+ for (const facetKey of Object.keys(filterFacets)) {
+ const facet = filterFacets[facetKey];
+ const xs = Object.keys(facet).filter(value => facet[value] === "x");
+ const failsNotEqualFacets = xs?.some(value => Doc.matchFieldValue(d, facetKey, value));
+ if (failsNotEqualFacets) {
+ return true;
+ }
+ }
+ return false;
+ }
export function FilterDocs(docs: Doc[], docFilters: string[], docRangeFilters: string[], viewSpecScript?: ScriptField) {
const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields
- for (let i = 0; i < docFilters.length; i += 3) {
- const [key, value, modifiers] = docFilters.slice(i, i + 3);
+ docFilters.forEach(filter => {
+ const fields = filter.split(":");
+ const key = fields[0];
+ const value = fields[1];
+ const modifiers = fields[2];
if (!filterFacets[key]) {
filterFacets[key] = {};
}
filterFacets[key][value] = modifiers;
- }
+ });
const filteredDocs = docFilters.length ? childDocs.filter(d => {
if (d.z) return true;
@@ -928,7 +965,7 @@ export namespace DocUtils {
const min = Number(docRangeFilters[i + 1]);
const max = Number(docRangeFilters[i + 2]);
const val = Cast(d[key], "number", null);
- if (val !== undefined && (val < min || val > max)) {
+ if (val === undefined || (val < min || val > max)) {
return false;
}
}
@@ -974,9 +1011,9 @@ export namespace DocUtils {
DocUtils.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: doc }, { doc: d }, "audio link", "audio timeline"));
}
- export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string) {
+ export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string, allowParCollectionLink?: boolean) {
const sv = DocumentManager.Instance.getDocumentView(source.doc);
- if (sv && sv.props.ContainingCollectionDoc === target.doc) return;
+ if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target.doc) return;
if (target.doc === Doc.UserDoc()) return undefined;
const linkDoc = Docs.Create.LinkDocument(source, target, { linkRelationship, layoutKey: "layout_linkView", description }, id);
@@ -1065,13 +1102,13 @@ export namespace DocUtils {
});
}
ctor = Docs.Create.WebDocument;
- options = { ...options, _fitWidth: true, _nativeWidth: 850, _width: 400, _height: 512, title: path, };
+ options = { ...options, _fitWidth: true, _width: 400, _height: 512, title: path, };
}
return ctor ? ctor(path, options) : undefined;
}
- export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number): void {
- ContextMenu.Instance.addItem({
+ export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false): void {
+ !simpleMenu && ContextMenu.Instance.addItem({
description: "Add Note ...",
subitems: DocListCast((Doc.UserDoc()["template-notes"] as Doc).data).map((note, i) => ({
description: ":" + StrCast(note.title),
@@ -1090,14 +1127,15 @@ export namespace DocUtils {
});
ContextMenu.Instance.addItem({
description: "Add Template Doc ...",
- subitems: DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc).map((dragDoc, i) => ({
+ subitems: DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({
description: ":" + StrCast(dragDoc.title),
event: undoBatch((args: { x: number, y: number }) => {
- const newDoc = Doc.ApplyTemplate(dragDoc);
+ const newDoc = Doc.copyDragFactory(dragDoc);
if (newDoc) {
newDoc.author = Doc.CurrentUserEmail;
newDoc.x = x;
newDoc.y = y;
+ if (newDoc.type === DocumentType.RTF) FormattedTextBox.SelectOnLoad = newDoc[Id];
docAdder(newDoc);
}
}),