aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2022-06-17 09:33:01 -0400
committerbobzel <zzzman@gmail.com>2022-06-17 09:33:01 -0400
commit21eb25198c27d65e398d11b018a8dc792297e35a (patch)
tree79c0617197a566b9d6deb11d0453e28d419ffac8 /src
parent2cc83f81afa317c693aa45d3a2e1497ef7b2d477 (diff)
updated fontIconBadge to use 'viewed' list to decrement value when items are viewed. cleaned up how sharedDoc is setup in currentUserUtils to allow updates to not require rebuilding DB
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts10
-rw-r--r--src/client/util/CurrentUserUtils.ts75
-rw-r--r--src/client/views/collections/TreeView.tsx2
-rw-r--r--src/client/views/nodes/button/FontIconBadge.tsx32
-rw-r--r--src/client/views/nodes/button/FontIconBox.tsx2
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx4
-rw-r--r--src/fields/Doc.ts1
-rw-r--r--src/fields/Types.ts4
8 files changed, 77 insertions, 53 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index b290b2d58..5f009573e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -98,6 +98,7 @@ export class DocumentOptions {
allowOverlayDrop?: BOOLt = new BoolInfo("can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?");
childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection ");
targetDropAction?: DROPt = new DAInfo("what should happen to the source document when ??? ");
+ userColor?: string; // color associated with a Dash user (seen in header fields of shared documents)
color?: string; // foreground color data doc
backgroundColor?: STRt = new StrInfo("background color for data doc");
_backgroundColor?: STRt = new StrInfo("background color for each template layout doc (overrides backgroundColor)", true);
@@ -190,6 +191,10 @@ export class DocumentOptions {
childLayoutString?: string; // template string for collection to use to render its children
childDontRegisterViews?: boolean;
childHideLinkButton?: boolean; // hide link buttons on all children
+ childContextMenuFilters?: List<ScriptField>;
+ childContextMenuScripts?: List<ScriptField>;
+ childContextMenuLabels?: List<string>;
+ childContextMenuIcons?: List<string>;
hideLinkButton?: boolean; // whether the blue link counter button should be hidden
hideDecorationTitle?: boolean;
hideOpenButton?: boolean;
@@ -198,7 +203,6 @@ export class DocumentOptions {
hideAllLinks?: boolean; // whether all individual blue anchor dots should be hidden
isTemplateForField?: string; // the field key for which the containing document is a rendering template
isTemplateDoc?: boolean;
- watchedDocuments?: Doc; // list of documents an icon doc monitors in order to display a badge count
targetScriptKey?: string; // where to write a template script (used by collections with click templates which need to target onClick, onDoubleClick, etc)
templates?: List<string>;
hero?: ImageField; // primary image that best represents a compound document (e.g., for a buxton device document that has multiple images)
@@ -250,7 +254,8 @@ export class DocumentOptions {
numBtnType?: string;
numBtnMax?: number;
numBtnMin?: number;
- switchToggle?: boolean;
+ switchToggle?: boolean;
+ badgeValue?: ScriptField;
//LINEAR VIEW
linearViewIsExpanded?: boolean; // is linear view expanded
@@ -294,6 +299,7 @@ export class DocumentOptions {
treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view
treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
treeViewGrowsHorizontally?: boolean; // whether an embedded tree view of the document can grow horizontally without growing vertically
+ treeViewChildDoubleClick?: ScriptField; //
// Action Button
buttonMenu?: boolean; // whether a action button should be displayed
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 60bfc165b..b4e18a8bb 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -7,7 +7,7 @@ import { List } from "../../fields/List";
import { PrefetchProxy } from "../../fields/Proxy";
import { RichTextField } from "../../fields/RichTextField";
import { ComputedField, ScriptField } from "../../fields/ScriptField";
-import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
+import { BoolCast, Cast, DateCast, DocCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
import { ImageField, nullAudio } from "../../fields/URLField";
import { SharingPermissions } from "../../fields/util";
import { Utils } from "../../Utils";
@@ -354,6 +354,7 @@ export class CurrentUserUtils {
}
static menuBtnDescriptions(doc: Doc) {
+ const badgeValue = ScriptField.MakeFunction("((len) => len ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length)")
return [
{ title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
{ title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' },
@@ -361,7 +362,7 @@ export class CurrentUserUtils {
{ title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" },
{ title: "Imports", target: Cast(doc.myImportDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
{ title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
- { title: "Shared with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
+ { title: "Shared with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', badgeValue},
{ title: "Trails", target: Cast(doc.myTrails, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
{ title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" },
];
@@ -369,8 +370,9 @@ export class CurrentUserUtils {
static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
if (doc.menuStack === undefined) {
- await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId); // sets up the right sidebar collection for mobile upload documents and sharing
- const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, watchedDocuments, hidden }) =>
+ await this.setupLinkDocs(doc, linkDatabaseId);
+ await this.setupSharedDocs(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing
+ const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, badgeValue, hidden }) =>
Docs.Create.FontIconDocument({
icon,
btnType: ButtonType.MenuButton,
@@ -387,7 +389,7 @@ export class CurrentUserUtils {
_removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
_width: 60,
_height: 60,
- watchedDocuments,
+ badgeValue,
onClick: ScriptField.MakeScript(click, { scriptContext: "any" })
})
);
@@ -900,10 +902,7 @@ export class CurrentUserUtils {
}
// Sharing sidebar is where shared documents are contained
- static async setupSharingSidebar(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
- if (doc.myPublishedDocs === undefined) {
- doc.myPublishedDocs = new List<Doc>();
- }
+ static async setupLinkDocs(doc: Doc, linkDatabaseId: string) {
if (doc.myLinkDatabase === undefined) {
let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId);
if (!linkDocs) {
@@ -915,28 +914,41 @@ export class CurrentUserUtils {
}
doc.myLinkDatabase = new PrefetchProxy(linkDocs);
}
- // TODO:glr NOTE: treeViewHideTitle & _showTitle may be confusing, treeViewHideTitle is for the editable title (just for tree view), _showTitle is to show the Document title for any document
- if (doc.mySharedDocs === undefined) {
- let sharedDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(sharingDocumentId + "outer");
- if (!sharedDocs) {
- sharedDocs = Docs.Create.TreeDocument([], {
- title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15,
- _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
- _chromeHidden: true, boxShadow: "0 0",
- dontRegisterView: true, explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
- }, sharingDocumentId + "outer", sharingDocumentId);
- (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment;
- }
- if (sharedDocs instanceof Doc) {
- Doc.GetProto(sharedDocs).userColor = sharedDocs.userColor || "rgb(202, 202, 202)";
- const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`);
- const dashboardFilter = ScriptField.MakeFunction(`doc._viewType === '${CollectionViewType.Docking}'`, { doc: Doc.name });
- sharedDocs.childContextMenuFilters = new List<ScriptField>([dashboardFilter!,]);
- sharedDocs.childContextMenuScripts = new List<ScriptField>([addToDashboards!,]);
- sharedDocs.childContextMenuLabels = new List<string>(["Add to Dashboards",]);
- sharedDocs.childContextMenuIcons = new List<string>(["user-plus",]);
- }
- doc.mySharedDocs = new PrefetchProxy(sharedDocs as Doc);
+ }
+ // A user's sharing document is where all documents that are shared to that user are placed.
+ // When the user views one of these documents, it will be added to the sharing documents 'viewed' list field
+ // The sharing document also stores the user's color value which helps distinguish shared documents from personal documents
+ static async setupSharedDocs(doc: Doc, sharingDocumentId: string) {
+ const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`);
+ const dashboardFilter = ScriptField.MakeFunction(`doc._viewType === '${CollectionViewType.Docking}'`, { doc: Doc.name });
+ const dblClkScript = ScriptField.MakeScript("{scriptContext.openLevel(documentView); addDocToList(scriptContext.props.treeView.props.Document, 'viewed', documentView.rootDoc);}", {scriptContext:"any", documentView:Doc.name})
+
+ const sharedDocOpts:DocumentOptions = {
+ title: "My Shared Docs",
+ userColor: "rgb(202, 202, 202)",
+ childContextMenuFilters: new List<ScriptField>([dashboardFilter!,]),
+ childContextMenuScripts: new List<ScriptField>([addToDashboards!,]),
+ childContextMenuLabels: new List<string>(["Add to Dashboards",]),
+ childContextMenuIcons: new List<string>(["user-plus",]),
+ treeViewChildDoubleClick: dblClkScript,
+ };
+ const sharedRequiredDocOpts:DocumentOptions = {
+ "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
+ childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15,
+ // NOTE: treeViewHideTitle & _showTitle is for a TreeView's editable title, _showTitle is for DocumentViews title bar
+ _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true,
+ explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
+ };
+
+ const sharedDocs = Docs.newAccount ? undefined : DocCast(doc.mySharedDocs) ?? DocCast(await DocServer.GetRefField(sharingDocumentId + "outer"));
+ if (!(sharedDocs instanceof Doc)) {
+ doc.mySharedDocs = new PrefetchProxy(
+ Docs.Create.TreeDocument([], {...sharedDocOpts, ...sharedRequiredDocOpts}, sharingDocumentId + "outer", sharingDocumentId));
+ } else {
+ Object.entries(sharedRequiredDocOpts).forEach(pair => {
+ const targetDoc = pair[0].startsWith("_") ? sharedDocs as Doc : Doc.GetProto(sharedDocs as Doc);
+ targetDoc[pair[0]] = pair[1];
+ });
}
}
@@ -1044,6 +1056,7 @@ export class CurrentUserUtils {
doc.savedFilters = new List<Doc>();
doc.filterDocCount = 0;
doc.freezeChildren = "remove|add";
+ doc.myPublishedDocs = doc.myPublishedDocs ?? new List<Doc>();
doc.myHeaderBarDoc = doc.myHeaderBarDoc ?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true });
this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon
this.setupDocTemplates(doc); // sets up the template menu of templates
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 8824750a3..59dc5671b 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -613,7 +613,7 @@ export class TreeView extends React.Component<TreeViewProps> {
return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!);
}
- onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
+ onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick,(!this.props.treeView.outlineMode ? this._openScript?.():null));
refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
ignoreEvent = (e: any) => {
diff --git a/src/client/views/nodes/button/FontIconBadge.tsx b/src/client/views/nodes/button/FontIconBadge.tsx
index cf86b5e07..df17d603f 100644
--- a/src/client/views/nodes/button/FontIconBadge.tsx
+++ b/src/client/views/nodes/button/FontIconBadge.tsx
@@ -6,31 +6,31 @@ import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils
import { DragManager } from "../../../util/DragManager";
import "./FontIconBadge.scss";
-interface FontIconBadgeProps {
- collection: Doc | undefined;
+interface FontIconBadgeProps {
+ value: string | undefined;
}
@observer
export class FontIconBadge extends React.Component<FontIconBadgeProps> {
_notifsRef = React.createRef<HTMLDivElement>();
- onPointerDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e,
- (e: PointerEvent) => {
- const dragData = new DragManager.DocumentDragData([this.props.collection!]);
- DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y);
- return true;
- },
- returnFalse, emptyFunction, false);
- }
+ // onPointerDown = (e: React.PointerEvent) => {
+ // setupMoveUpEvents(this, e,
+ // (e: PointerEvent) => {
+ // const dragData = new DragManager.DocumentDragData([this.props.collection!]);
+ // DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y);
+ // return true;
+ // },
+ // returnFalse, emptyFunction, false);
+ // }
render() {
- if (!(this.props.collection instanceof Doc)) return (null);
- const length = DocListCast(this.props.collection.data).filter(d => GetEffectiveAcl(d) !== AclPrivate).length; // Object.keys(d).length).length; // filter out any documents that we can't read
+ if (this.props.value === undefined) return (null);
return <div className="fontIconBadge-container" ref={this._notifsRef}>
- <div className="fontIconBadge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}
- onPointerDown={this.onPointerDown} >
- {length}
+ <div className="fontIconBadge" style={{ "display": "initial" }}
+ // onPointerDown={this.onPointerDown}
+ >
+ {this.props.value}
</div>
</div>;
}
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index a1b9023f3..97e6eddfe 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -521,7 +521,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
<div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}>
{this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />}
{menuLabel}
- <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} />
+ <FontIconBadge value={ScriptCast(this.Document.badgeValue,null)?.script.run({self:this.Document}).result} />
</div >
);
break;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index f29b879b3..16a523b40 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -382,11 +382,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}
- // creates links between terms in a document and documents which have a matching Id
+ // creates links between terms in a document and published documents (myPublishedDocs) that have titles starting with an '@'
hyperlinkTerm = (tr: any, target: Doc, newAutoLinks: Set<Doc>) => {
const editorView = this._editorView;
if (editorView && (editorView as any).docView && !Doc.AreProtosEqual(target, this.rootDoc)) {
- const autoLinkTerm = StrCast(target.title).replace(/^@/, "");
+ const autoLinkTerm = StrCast(target.title).replace(/^@/, "");
const flattened1 = this.findInNode(editorView, editorView.state.doc, autoLinkTerm);
var alink: Doc | undefined;
flattened1.forEach((flat, i) => {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 6abc27b23..7b72787d1 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1436,6 +1436,7 @@ ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return Doc.cop
ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return Doc.delegateDragFactory(dragFactory); });
ScriptingGlobals.add(function copyField(field: any) { return Field.Copy(field); });
ScriptingGlobals.add(function docList(field: any) { return DocListCast(field); });
+ScriptingGlobals.add(function addDocToList(doc: Doc, field: string, added:Doc) { return Doc.AddDocToList(doc,field, added); });
ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); });
ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); });
ScriptingGlobals.add(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); });
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index 7e2aa5681..bf40a0d7b 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -76,6 +76,10 @@ export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal
return defaultVal === null ? undefined : defaultVal;
}
+export function DocCast(field: FieldResult, defaultVal?: Doc) {
+ return Cast(field, Doc, null) ?? defaultVal;
+}
+
export function NumCast(field: FieldResult, defaultVal: number | null = 0) {
return Cast(field, "number", defaultVal);
}