aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/CurrentUserUtils.ts458
-rw-r--r--src/client/util/DictationManager.ts23
-rw-r--r--src/client/util/DocumentManager.ts20
-rw-r--r--src/client/util/DragManager.ts10
-rw-r--r--src/client/util/GroupManager.tsx57
-rw-r--r--src/client/util/GroupMemberView.tsx22
-rw-r--r--src/client/util/History.ts4
-rw-r--r--src/client/util/HypothesisUtils.ts5
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx9
-rw-r--r--src/client/util/Import & Export/ImportMetadataEntry.tsx8
-rw-r--r--src/client/util/InteractionUtils.tsx62
-rw-r--r--src/client/util/SearchUtil.ts14
-rw-r--r--src/client/util/SelectionManager.ts30
-rw-r--r--src/client/util/SettingsManager.scss16
-rw-r--r--src/client/util/SettingsManager.tsx57
-rw-r--r--src/client/util/SharingManager.scss23
-rw-r--r--src/client/util/SharingManager.tsx289
17 files changed, 686 insertions, 421 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 5954e12dc..7a1c193c1 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,29 +1,35 @@
import { computed, observable, reaction } from "mobx";
import * as rp from 'request-promise';
-import { Utils } from "../../Utils";
-import { DocServer } from "../DocServer";
-import { Docs, DocumentOptions, DocUtils } from "../documents/Documents";
-import { UndoManager } from "./UndoManager";
-import { Doc, DocListCast, DocListCastAsync, DataSym } from "../../fields/Doc";
+import { DataSym, Doc, DocListCast, DocListCastAsync } from "../../fields/Doc";
+import { Id } from "../../fields/FieldSymbols";
import { List } from "../../fields/List";
+import { PrefetchProxy } from "../../fields/Proxy";
+import { RichTextField } from "../../fields/RichTextField";
import { listSpec } from "../../fields/Schema";
-import { ScriptField, ComputedField } from "../../fields/ScriptField";
-import { Cast, PromiseValue, StrCast, NumCast, BoolCast } from "../../fields/Types";
+import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
+import { ComputedField, ScriptField } from "../../fields/ScriptField";
+import { BoolCast, Cast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
import { nullAudio } from "../../fields/URLField";
-import { DragManager } from "./DragManager";
-import { Scripting } from "./Scripting";
-import { CollectionViewType, CollectionView } from "../views/collections/CollectionView";
-import { makeTemplate } from "./DropConverter";
-import { RichTextField } from "../../fields/RichTextField";
-import { PrefetchProxy } from "../../fields/Proxy";
-import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
-import { MainView } from "../views/MainView";
+import { Utils } from "../../Utils";
+import { DocServer } from "../DocServer";
+import { Docs, DocumentOptions, DocUtils } from "../documents/Documents";
import { DocumentType } from "../documents/DocumentTypes";
-import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
+import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView";
+import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
+import { MainView } from "../views/MainView";
+import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
import { LabelBox } from "../views/nodes/LabelBox";
+import { OverlayView } from "../views/OverlayView";
+import { DocumentManager } from "./DocumentManager";
+import { DragManager } from "./DragManager";
+import { makeTemplate } from "./DropConverter";
+import { HistoryUtil } from "./History";
import { LinkManager } from "./LinkManager";
-import { Id } from "../../fields/FieldSymbols";
+import { Scripting } from "./Scripting";
+import { SearchUtil } from "./SearchUtil";
+import { SelectionManager } from "./SelectionManager";
+import { UndoManager } from "./UndoManager";
export class CurrentUserUtils {
private static curr_id: string;
@@ -36,7 +42,7 @@ export class CurrentUserUtils {
@computed public static get UserDocument() { return Doc.UserDoc(); }
@observable public static GuestTarget: Doc | undefined;
- @observable public static GuestWorkspace: Doc | undefined;
+ @observable public static GuestDashboard: Doc | undefined;
@observable public static GuestMobile: Doc | undefined;
@observable public static propertiesWidth: number = 0;
@@ -46,14 +52,14 @@ export class CurrentUserUtils {
if (doc["template-button-query"] === undefined) {
const queryTemplate = Docs.Create.MulticolumnDocument(
[
- Docs.Create.SearchDocument({ _viewType: CollectionViewType.Schema, ignoreClick: true, forceActive: true, lockedPosition: true, title: "query", _height: 200, system: true }),
+ Docs.Create.SearchDocument({ _viewType: CollectionViewType.Schema, ignoreClick: true, forceActive: true, _chromeStatus: "disabled", lockedPosition: true, title: "query", _height: 200, system: true }),
Docs.Create.FreeformDocument([], { title: "data", _height: 100, system: true })
],
- { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true, system: true }
+ { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, system: true }
);
queryTemplate.isTemplateDoc = makeTemplate(queryTemplate);
doc["template-button-query"] = CurrentUserUtils.ficon({
- onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
dragFactory: new PrefetchProxy(queryTemplate) as any as Doc,
removeDropProperties: new List<string>(["dropAction"]), title: "query view", icon: "question-circle"
});
@@ -72,7 +78,7 @@ export class CurrentUserUtils {
this.mobileTextContainer({},
[this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])]);
doc["template-mobile-button"] = CurrentUserUtils.ficon({
- onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
dragFactory: new PrefetchProxy(queryTemplate) as any as Doc,
removeDropProperties: new List<string>(["dropAction"]), title: "mobile button", icon: "mobile"
});
@@ -84,11 +90,11 @@ export class CurrentUserUtils {
Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }),
Docs.Create.TextDocument("", { title: "text", _height: 100, system: true })
],
- { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true, system: true }
+ { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, system: true }
);
slideTemplate.isTemplateDoc = makeTemplate(slideTemplate);
doc["template-button-slides"] = CurrentUserUtils.ficon({
- onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
dragFactory: new PrefetchProxy(slideTemplate) as any as Doc,
removeDropProperties: new List<string>(["dropAction"]), title: "presentation slide", icon: "address-card"
});
@@ -96,6 +102,7 @@ export class CurrentUserUtils {
if (doc["template-button-description"] === undefined) {
const descriptionTemplate = Doc.MakeDelegate(Docs.Create.TextDocument(" ", { title: "header", _height: 100, system: true }, "header")); // text needs to be a space to allow templateText to be created
+ descriptionTemplate.system = true;
descriptionTemplate[DataSym].layout =
"<div>" +
" <FormattedTextBox {...props} height='{this._headerHeight||75}px' background='{this._headerColor||`orange`}' fieldKey={'header'}/>" +
@@ -104,14 +111,15 @@ export class CurrentUserUtils {
(descriptionTemplate.proto as Doc).isTemplateDoc = makeTemplate(descriptionTemplate.proto as Doc, true, "descriptionView");
doc["template-button-description"] = CurrentUserUtils.ficon({
- onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
dragFactory: new PrefetchProxy(descriptionTemplate) as any as Doc,
- removeDropProperties: new List<string>(["dropAction"]), title: "description view", icon: "window-maximize"
+ removeDropProperties: new List<string>(["dropAction"]), title: "description view", icon: "window-maximize", system: true
});
}
if (doc["template-button-link"] === undefined) { // set _backgroundColor to transparent to prevent link dot from obscuring document it's attached to.
const linkTemplate = Doc.MakeDelegate(Docs.Create.TextDocument(" ", { title: "header", _height: 100, system: true }, "header")); // text needs to be a space to allow templateText to be created
+ linkTemplate.system = true;
Doc.GetProto(linkTemplate).layout =
"<div>" +
" <FormattedTextBox {...props} height='{this._headerHeight||75}px' background='{this._headerColor||`lightGray`}' fieldKey={'header'}/>" +
@@ -150,9 +158,9 @@ export class CurrentUserUtils {
linkTemplate.header = new RichTextField(JSON.stringify(rtf2), "");
doc["template-button-link"] = CurrentUserUtils.ficon({
- onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
dragFactory: new PrefetchProxy(linkTemplate) as any as Doc,
- removeDropProperties: new List<string>(["dropAction"]), title: "link view", icon: "window-maximize"
+ removeDropProperties: new List<string>(["dropAction"]), title: "link view", icon: "window-maximize", system: true
});
}
@@ -182,7 +190,7 @@ export class CurrentUserUtils {
box.isTemplateDoc = makeTemplate(box, true, "switch");
doc["template-button-switch"] = CurrentUserUtils.ficon({
- onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
dragFactory: new PrefetchProxy(box) as any as Doc,
removeDropProperties: new List<string>(["dropAction"]), title: "data switch", icon: "toggle-on", system: true
});
@@ -232,7 +240,7 @@ export class CurrentUserUtils {
long.title = "Long Description";
doc["template-button-detail"] = CurrentUserUtils.ficon({
- onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'),
+ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
dragFactory: new PrefetchProxy(detailView) as any as Doc,
removeDropProperties: new List<string>(["dropAction"]), title: "detail view", icon: "window-maximize", system: true
});
@@ -392,74 +400,68 @@ export class CurrentUserUtils {
}[] {
if (doc.emptyPresentation === undefined) {
doc.emptyPresentation = Docs.Create.PresDocument(new List<Doc>(),
- { title: "Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeStatus: "replaced", boxShadow: "0 0", system: true });
+ { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeStatus: "replaced", boxShadow: "0 0", system: true, cloneFieldFilter: new List<string>(["system"]) });
+ ((doc.emptyPresentation as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptyCollection === undefined) {
doc.emptyCollection = Docs.Create.FreeformDocument([],
- { _nativeWidth: undefined, _nativeHeight: undefined, _width: 150, _height: 100, title: "freeform", system: true });
+ { _nativeWidth: undefined, _nativeHeight: undefined, _width: 150, _height: 100, title: "freeform", system: true, cloneFieldFilter: new List<string>(["system"]) });
+ ((doc.emptyCollection as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptyPane === undefined) {
- doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, title: "Untitled Collection", system: true });
+ doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _width: 500, _height: 800, title: "Untitled Tab", system: true, cloneFieldFilter: new List<string>(["system"]) });
+ ((doc.emptyPane as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptyComparison === undefined) {
- doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300, system: true });
+ doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300, system: true, cloneFieldFilter: new List<string>(["system"]) });
}
if (doc.emptyScript === undefined) {
- doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250, title: "script", system: true });
+ doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250, title: "script", system: true, cloneFieldFilter: new List<string>(["system"]) });
+ ((doc.emptyScript as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptyScreenshot === undefined) {
- doc.emptyScreenshot = Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot", system: true });
+ doc.emptyScreenshot = Docs.Create.ScreenshotDocument("", { _width: 400, _height: 200, title: "screen snapshot", system: true, cloneFieldFilter: new List<string>(["system"]) });
}
if (doc.emptyAudio === undefined) {
- doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { _width: 200, title: "ready to record audio", system: true });
+ doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { _width: 200, title: "ready to record audio", system: true, cloneFieldFilter: new List<string>(["system"]) });
+ ((doc.emptyAudio as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptyImage === undefined) {
doc.emptyImage = Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth: 250, title: "an image of a cat", system: true });
}
if (doc.emptyButton === undefined) {
- doc.emptyButton = Docs.Create.ButtonDocument({ _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, title: "Button", system: true });
+ doc.emptyButton = Docs.Create.ButtonDocument({ _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, title: "Button", system: true, cloneFieldFilter: new List<string>(["system"]) });
+ ((doc.emptyButton as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptyDocHolder === undefined) {
doc.emptyDocHolder = Docs.Create.DocumentDocument(
ComputedField.MakeFunction("selectedDocs(this,this.excludeCollections,[_last_])?.[0]") as any,
- { _width: 250, _height: 250, title: "container", system: true });
+ { _width: 250, _height: 250, title: "container", system: true, cloneFieldFilter: new List<string>(["system"]) });
}
if (doc.emptyWebpage === undefined) {
- doc.emptyWebpage = Docs.Create.WebDocument("", { title: "webpage", _nativeWidth: 850, _nativeHeight: 962, _width: 400, UseCors: true, system: true });
+ doc.emptyWebpage = Docs.Create.WebDocument("", { title: "webpage", _nativeWidth: 850, _nativeHeight: 962, _width: 400, useCors: true, system: true, cloneFieldFilter: new List<string>(["system"]) });
}
if (doc.activeMobileMenu === undefined) {
this.setupActiveMobileMenu(doc);
}
return [
- { toolTip: "Tap to create a collection in a new pane, drag for a collection", title: "Col", icon: "folder", click: 'openOnRight(getCopy(this.clickFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyCollection as Doc, noviceMode: true, clickFactory: doc.emptyPane as Doc, },
- { toolTip: "Tap to create a webpage in a new pane, drag for a webpage", title: "Web", icon: "globe-asia", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyWebpage as Doc, noviceMode: true },
- { toolTip: "Tap to create a cat image in a new pane, drag for a cat image", title: "Image", icon: "cat", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyImage as Doc },
- { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyComparison as Doc, noviceMode: true },
- { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyScreenshot as Doc },
- // { title: "Drag a webcam", title: "Cam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { _width: 400, _height: 400, title: "a test cam" })' },
- { toolTip: "Tap to create an audio recorder in a new pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyAudio as Doc, noviceMode: true },
- { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyButton as Doc, noviceMode: true },
-
- { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Present", icon: "tv", click: 'openOnRight(Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().activePresentation = getCopy(this.dragFactory, true)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true },
- { toolTip: "Tap to create a search box in a new pane, drag for a search box", title: "Query", icon: "search", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptySearch as Doc },
- { toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyScript as Doc },
- // { title: "Drag an import folder", title: "Load", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
+ { toolTip: "Tap to create a collection in a new pane, drag for a collection", title: "Col", icon: "folder", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyCollection as Doc, noviceMode: true, clickFactory: doc.emptyPane as Doc, },
+ { toolTip: "Tap to create a webpage in a new pane, drag for a webpage", title: "Web", icon: "globe-asia", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWebpage as Doc, noviceMode: true },
+ { toolTip: "Tap to create a cat image in a new pane, drag for a cat image", title: "Image", icon: "cat", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyImage as Doc },
+ { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyComparison as Doc, noviceMode: true },
+ { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc },
+ { toolTip: "Tap to create an audio recorder in a new pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true },
+ { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc, noviceMode: true },
+ { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true },
+ { toolTip: "Tap to create a search box in a new pane, drag for a search box", title: "Query", icon: "search", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptySearch as Doc },
+ { toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScript as Doc },
{ toolTip: "Tap to create a mobile view in a new pane, drag for a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc },
- // { title: "Drag an instance of the device collection", title: "Buxton", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.Buxton()' },
- // { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this)', backgroundColor: "blue", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
- // { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
- // { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activeInkPen, this)`, activeInkPen: doc },
- // { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activeInkPen = sameDocs(this.activeInkPen, this) ? undefined : this);', ischecked: `sameDocs(this.activeInkPen, this)`, backgroundColor: "pink", activeInkPen: doc },
- // { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activeInkPen = this;', ischecked: `sameDocs(this.activeInkPen, this)`, backgroundColor: "white", activeInkPen: doc },
- { toolTip: "Tap to create a document previewer in a new pane, drag for a document previewer", title: "Prev", icon: "expand", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory,true)', dragFactory: doc.emptyDocHolder as Doc },
+ { toolTip: "Tap to create a document previewer in a new pane, drag for a document previewer", title: "Prev", icon: "expand", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyDocHolder as Doc },
{ toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' },
- { toolTip: "Connect a Google Account", title: "Google Account", icon: "external-link-alt", click: 'GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)' },
];
}
-
-
// setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
static async setupCreatorButtons(doc: Doc) {
let alreadyCreatedButtons: string[] = [];
@@ -488,7 +490,8 @@ export class CurrentUserUtils {
dragFactory,
clickFactory,
userDoc: noviceMode ? undefined as any : doc,
- hidden: noviceMode ? undefined as any : ComputedField.MakeFunction("self.userDoc.noviceMode"), system: true
+ hidden: noviceMode ? undefined as any : ComputedField.MakeFunction("self.userDoc.noviceMode"),
+ system: true
}));
if (dragCreatorSet === undefined) {
@@ -504,39 +507,43 @@ export class CurrentUserUtils {
}
static menuBtnDescriptions(doc: Doc): {
- title: string, icon: string, click: string, watchedDocuments?: Doc
+ title: string, target: Doc, icon: string, click: string, watchedDocuments?: Doc
}[] {
this.setupSharingSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing
return [
- { title: "Sharing", icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc["sidebar-sharing"] as Doc },
- { title: "Workspace", icon: "desktop", click: 'selectMainMenu(self)' },
- { title: "Catalog", icon: "file", click: 'selectMainMenu(self)' },
- { title: "Archive", icon: "archive", click: 'selectMainMenu(self)' },
- { title: "Import", icon: "upload", click: 'selectMainMenu(self)' },
- { title: "Tools", icon: "wrench", click: 'selectMainMenu(self)' },
- { title: "Help", icon: "question-circle", click: 'selectMainMenu(self)' },
- { title: "Settings", icon: "cog", click: 'selectMainMenu(self)' },
- { title: "User Doc", icon: "address-card", click: 'selectMainMenu(self)' },
+ { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
+ { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
+ { title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
+ { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
+ { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)' },
+ { title: "Filter", target: Cast(doc.myFilter, Doc, null), icon: "filter", click: 'selectMainMenu(self)' },
+ { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
+ { title: "Catalog", target: undefined as any, icon: "file", click: 'selectMainMenu(self)' },
+ { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' },
+ { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' },
+ { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' },
];
}
static setupSearchPanel(doc: Doc) {
- if (doc["search-panel"] === undefined) {
- doc["search-panel"] = new PrefetchProxy(Docs.Create.SearchDocument({
- _width: 500, _height: 400, backgroundColor: "dimGray", ignoreClick: true,
+ if (doc.mySearchPanelDoc === undefined) {
+ doc.mySearchPanelDoc = new PrefetchProxy(Docs.Create.SearchDocument({
+ _width: 500, _height: 300, backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true,
childDropAction: "alias", lockedPosition: true, _viewType: CollectionViewType.Schema, _chromeStatus: "disabled", title: "sidebar search stack", system: true
})) as any as Doc;
}
}
static setupMenuPanel(doc: Doc) {
if (doc.menuStack === undefined) {
- const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, icon, click, watchedDocuments }) =>
+ const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, watchedDocuments }) =>
Docs.Create.FontIconDocument({
icon,
iconShape: "square",
title,
+ target,
_backgroundColor: "black",
- _stayInCollection: true,
+ dropAction: "alias",
+ removeDropProperties: new List<string>(["dropAction"]),
childDropAction: "same",
_width: 60,
_height: 60,
@@ -588,7 +595,7 @@ export class CurrentUserUtils {
// SEts up mobile buttons for inside mobile menu
static setupMobileButtons(doc?: Doc, buttons?: string[]) {
const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [
- { title: "WORKSPACES", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Workspaces from your mobile, and navigate through all of your documents. " },
+ { title: "DASHBOARDS", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Dashboards from your mobile, and navigate through all of your documents. " },
{ title: "UPLOAD", icon: "upload", click: 'openMobileUploads()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." },
{ title: "MOBILE UPLOAD", icon: "mobile", click: 'switchToMobileUploadCollection()', backgroundColor: "lightgrey", info: "Access the collection of your mobile uploads." },
{ title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "lightgrey", info: "Use your phone to record, dictate and then upload audio onto Dash Web." },
@@ -685,11 +692,11 @@ export class CurrentUserUtils {
}
static setupLibrary(userDoc: Doc) {
- return CurrentUserUtils.setupWorkspaces(userDoc);
+ return CurrentUserUtils.setupDashboards(userDoc);
}
- // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker.
- // when clicked, this panel will be displayed in the target container (ie, sidebarContainer)
+ // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker.
+ // when clicked, this panel will be displayed in the target container (ie, sidebarContainer)
static async setupToolsBtnPanel(doc: Doc) {
// setup a masonry view of all he creators
const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc);
@@ -711,92 +718,88 @@ export class CurrentUserUtils {
doc.myColorPicker = new PrefetchProxy(color);
}
- if (doc["sidebar-tools"] === undefined) {
+ if (doc.myTools === undefined) {
const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], {
- title: "sidebar-tools", _width: 500, _yMargin: 20, lockedPosition: true, _chromeStatus: "disabled", hideFilterView: true, forceActive: true, system: true
+ title: "My Tools", _width: 500, _yMargin: 20, lockedPosition: true, _chromeStatus: "disabled", forceActive: true, system: true
})) as any as Doc;
- doc["sidebar-tools"] = toolsStack;
+ doc.myTools = toolsStack;
}
}
- static setupWorkspaces(doc: Doc) {
- // setup workspaces library item
- doc.myWorkspaces === undefined;
- if (doc.myWorkspaces === undefined) {
- doc.myWorkspaces = new PrefetchProxy(Docs.Create.TreeDocument([], {
- title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true
- }));
- }
- if (doc["sidebar-workspaces"] === undefined) {
- const newWorkspace = ScriptField.MakeScript(`createNewWorkspace()`);
- (doc.myWorkspaces as Doc).contextMenuScripts = new List<ScriptField>([newWorkspace!]);
- (doc.myWorkspaces as Doc).contextMenuLabels = new List<string>(["Create New Workspace"]);
-
- const workspaces = doc.myWorkspaces as Doc;
-
- doc["sidebar-workspaces"] = new PrefetchProxy(Docs.Create.TreeDocument([workspaces], {
+ static async setupDashboards(doc: Doc) {
+ // setup dashboards library item
+ await doc.myDashboards;
+ if (doc.myDashboards === undefined) {
+ doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], {
+ title: "My Dashboards", _height: 400,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
- treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
+ treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
- })) as any as Doc;
+ }));
+ const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`);
+ (doc.myDashboards as any as Doc).contextMenuScripts = new List<ScriptField>([newDashboard!]);
+ (doc.myDashboards as any as Doc).contextMenuLabels = new List<string>(["Create New Dashboard"]);
}
+ return doc.myDashboards as any as Doc;
}
- static setupCatalog(doc: Doc) {
- doc.myCatalog === undefined;
- if (doc.myCatalog === undefined) {
- doc.myCatalog = new PrefetchProxy(Docs.Create.SchemaDocument([], [], {
- title: "CATALOG", _height: 1000, _fitWidth: true, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false,
- childDropAction: "alias", targetDropAction: "same", _stayInCollection: true, treeViewOpen: true, system: true
+ static async setupPresentations(doc: Doc) {
+ await doc.myPresentations;
+ if (doc.myPresentations === undefined) {
+ doc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], {
+ title: "My Presentations", _height: 100,
+ treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
+ treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
+ lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
}));
+ const newPresentations = ScriptField.MakeScript(`createNewPresentation()`);
+ (doc.myPresentations as any as Doc).contextMenuScripts = new List<ScriptField>([newPresentations!]);
+ (doc.myPresentations as any as Doc).contextMenuLabels = new List<string>(["Create New Presentation"]);
+ const presentations = doc.myPresentations as any as Doc;
}
+ return doc.myPresentations as any as Doc;
+ }
- if (doc["sidebar-catalog"] === undefined) {
- const catalog = doc.myCatalog as Doc;
-
- doc["sidebar-catalog"] = new PrefetchProxy(Docs.Create.TreeDocument([catalog], {
- title: "sidebar-catalog",
+ static setupRecentlyClosedDocs(doc: Doc) {
+ // setup Recently Closed library item
+ doc.myRecentlyClosedDocs === undefined;
+ if (doc.myRecentlyClosedDocs === undefined) {
+ doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
+ title: "Recently Closed", _height: 500,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
- treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
+ treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
- })) as any as Doc;
+ }));
+ const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
+ (doc.myRecentlyClosedDocs as any as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
+ (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List<string>(["Clear All"]);
}
}
- static setupRecentlyClosed(doc: Doc) {
+ static setupFilterDocs(doc: Doc) {
// setup Recently Closed library item
- doc.myRecentlyClosed === undefined;
- if (doc.myRecentlyClosed === undefined) {
- doc.myRecentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], {
- title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, _stayInCollection: true, system: true
- }));
- }
- // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready
- PromiseValue(Cast(doc.myRecentlyClosed, Doc)).then(recent => recent && PromiseValue(recent.data).then(DocListCast));
- if (doc["sidebar-recentlyClosed"] === undefined) {
- const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
- (doc.myRecentlyClosed as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
- (doc.myRecentlyClosed as Doc).contextMenuLabels = new List<string>(["Clear All"]);
-
- const recentlyClosed = doc.myRecentlyClosed as Doc;
-
- doc["sidebar-recentlyClosed"] = new PrefetchProxy(Docs.Create.TreeDocument([recentlyClosed], {
- title: "sidebar-recentlyClosed",
+ doc.myFilter === undefined;
+ if (doc.myFilter === undefined) {
+ doc.myFilter = new PrefetchProxy(Docs.Create.FilterDocument({
+ title: "FilterDoc", _height: 500,
treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias",
- treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true,
+ treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
- })) as any as Doc;
+ }));
+ const clearAll = ScriptField.MakeScript(`self.data = new List([])`);
+ (doc.myFilter as any as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
+ (doc.myFilter as any as Doc).contextMenuLabels = new List<string>(["Clear All"]);
}
}
static setupUserDoc(doc: Doc) {
- if (doc["sidebar-userDoc"] === undefined) {
+ if (doc.myUserDoc === undefined) {
doc.treeViewOpen = true;
doc.treeViewExpandedView = "fields";
- doc["sidebar-userDoc"] = new PrefetchProxy(Docs.Create.TreeDocument([doc], {
- treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, title: "sidebar-userDoc",
- treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false,
+ doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], {
+ treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, title: "My UserDoc",
+ treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false,
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
})) as any as Doc;
}
@@ -806,9 +809,8 @@ export class CurrentUserUtils {
if (doc.sidebar === undefined) {
const sidebarContainer = new Doc();
sidebarContainer._chromeStatus = "disabled";
- sidebarContainer.onClick = ScriptField.MakeScript("freezeSidebar()");
+ sidebarContainer.system = true;
doc.sidebar = new PrefetchProxy(sidebarContainer);
- doc.system = true;
}
return doc.sidebar as Doc;
}
@@ -817,9 +819,10 @@ export class CurrentUserUtils {
static async setupSidebarButtons(doc: Doc) {
CurrentUserUtils.setupSidebarContainer(doc);
await CurrentUserUtils.setupToolsBtnPanel(doc);
- CurrentUserUtils.setupWorkspaces(doc);
- CurrentUserUtils.setupCatalog(doc);
- CurrentUserUtils.setupRecentlyClosed(doc);
+ CurrentUserUtils.setupDashboards(doc);
+ CurrentUserUtils.setupPresentations(doc);
+ CurrentUserUtils.setupRecentlyClosedDocs(doc);
+ CurrentUserUtils.setupFilterDocs(doc);
CurrentUserUtils.setupUserDoc(doc);
}
@@ -847,8 +850,8 @@ export class CurrentUserUtils {
}
// sets up the default set of documents to be shown in the Overlay layer
static setupOverlays(doc: Doc) {
- if (doc.myOverlayDocuments === undefined) {
- doc.myOverlayDocuments = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "overlay documents", backgroundColor: "#aca3a6", system: true }));
+ if (doc.myOverlayDocs === undefined) {
+ doc.myOverlayDocs = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "overlay documents", backgroundColor: "#aca3a6", system: true }));
}
}
@@ -859,34 +862,30 @@ export class CurrentUserUtils {
title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data", system: true
}));
}
- if (doc.activePresentation === undefined) {
- doc.activePresentation = Doc.MakeCopy(doc.emptyPresentation as Doc, true);
- }
}
// Sharing sidebar is where shared documents are contained
static setupSharingSidebar(doc: Doc) {
- if (doc["sidebar-sharing"] === undefined) {
- doc["sidebar-sharing"] = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Shared Documents", childDropAction: "alias", system: true }));
+ if (doc.mySharedDocs === undefined) {
+ doc.mySharedDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "My SharedDocs", childDropAction: "alias", system: true, _yMargin: 50, _gridGap: 15, _showTitle: "title", ignoreClick: true, lockedPosition: true }));
}
}
// Import sidebar is where shared documents are contained
static setupImportSidebar(doc: Doc) {
- if (doc["sidebar-import-documents"] === undefined) {
- doc["sidebar-import-documents"] = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Imported Documents", forceActive: true, _showTitle: "title", childDropAction: "alias", _autoHeight: true, _yMargin: 30, lockedPosition: true, _chromeStatus: "disabled" }));
+ if (doc.myImportDocs === undefined) {
+ doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "My ImportDocuments", forceActive: true, ignoreClick: true, _showTitle: "title", childDropAction: "alias", _autoHeight: true, _yMargin: 50, _gridGap: 15, lockedPosition: true, _chromeStatus: "disabled", system: true }));
}
- if (doc["sidebar-import"] === undefined) {
- const uploads = Cast(doc["sidebar-import-documents"], Doc, null);
+ if (doc.myImportPanel === undefined) {
+ const uploads = Cast(doc.myImportDocs, Doc, null);
const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _backgroundColor: "black", title: "Import", icon: "upload", system: true });
- doc["sidebar-import"] = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "Imported Documents", _yMargin: 20, ignoreClick: true, lockedPosition: true }));
+ doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, ignoreClick: true, lockedPosition: true, system: true }));
}
}
-
static setupClickEditorTemplates(doc: Doc) {
if (doc["clickFuncs-child"] === undefined) {
- // to use this function, select it from the context menu of a collection. then edit the onChildClick script. Add two Doc variables: 'target' and 'thisContainer', then assign 'target' to some target collection. After that, clicking on any document in the initial collection will open it in the target
+ // to use this function, select it from the context menu of a collection. then edit the onChildClick script. Add two Doc variables: 'target' and 'thisContainer', then assign 'target' to some target collection. After that, clicking on any document in the initial collection will open it in the target
const openInTarget = Docs.Create.ScriptingDocument(ScriptField.MakeScript(
"docCast(thisContainer.target).then((target) => target && (target.proto.data = new List([self]))) ",
{ thisContainer: Doc.name }), {
@@ -933,6 +932,7 @@ export class CurrentUserUtils {
}
static async updateUserDocument(doc: Doc) {
+ doc.system = true;
doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode;
doc.title = Doc.CurrentUserEmail;
doc.activeInkPen = doc;
@@ -958,11 +958,11 @@ export class CurrentUserUtils {
this.setupDocTemplates(doc); // sets up the template menu of templates
this.setupImportSidebar(doc);
this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
- this.setupMenuPanel(doc);
this.setupSearchPanel(doc);
this.setupOverlays(doc); // documents in overlay layer
this.setupDockedButtons(doc); // the bottom bar of font icons
await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels
+ this.setupMenuPanel(doc);
doc.globalLinkDatabase = Docs.Prototypes.MainLinkDocument();
doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument();
doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument();
@@ -975,6 +975,7 @@ export class CurrentUserUtils {
return doc;
}
+
public static async loadCurrentUser() {
return rp.get(Utils.prepend("/getCurrentUser")).then(response => {
if (response) {
@@ -998,12 +999,141 @@ export class CurrentUserUtils {
}
});
}
-}
-Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); },
- "creates a new workspace when called");
+ public static _urlState: HistoryUtil.DocUrl;
+
+ public static openDashboard = (userDoc: Doc, doc: Doc, fromHistory = false) => {
+ CurrentUserUtils.MainDocId = doc[Id];
+
+ if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
+ !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
+ userDoc ? (userDoc.activeDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
+ }
+ const state = CurrentUserUtils._urlState;
+ if (state.sharing === true && !userDoc) {
+ DocServer.Control.makeReadOnly();
+ } else {
+ fromHistory || HistoryUtil.pushState({
+ type: "doc",
+ docId: doc[Id],
+ readonly: state.readonly,
+ nro: state.nro,
+ sharing: false,
+ });
+ if (state.readonly === true || state.readonly === null) {
+ DocServer.Control.makeReadOnly();
+ } else if (state.safe) {
+ if (!state.nro) {
+ DocServer.Control.makeReadOnly();
+ }
+ CollectionView.SetSafeMode(true);
+ } else if (state.nro || state.nro === null || state.readonly === false) {
+ } else if (doc.readOnly) {
+ DocServer.Control.makeReadOnly();
+ } else {
+ DocServer.Control.makeEditable();
+ }
+ }
+
+ return true;
+ }
+ public static importDocument = () => {
+ const input = document.createElement("input");
+ input.type = "file";
+ input.multiple = true;
+ input.accept = ".zip, application/pdf, video/*, image/*, audio/*";
+ input.onchange = async _e => {
+ const upload = Utils.prepend("/uploadDoc");
+ const formData = new FormData();
+ const file = input.files && input.files[0];
+ if (file && file.type === 'application/zip') {
+ formData.append('file', file);
+ formData.append('remap', "true");
+ const response = await fetch(upload, { method: "POST", body: formData });
+ const json = await response.json();
+ if (json !== "error") {
+ const doc = await DocServer.GetRefField(json);
+ if (doc instanceof Doc) {
+ setTimeout(() => SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs =>
+ docs.docs.forEach(d => LinkManager.Instance.addLink(d))), 2000); // need to give solr some time to update so that this query will find any link docs we've added.
+ }
+ }
+ } else if (input.files && input.files.length !== 0) {
+ const importDocs = Cast(Doc.UserDoc().myImportDocs, Doc, null);
+ const disposer = OverlayView.ShowSpinner();
+ DocListCastAsync(importDocs.data).then(async list => {
+ const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {});
+ list?.splice(0, 0, ...results);
+ disposer();
+ });
+ } else {
+ console.log("No file selected");
+ }
+ };
+ input.click();
+ }
+
+ public static snapshotDashboard = (userDoc: Doc) => {
+ const copy = CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
+ Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy);
+ CurrentUserUtils.openDashboard(userDoc, copy);
+ }
+
+ public static createNewDashboard = (userDoc: Doc, id?: string) => {
+ const myPresentations = userDoc.myPresentations as Doc;
+ const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true);
+ const dashboards = Cast(userDoc.myDashboards, Doc) as Doc;
+ const dashboardCount = DocListCast(dashboards.data).length + 1;
+ const emptyPane = Cast(userDoc.emptyPane, Doc, null);
+ emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
+ const freeformOptions: DocumentOptions = {
+ x: 0,
+ y: 400,
+ _width: 1500,
+ _height: 1000,
+ title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`,
+ };
+ const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
+ const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row");
+ Doc.AddDocToList(myPresentations, "data", presentation);
+ userDoc.activePresentation = presentation;
+ const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`);
+ const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
+ const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`);
+ const createDashboard = ScriptField.MakeScript(`createNewDashboard()`);
+ dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, snapshotDashboard!, createDashboard!]);
+ dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard"]);
+
+ Doc.AddDocToList(dashboards, "data", dashboardDoc);
+ CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
+ }
+
+ public static get MySearchPanelDoc() { return Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null); }
+ public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); }
+ public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); }
+ public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); }
+ public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); }
+ public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); }
+}
+
+Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
+ const copy = Doc.copyDragFactory(dragFactory);
+ if (copy) {
+ CollectionDockingView.AddSplit(copy, "right");
+ const view = DocumentManager.Instance.getFirstDocumentView(copy);
+ view && SelectionManager.SelectDoc(view, false);
+ }
+});
+Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); },
+ "creates a snapshot copy of a dashboard");
+Scripting.addGlobal(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); },
+ "creates a new dashboard when called");
+Scripting.addGlobal(function createNewPresentation() { return MainView.Instance.createNewPresentation(); },
+ "creates a new presentation when called");
Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); },
"returns all the links to the document or its annotations", "(doc: any)");
Scripting.addGlobal(function directLinks(doc: any) { return new List(LinkManager.Instance.getAllDirectLinks(doc)); },
"returns all the links directly to the document", "(doc: any)");
+Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); },
+ "imports files from device directly into the import sidebar");
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 540540642..231e1fa8d 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -6,7 +6,6 @@ import { DocumentType } from "../documents/DocumentTypes";
import { Doc, Opt } from "../../fields/Doc";
import { List } from "../../fields/List";
import { Docs } from "../documents/Documents";
-import { CollectionViewType } from "../views/collections/CollectionView";
import { Cast, CastCtor } from "../../fields/Types";
import { listSpec } from "../../fields/Schema";
import { AudioField, ImageField } from "../../fields/URLField";
@@ -88,7 +87,7 @@ export namespace DictationManager {
export const listen = async (options?: Partial<ListeningOptions>) => {
let results: string | undefined;
- const overlay = options !== undefined && options.useOverlay;
+ const overlay = options?.useOverlay;
if (overlay) {
DictationOverlay.Instance.dictationOverlayVisible = true;
DictationOverlay.Instance.isListening = { interim: false };
@@ -100,11 +99,11 @@ export namespace DictationManager {
Utils.CopyText(results);
if (overlay) {
DictationOverlay.Instance.isListening = false;
- const execute = options && options.tryExecute;
+ const execute = options?.tryExecute;
DictationOverlay.Instance.dictatedPhrase = execute ? results.toLowerCase() : results;
DictationOverlay.Instance.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true;
}
- options && options.tryExecute && await DictationManager.Commands.execute(results);
+ options?.tryExecute && await DictationManager.Commands.execute(results);
}
} catch (e) {
if (overlay) {
@@ -129,12 +128,12 @@ export namespace DictationManager {
}
isListening = true;
- const handler = options ? options.interimHandler : undefined;
- const continuous = options ? options.continuous : undefined;
+ const handler = options?.interimHandler;
+ const continuous = options?.continuous;
const indefinite = continuous && continuous.indefinite;
- const language = options ? options.language : undefined;
- const intra = options && options.delimiters ? options.delimiters.intra : undefined;
- const inter = options && options.delimiters ? options.delimiters.inter : undefined;
+ const language = options?.language;
+ const intra = options?.delimiters?.intra;
+ const inter = options?.delimiters?.inter;
recognizer.onstart = () => console.log("initiating speech recognition session...");
recognizer.interimResults = handler !== undefined;
@@ -154,7 +153,7 @@ export namespace DictationManager {
recognizer.onresult = (e: SpeechRecognitionEvent) => {
current = synthesize(e, intra);
let matchedTerminator: string | undefined;
- if (options && options.terminators && (matchedTerminator = options.terminators.find(end => current ? current.trim().toLowerCase().endsWith(end.toLowerCase()) : false))) {
+ if (options?.terminators && (matchedTerminator = options.terminators.find(end => current ? current.trim().toLowerCase().endsWith(end.toLowerCase()) : false))) {
current = matchedTerminator;
recognizer.abort();
return complete();
@@ -324,7 +323,7 @@ export namespace DictationManager {
["open fields", {
action: (target: DocumentView) => {
const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 });
- target.props.addDocTab(kvp, "onRight");
+ target.props.addDocTab(kvp, "add:right");
}
}],
@@ -338,7 +337,7 @@ export namespace DictationManager {
const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"ordered_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`;
proto.data = new RichTextField(proseMirrorState);
proto.backgroundColor = "#eeffff";
- target.props.addDocTab(newBox, "onRight");
+ target.props.addDocTab(newBox, "add:right");
}
}]
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 962294933..420b29559 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -124,7 +124,7 @@ export class DocumentManager {
}
static addRightSplit = (doc: Doc, finished?: () => void) => {
- CollectionDockingView.AddRightSplit(doc);
+ CollectionDockingView.AddSplit(doc, "right");
finished?.();
}
public jumpToDocument = async (
@@ -160,6 +160,9 @@ export class DocumentManager {
docView.props.Document.hidden = !docView.props.Document.hidden;
}
else {
+ const contView = docContext && getFirstDocView(docContext, originatingDoc);
+ contView && contView.topMost && contView.select(false); // bcz: change this to a function prop: popTab() that will make sure the tab for the document is topmost;
+ docView.select(false);
docView.props.Document.hidden && (docView.props.Document.hidden = undefined);
docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish);
highlight();
@@ -181,7 +184,7 @@ export class DocumentManager {
// now find the target document within the context
if (targetDoc.displayTimecode) { // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode;
- targetDocContext.currentTimecode = targetDoc.displayTimecode;
+ targetDocContext._currentTimecode = targetDoc.displayTimecode;
finished?.();
} else { // no timecode means we need to find the context view and focus on our target
setTimeout(() => {
@@ -212,8 +215,8 @@ export class DocumentManager {
public async FollowLink(link: Opt<Doc>, doc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
const linkDocs = link ? [link] : DocListCast(doc.links);
SelectionManager.DeselectAll();
- const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc)); // link docs where 'doc' is anchor1
- const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc)); // link docs where 'doc' is anchor2
+ const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor1
+ const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor2
const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
@@ -222,17 +225,16 @@ export class DocumentManager {
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;
+ (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, 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")));
+ (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc) ? Cast(linkDoc.anchor2_timecode, "number") : Cast(linkDoc.anchor1_timecode, "number")));
if (target) {
const containerDoc = (await Cast(target.annotationOn, Doc)) || target;
- containerDoc.currentTimecode = targetTimecode;
+ containerDoc._currentTimecode = targetTimecode;
const targetContext = await target?.context as Doc;
const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined;
- console.log(targetNavContext);
- DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "onRight"), finished), targetNavContext, linkDoc, undefined, doc, finished);
+ DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "add:right"), finished), targetNavContext, linkDoc, undefined, doc, finished);
} else {
finished?.();
}
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 0cca61841..9797f5488 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -117,10 +117,11 @@ export namespace DragManager {
}
export class DocumentDragData {
- constructor(dragDoc: Doc[]) {
+ constructor(dragDoc: Doc[], dropAction?: dropActionType) {
this.draggedDocuments = dragDoc;
this.droppedDocuments = [];
this.offset = [0, 0];
+ this.dropAction = dropAction;
}
draggedDocuments: Doc[];
droppedDocuments: Doc[];
@@ -143,7 +144,7 @@ export namespace DragManager {
linkSourceDocument: Doc;
dontClearTextBox?: boolean;
linkDocument?: Doc;
- linkDropCallback?: (data: LinkDragData) => void;
+ linkDropCallback?: (data: { linkDocument?: Doc }) => void;
}
export class ColumnDragData {
constructor(colKey: SchemaHeaderField) {
@@ -160,7 +161,7 @@ export namespace DragManager {
this.annotationDocument = annotationDoc;
this.offset = [0, 0];
}
- linkedToDoc?: boolean;
+ linkDocument?: Doc;
targetContext: Doc | undefined;
dragDocument: Doc;
annotationDocument: Doc;
@@ -168,6 +169,7 @@ export namespace DragManager {
offset: number[];
dropAction: dropActionType;
userDropAction: dropActionType;
+ linkDropCallback?: (data: { linkDocument?: Doc }) => void;
}
export function MakeDropTarget(
@@ -434,7 +436,7 @@ export namespace DragManager {
const target = document.elementFromPoint(e.x, e.y);
- if (target && !options?.noAutoscroll && !dragData.draggedDocuments?.some((d: any) => d._noAutoscroll)) {
+ if (target && !Doc.UserDoc()._noAutoscroll && !options?.noAutoscroll && !dragData.draggedDocuments?.some((d: any) => d._noAutoscroll)) {
const autoScrollHandler = () => {
target.dispatchEvent(
new CustomEvent<React.DragEvent>("dashDragAutoScroll", {
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index d03989675..cb15b5081 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -1,24 +1,19 @@
-import * as React from "react";
-import { observable, action, runInAction, computed } from "mobx";
-import { SelectionManager } from "./SelectionManager";
-import MainViewModal from "../views/MainViewModal";
-import { observer } from "mobx-react";
-import { Doc, DocListCast, Opt, DocListCastAsync } from "../../fields/Doc";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import * as fa from '@fortawesome/free-solid-svg-icons';
-import { library } from "@fortawesome/fontawesome-svg-core";
-import SharingManager, { User } from "./SharingManager";
-import { Utils } from "../../Utils";
-import * as RequestPromise from "request-promise";
+import { action, computed, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import * as React from "react";
import Select from 'react-select';
-import "./GroupManager.scss";
-import { StrCast, Cast } from "../../fields/Types";
-import GroupMemberView from "./GroupMemberView";
+import * as RequestPromise from "request-promise";
+import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
+import { Cast, StrCast } from "../../fields/Types";
import { setGroups } from "../../fields/util";
+import { Utils } from "../../Utils";
import { DocServer } from "../DocServer";
+import { MainViewModal } from "../views/MainViewModal";
import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
-
-library.add(fa.faPlus, fa.faTimes, fa.faInfoCircle, fa.faCaretUp, fa.faCaretRight, fa.faCaretDown);
+import "./GroupManager.scss";
+import { GroupMemberView } from "./GroupMemberView";
+import { SharingManager, User } from "./SharingManager";
/**
* Interface for options for the react-select component
@@ -29,7 +24,7 @@ export interface UserOptions {
}
@observer
-export default class GroupManager extends React.Component<{}> {
+export class GroupManager extends React.Component<{}> {
static Instance: GroupManager;
@observable isOpen: boolean = false; // whether the GroupManager is to be displayed or not.
@@ -71,7 +66,7 @@ export default class GroupManager extends React.Component<{}> {
const evaluating = raw.map(async user => {
const userDocument = await DocServer.GetRefField(user.userDocumentId);
if (userDocument instanceof Doc) {
- const notificationDoc = await Cast(userDocument["sidebar-sharing"], Doc);
+ const notificationDoc = await Cast(userDocument.mySharedDocs, Doc);
runInAction(() => {
if (notificationDoc instanceof Doc) {
this.users.push(user.email);
@@ -92,6 +87,7 @@ export default class GroupManager extends React.Component<{}> {
const members: string[] = JSON.parse(StrCast(group.members));
if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(StrCast(group.groupName));
});
+ this.currentUserGroups.push("Public");
setGroups(this.currentUserGroups);
});
}
@@ -121,6 +117,7 @@ export default class GroupManager extends React.Component<{}> {
close = () => {
this.isOpen = false;
this.currentGroup = undefined;
+ this.selectedUsers = null;
// this.users = [];
this.createGroupModalOpen = false;
TaskCompletionBox.taskCompleted = false;
@@ -162,7 +159,7 @@ export default class GroupManager extends React.Component<{}> {
* @returns the members of the admin group.
*/
get adminGroupMembers(): string[] {
- return this.getGroup("admin") ? JSON.parse(StrCast(this.getGroup("admin")!.members)) : "";
+ return this.getGroup("Admin") ? JSON.parse(StrCast(this.getGroup("Admin")!.members)) : "";
}
/**
@@ -182,7 +179,7 @@ export default class GroupManager extends React.Component<{}> {
*/
createGroupDoc(groupName: string, memberEmails: string[] = []) {
const groupDoc = new Doc;
- groupDoc.groupName = groupName;
+ groupDoc.groupName = groupName.toLowerCase() === "admin" ? "Admin" : groupName;
groupDoc.owners = JSON.stringify([Doc.CurrentUserEmail]);
groupDoc.members = JSON.stringify(memberEmails);
if (memberEmails.includes(Doc.CurrentUserEmail)) {
@@ -285,7 +282,11 @@ export default class GroupManager extends React.Component<{}> {
alert("Please enter a group name");
return;
}
- if (this.getAllGroups().find(group => group.groupName === this.inputRef.current!.value)) { // why do I need a null check here?
+ if (this.inputRef.current.value.toLowerCase() === "admin" && this.getGroup("Admin")) {
+ alert("You cannot override the Admin group");
+ return;
+ }
+ if (this.getGroup(this.inputRef.current.value)) {
alert("Please select a unique group name");
return;
}
@@ -314,7 +315,7 @@ export default class GroupManager extends React.Component<{}> {
<div className={"close-button"} onClick={action(() => {
this.createGroupModalOpen = false; TaskCompletionBox.taskCompleted = false;
})}>
- <FontAwesomeIcon icon={fa.faTimes} color={"black"} size={"lg"} />
+ <FontAwesomeIcon icon={"times"} color={"black"} size={"lg"} />
</div>
</div>
<input
@@ -398,19 +399,19 @@ export default class GroupManager extends React.Component<{}> {
<div className="group-heading">
<p><b>Manage Groups</b></p>
<button onClick={action(() => this.createGroupModalOpen = true)}>
- <FontAwesomeIcon icon={fa.faPlus} size={"sm"} /> Create Group
+ <FontAwesomeIcon icon={"plus-hexagon"} size={"sm"} /> Create Group
</button>
<div className={"close-button"} onClick={this.close}>
- <FontAwesomeIcon icon={fa.faTimes} color={"black"} size={"lg"} />
+ <FontAwesomeIcon icon={"times"} color={"black"} size={"lg"} />
</div>
</div>
<div className="main-container">
<div
className="sort-groups"
onClick={action(() => this.groupSort = this.groupSort === "ascending" ? "descending" : this.groupSort === "descending" ? "none" : "ascending")}>
- Name {this.groupSort === "ascending" ? <FontAwesomeIcon icon={fa.faCaretUp} size={"xs"} />
- : this.groupSort === "descending" ? <FontAwesomeIcon icon={fa.faCaretDown} size={"xs"} />
- : <FontAwesomeIcon icon={fa.faCaretRight} size={"xs"} />
+ Name {this.groupSort === "ascending" ? <FontAwesomeIcon icon={"caret-up"} size={"xs"} />
+ : this.groupSort === "descending" ? <FontAwesomeIcon icon={"caret-down"} size={"xs"} />
+ : <FontAwesomeIcon icon={"caret-right"} size={"xs"} />
}
</div>
<div className="group-body">
@@ -421,7 +422,7 @@ export default class GroupManager extends React.Component<{}> {
>
<div className="group-name" >{group.groupName}</div>
<div className="group-info" onClick={action(() => this.currentGroup = group)}>
- <FontAwesomeIcon icon={fa.faInfoCircle} color={"#e8e8e8"} size={"sm"} style={{ backgroundColor: "#1e89d7", borderRadius: "100%", border: "1px solid #1e89d7" }} />
+ <FontAwesomeIcon icon={"info-circle"} color={"#e8e8e8"} size={"sm"} style={{ backgroundColor: "#1e89d7", borderRadius: "100%", border: "1px solid #1e89d7" }} />
</div>
</div>
)}
diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx
index 531ef988a..4ead01e9f 100644
--- a/src/client/util/GroupMemberView.tsx
+++ b/src/client/util/GroupMemberView.tsx
@@ -1,25 +1,21 @@
-import * as React from "react";
-import MainViewModal from "../views/MainViewModal";
-import { observer } from "mobx-react";
-import GroupManager, { UserOptions } from "./GroupManager";
-import { library } from "@fortawesome/fontawesome-svg-core";
-import { StrCast } from "../../fields/Types";
-import { action, observable } from "mobx";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import * as fa from '@fortawesome/free-solid-svg-icons';
+import { action, observable } from "mobx";
+import { observer } from "mobx-react";
+import * as React from "react";
import Select from "react-select";
import { Doc } from "../../fields/Doc";
+import { StrCast } from "../../fields/Types";
+import { MainViewModal } from "../views/MainViewModal";
+import { GroupManager, UserOptions } from "./GroupManager";
import "./GroupMemberView.scss";
-library.add(fa.faTimes, fa.faTrashAlt);
-
interface GroupMemberViewProps {
group: Doc;
onCloseButtonClick: () => void;
}
@observer
-export default class GroupMemberView extends React.Component<GroupMemberViewProps> {
+export class GroupMemberView extends React.Component<GroupMemberViewProps> {
@observable private memberSort: "ascending" | "descending" | "none" = "none";
@@ -43,7 +39,7 @@ export default class GroupMemberView extends React.Component<GroupMemberViewProp
>
</input>
<div className={"memberView-closeButton"} onClick={action(this.props.onCloseButtonClick)}>
- <FontAwesomeIcon icon={fa.faTimes} color={"black"} size={"lg"} />
+ <FontAwesomeIcon icon={"times"} color={"black"} size={"lg"} />
</div>
{GroupManager.Instance.hasEditAccess(this.props.group) ?
<div className="group-buttons">
@@ -88,7 +84,7 @@ export default class GroupMemberView extends React.Component<GroupMemberViewProp
</div>
{hasEditAccess ?
<div className={"remove-button"} onClick={() => GroupManager.Instance.removeMemberFromGroup(this.props.group, member)}>
- <FontAwesomeIcon icon={fa.faTrashAlt} size={"sm"} />
+ <FontAwesomeIcon icon={"trash-alt"} size={"sm"} />
</div>
: null}
</div>
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 7b7d4b835..cbe36b401 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -1,8 +1,8 @@
import { Doc } from "../../fields/Doc";
import { DocServer } from "../DocServer";
-import { MainView } from "../views/MainView";
import * as qs from 'query-string';
import { Utils, OmitKeys } from "../../Utils";
+import { CurrentUserUtils } from "./CurrentUserUtils";
export namespace HistoryUtil {
export interface DocInitializerList {
@@ -197,7 +197,7 @@ export namespace HistoryUtil {
await Promise.all(Object.keys(init).map(id => initDoc(id, init[id])));
}
if (field instanceof Doc) {
- MainView.Instance.openWorkspace(field, true);
+ CurrentUserUtils.openDashboard(Doc.UserDoc(), field, true);
}
}
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index 9ede94e4b..4a5b52e1e 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -21,7 +21,7 @@ export namespace Hypothesis {
export const getSourceWebDoc = async (uri: string) => {
const result = await findWebDoc(uri);
console.log(result ? "existing doc found" : "existing doc NOT found");
- return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _nativeHeight: 962, _width: 400, UseCors: true }); // create and return a new Web doc with given uri if no matching docs are found
+ return result || Docs.Create.WebDocument(uri, { title: uri, _nativeWidth: 850, _nativeHeight: 962, _width: 400, useCors: true }); // create and return a new Web doc with given uri if no matching docs are found
};
@@ -34,7 +34,7 @@ export namespace Hypothesis {
const results: Doc[] = [];
await SearchUtil.Search("web", true).then(action(async (res: SearchUtil.DocSearchResult) => {
- const docs = await Promise.all(res.docs.map(async doc => (await Cast(doc.extendsDoc, Doc)) || doc));
+ const docs = res.docs;
const filteredDocs = docs.filter(doc =>
doc.author === Doc.CurrentUserEmail && doc.type === DocumentType.WEB && doc.data
);
@@ -60,6 +60,7 @@ export namespace Hypothesis {
DocumentLinksButton.AnnotationId = annotationId;
DocumentLinksButton.AnnotationUri = annotationUri;
DocumentLinksButton.StartLink = sourceDoc;
+ DocumentLinksButton.StartLinkView = undefined;
});
} else { // if a link has already been started, complete the link to sourceDoc
runInAction(() => {
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 77f13e9f4..d04270afa 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -1,5 +1,3 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faCloudUploadAlt, faPlus, faTag } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { BatchedArray } from "array-batcher";
import "fs";
@@ -47,7 +45,6 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
constructor(props: FieldViewProps) {
super(props);
- library.add(faTag, faPlus);
const doc = this.props.Document;
this.editingMetadata = this.editingMetadata || false;
this.persistent = this.persistent || false;
@@ -301,7 +298,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
opacity: uploading ? 0 : 1,
transition: "0.4s opacity ease"
}}>
- <FontAwesomeIcon icon={faCloudUploadAlt} color="#FFFFFF" size={"2x"} />
+ <FontAwesomeIcon icon={"cloud-upload-alt"} color="#FFFFFF" size={"2x"} />
</div>
<img
style={{
@@ -366,7 +363,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
opacity: uploading ? 0 : 1,
transition: "0.4s opacity ease"
}}
- icon={isEditing ? faCloudUploadAlt : faTag}
+ icon={isEditing ? "cloud-upload-alt" : "tag"}
color="#FFFFFF"
size={"1x"}
/>
@@ -399,7 +396,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
marginLeft: 6.4,
marginTop: 5.2
}}
- icon={faPlus}
+ icon={"plus"}
size={"1x"}
/>
</div>
diff --git a/src/client/util/Import & Export/ImportMetadataEntry.tsx b/src/client/util/Import & Export/ImportMetadataEntry.tsx
index dcb94e2e0..1870213b9 100644
--- a/src/client/util/Import & Export/ImportMetadataEntry.tsx
+++ b/src/client/util/Import & Export/ImportMetadataEntry.tsx
@@ -4,7 +4,6 @@ import { EditableView } from "../../views/EditableView";
import { action, computed } from "mobx";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPlus } from "@fortawesome/free-solid-svg-icons";
-import { library } from '@fortawesome/fontawesome-svg-core';
import { Doc } from "../../../fields/Doc";
import { StrCast, BoolCast } from "../../../fields/Types";
@@ -24,11 +23,6 @@ export default class ImportMetadataEntry extends React.Component<KeyValueProps>
private valueRef = React.createRef<EditableView>();
private checkRef = React.createRef<HTMLInputElement>();
- constructor(props: KeyValueProps) {
- super(props);
- library.add(faPlus);
- }
-
@computed
public get valid() {
return (this.key.length > 0 && this.key !== keyPlaceholder) && (this.value.length > 0 && this.value !== valuePlaceholder);
@@ -132,7 +126,7 @@ export default class ImportMetadataEntry extends React.Component<KeyValueProps>
</div>
<div onClick={() => this.props.remove(this)} title={"Delete Entry"}>
<FontAwesomeIcon
- icon={faPlus}
+ icon={"plus"}
color={"red"}
size={"1x"}
style={{
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index ae3b3e064..4a203d41f 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -132,7 +132,6 @@ export namespace InteractionUtils {
if (isNaN(scaley)) {
scaley = 1;
}
- console.log(pts.length);
return pts;
}
@@ -140,7 +139,7 @@ export namespace InteractionUtils {
export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number,
color: string, width: number, strokeWidth: number, bezier: string, fill: string, arrowStart: string, arrowEnd: string,
- dash: string, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean, nodefs: boolean) {
+ dash: string | undefined, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean, nodefs: boolean) {
let pts: { X: number; Y: number; }[] = [];
if (shape) { //if any of the shape are true
pts = makePolygon(shape, points);
@@ -182,13 +181,13 @@ export namespace InteractionUtils {
const strpts = pts.reduce((acc: string, pt: { X: number, Y: number }) => acc +
`${(pt.X - left - width / 2) * scalex + width / 2},
${(pt.Y - top - width / 2) * scaley + width / 2} `, "");
- const dashArray = String(Number(width) * Number(dash));
+ const dashArray = dash && Number(dash) ? String(Number(width) * Number(dash)) : undefined;
const defGuid = Utils.GenerateGuid();
const arrowDim = Math.max(0.5, 8 / Math.log(Math.max(2, strokeWidth)));
const addables = pts.map((pts, i) =>
<svg height="10" width="10">
- <circle cx={(pts.X - left - width / 2) * scalex + width / 2} cy={(pts.Y - top - width / 2) * scaley + width / 2} r={strokeWidth / 2} stroke="black" stroke-width={1} fill="blue"
+ <circle cx={(pts.X - left - width / 2) * scalex + width / 2} cy={(pts.Y - top - width / 2) * scaley + width / 2} r={strokeWidth / 2} stroke="black" strokeWidth={1} fill="blue"
onDoubleClick={(e) => { console.log(i); }} pointerEvents="all" cursor="all-scroll"
/>
</svg>);
@@ -210,7 +209,7 @@ export namespace InteractionUtils {
points={strpts}
style={{
filter: drawHalo ? "url(#inkSelectionHalo)" : undefined,
- fill: fill ? fill : "transparent",
+ fill: fill ? fill : "none",
opacity: strokeWidth !== width ? 0.5 : undefined,
pointerEvents: pevents as any,
stroke: color ?? "rgb(0, 0, 0)",
@@ -300,19 +299,52 @@ export namespace InteractionUtils {
return points;
case "circle":
+ // const centerX = (right + left) / 2;
+ // const centerY = (bottom + top) / 2;
+ // const radius = bottom - centerY;
+
+ // for (var y = top; y < bottom; y++) {
+ // const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ // points.push({ X: x, Y: y });
+ // }
+ // for (var y = bottom; y > top; y--) {
+ // const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ // const newX = centerX - (x - centerX);
+ // points.push({ X: newX, Y: y });
+ // }
+ // points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
const centerX = (right + left) / 2;
const centerY = (bottom + top) / 2;
- const radius = bottom - centerY;
- for (var y = top; y < bottom; y++) {
- const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
- points.push({ X: x, Y: y });
- }
- for (var y = bottom; y > top; y--) {
- const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
- const newX = centerX - (x - centerX);
- points.push({ X: newX, Y: y });
+ if ((bottom - centerY) < (right - centerX)) {
+ const radius = bottom - centerY;
+ for (var y = top; y < bottom; y++) {
+ const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ points.push({ X: x, Y: y });
+ }
+ for (var y = bottom; y > top; y--) {
+ const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ const newX = centerX - (x - centerX);
+ points.push({ X: newX, Y: y });
+ }
+ points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
+
+ } else {
+ //right = bottom
+ //left = top
+ const radius = right - centerX;
+ for (var x = left; x < right; x++) {
+ const y = Math.sqrt(Math.pow(radius, 2) - (Math.pow((x - centerX), 2))) + centerY;
+ points.push({ X: x, Y: y });
+ }
+ for (var x = right; x > left; x--) {
+ const y = Math.sqrt(Math.pow(radius, 2) - (Math.pow((x - centerX), 2))) + centerY;
+ const newY = centerY - (y - centerY);
+ points.push({ X: x, Y: newY });
+ }
+ points.push({ X: left, Y: Math.sqrt(Math.pow(radius, 2) - (Math.pow((left - centerX), 2))) + centerY });
+
+
}
- points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
return points;
// case "arrow":
// const x1 = left;
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index b63fc8991..b09eff849 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -23,12 +23,14 @@ export namespace SearchUtil {
}
export interface SearchParams {
- hl?: boolean;
+ hl?: string;
"hl.fl"?: string;
start?: number;
rows?: number;
fq?: string;
+ sort?: string;
allowAliases?: boolean;
+ onlyAliases?: boolean;
"facet"?: string;
"facet.field"?: string;
}
@@ -37,8 +39,12 @@ export namespace SearchUtil {
export async function Search(query: string, returnDocs: boolean, options: SearchParams = {}) {
query = query || "*"; //If we just have a filter query, search for * as the query
const rpquery = Utils.prepend("/dashsearch");
- const replacedQuery = query.replace(/type_t:([^ )])/g, (substring, arg) => `{!join from=id to=proto_i}type_t:${arg}`);
- const gotten = await rp.get(rpquery, { qs: { ...options, sort: "lastModified_d desc", q: replacedQuery } });
+ let replacedQuery = query.replace(/type_t:([^ )])/g, (substring, arg) => `{!join from=id to=proto_i}*:* AND ${arg}`);
+ if (options.onlyAliases) {
+ const header = query.match(/_[atnb]?:/) ? replacedQuery : "DEFAULT:" + replacedQuery;
+ replacedQuery = `{!join from=id to=proto_i}${header}`;
+ }
+ const gotten = await rp.get(rpquery, { qs: { ...options, q: replacedQuery } });
const result: IdSearchResult = gotten.startsWith("<") ? { ids: [], docs: [], numFound: 0, lines: [] } : JSON.parse(gotten);
if (!returnDocs) {
return result;
@@ -80,6 +86,8 @@ export namespace SearchUtil {
if (testDoc instanceof Doc && testDoc.type !== DocumentType.KVP && (options.allowAliases || testDoc.proto === undefined || theDocs.findIndex(d => Doc.AreProtosEqual(d, testDoc)) === -1)) {
theDocs.push(testDoc);
theLines.push([]);
+ } else {
+ result.numFound--;
}
}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 113278593..730db4f29 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,10 +1,10 @@
import { observable, action, runInAction, ObservableMap } from "mobx";
-import { Doc } from "../../fields/Doc";
+import { Doc, Opt } from "../../fields/Doc";
import { DocumentView } from "../views/nodes/DocumentView";
import { computedFn } from "mobx-utils";
import { List } from "../../fields/List";
-import { Scripting } from "./Scripting";
-import { DocumentManager } from "./DocumentManager";
+import { CollectionSchemaView } from "../views/collections/CollectionSchemaView";
+import { CollectionViewType } from "../views/collections/CollectionView";
export namespace SelectionManager {
@@ -12,10 +12,16 @@ export namespace SelectionManager {
@observable IsDragging: boolean = false;
SelectedDocuments: ObservableMap<DocumentView, boolean> = new ObservableMap();
+ @observable SelectedSchemaDocument: Doc | undefined;
+ @observable SelectedSchemaCollection: CollectionSchemaView | undefined;
@action
+ SelectSchemaDoc(collectionView: Opt<CollectionSchemaView>, doc: Opt<Doc>) {
+ manager.SelectedSchemaDocument = doc;
+ manager.SelectedSchemaCollection = collectionView;
+ }
+ @action
SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
-
// if doc is not in SelectedDocuments, add it
if (!manager.SelectedDocuments.get(docView)) {
if (!ctrlPressed) {
@@ -26,6 +32,8 @@ export namespace SelectionManager {
docView.props.whenActiveChanged(true);
} else if (!ctrlPressed && Array.from(manager.SelectedDocuments.entries()).length > 1) {
Array.from(manager.SelectedDocuments.keys()).map(dv => dv !== docView && dv.props.whenActiveChanged(false));
+ manager.SelectedSchemaDocument = undefined;
+ manager.SelectedSchemaCollection = undefined;
manager.SelectedDocuments.clear();
manager.SelectedDocuments.set(docView, true);
}
@@ -42,7 +50,8 @@ export namespace SelectionManager {
}
@action
DeselectAll(): void {
-
+ manager.SelectedSchemaCollection = undefined;
+ manager.SelectedSchemaDocument = undefined;
Array.from(manager.SelectedDocuments.keys()).map(dv => dv.props.whenActiveChanged(false));
manager.SelectedDocuments.clear();
Doc.UserDoc().activeSelection = new List<Doc>([]);
@@ -57,6 +66,9 @@ export namespace SelectionManager {
export function SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
manager.SelectDoc(docView, ctrlPressed);
}
+ export function SelectSchemaDoc(colSchema: Opt<CollectionSchemaView>, document: Opt<Doc>): void {
+ manager.SelectSchemaDoc(colSchema, document);
+ }
// computed functions, such as used in IsSelected generate errors if they're called outside of a
// reaction context. Specifying the context with 'outsideReaction' allows an efficiency feature
@@ -82,6 +94,12 @@ export namespace SelectionManager {
}
export function SelectedDocuments(): Array<DocumentView> {
- return Array.from(manager.SelectedDocuments.keys());
+ return Array.from(manager.SelectedDocuments.keys()).filter(dv => dv.props.Document._viewType !== CollectionViewType.Docking);
+ }
+ export function SelectedSchemaDoc(): Doc | undefined {
+ return manager.SelectedSchemaDocument;
+ }
+ export function SelectedSchemaCollection(): CollectionSchemaView | undefined {
+ return manager.SelectedSchemaCollection;
}
} \ No newline at end of file
diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss
index ec513e5d5..68e0b91b0 100644
--- a/src/client/util/SettingsManager.scss
+++ b/src/client/util/SettingsManager.scss
@@ -4,7 +4,6 @@
//background-color: whitesmoke !important;
color: grey;
width: 450px;
- height: 300px;
button {
background: #315a96;
@@ -142,7 +141,7 @@
.colorFlyout {
margin-top: 2px;
- margin-right: 25px;
+ margin-right: 18px;
&:hover {
cursor: pointer;
@@ -163,6 +162,7 @@
.preferences-color {
display: flex;
+ margin-top: 2px;
.preferences-color-text {
color: black;
@@ -174,6 +174,8 @@
.preferences-font {
display: flex;
+ height: 23px;
+ margin-top: 2px;
.preferences-font-text {
color: black;
@@ -194,6 +196,16 @@
}
}
+ .preferences-check {
+ color: black;
+ font-size: 9;
+ /* margin-top: 4; */
+ margin-right: 4;
+ margin-bottom: -3;
+ margin-left: 5;
+ margin-top: -1px;
+ }
+
.size-select {
width: 60px;
color: black;
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index b4778d3eb..0a1890ca4 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -1,27 +1,25 @@
-import { observable, runInAction, action, computed } from "mobx";
-import * as React from "react";
-import MainViewModal from "../views/MainViewModal";
-import { observer } from "mobx-react";
-import * as fa from '@fortawesome/free-solid-svg-icons';
-import { SelectionManager } from "./SelectionManager";
-import "./SettingsManager.scss";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Networking } from "../Network";
-import { CurrentUserUtils } from "./CurrentUserUtils";
-import { Utils, addStyleSheet, addStyleSheetRule, removeStyleSheetRule } from "../../Utils";
+import { action, computed, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import * as React from "react";
+import { ColorState, SketchPicker } from "react-color";
import { Doc } from "../../fields/Doc";
-import GroupManager from "./GroupManager";
-import GoogleAuthenticationManager from "../apis/GoogleAuthenticationManager";
+import { BoolCast, StrCast } from "../../fields/Types";
+import { addStyleSheet, addStyleSheetRule, Utils } from "../../Utils";
+import { GoogleAuthenticationManager } from "../apis/GoogleAuthenticationManager";
import { DocServer } from "../DocServer";
-import { BoolCast, StrCast, NumCast } from "../../fields/Types";
+import { Networking } from "../Network";
+import { MainViewModal } from "../views/MainViewModal";
+import { CurrentUserUtils } from "./CurrentUserUtils";
+import { GroupManager } from "./GroupManager";
+import "./SettingsManager.scss";
import { undoBatch } from "./UndoManager";
-import { ColorState, SketchPicker } from "react-color";
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@observer
-export default class SettingsManager extends React.Component<{}> {
+export class SettingsManager extends React.Component<{}> {
public static Instance: SettingsManager;
static _settingsStyle = addStyleSheet();
@observable private isOpen = false;
@@ -32,7 +30,7 @@ export default class SettingsManager extends React.Component<{}> {
@observable private new_password = "";
@observable private new_confirm = "";
- @computed get backgroundColor() { return Doc.UserDoc().defaultColor; }
+ @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; }
constructor(props: {}) {
super(props);
@@ -54,6 +52,7 @@ export default class SettingsManager extends React.Component<{}> {
}
@undoBatch selectUserMode = action((e: React.ChangeEvent) => Doc.UserDoc().noviceMode = (e.currentTarget as any)?.value === "Novice");
+ @undoBatch changeShowTitle = action((e: React.ChangeEvent) => Doc.UserDoc().showTitle = (e.currentTarget as any).value);
@undoBatch changeFontFamily = action((e: React.ChangeEvent) => Doc.UserDoc().fontFamily = (e.currentTarget as any).value);
@undoBatch changeFontSize = action((e: React.ChangeEvent) => Doc.UserDoc().fontSize = (e.currentTarget as any).value);
@undoBatch switchColor = action((color: ColorState) => Doc.UserDoc().activeCollectionBackground = String(color.hex));
@@ -86,17 +85,31 @@ export default class SettingsManager extends React.Component<{}> {
return <div className="preferences-content">
<div className="preferences-color">
- <div className="preferences-color-text">Background Color</div>
+ <div className="preferences-color-text">Back. Color</div>
{colorFlyout}
</div>
<div className="preferences-font">
- <div className="preferences-font-text">Default Font</div>
+ <div className="preferences-font-text">Font</div>
<select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, "Times New Roman")} >
{fontFamilies.map(font => <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}> {font} </option>)}
</select>
- <select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7pt")}>
+ <select className="size-select" style={{ marginRight: "10px" }} onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7pt")}>
{fontSizes.map(size => <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}> {size} </option>)}
</select>
+ <div>
+ <div className="preferences-check">Show title</div>
+ <input type="checkbox" onChange={e => Doc.UserDoc().showTitle = !Doc.UserDoc().showTitle} checked={BoolCast(Doc.UserDoc().showTitle)} />
+ </div>
+ <div>
+ <div className="preferences-check">Alt Buttons</div>
+ <input type="checkbox" onChange={e => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"]}
+ checked={BoolCast(Doc.UserDoc()["documentLinksButton-hideEnd"])} />
+ </div>
+ <div>
+ Autoscroll
+ <input type="checkbox" onChange={e => Doc.UserDoc()._noAutoscroll = !Doc.UserDoc()._noAutoscroll}
+ checked={!BoolCast(Doc.UserDoc()._noAutoscroll)} />
+ </div>
</div>
</div>;
}
@@ -158,10 +171,10 @@ export default class SettingsManager extends React.Component<{}> {
<div className="settings-title">Settings</div>
<div className="settings-username">{Doc.CurrentUserEmail}</div>
<button className="logout-button" onClick={() => window.location.assign(Utils.prepend("/logout"))} >
- {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}
+ {CurrentUserUtils.GuestDashboard ? "Exit" : "Log Out"}
</button>
<div className="close-button" onClick={this.close}>
- <FontAwesomeIcon icon={fa.faTimes} color="black" size={"lg"} />
+ <FontAwesomeIcon icon={"times"} color="black" size={"lg"} />
</div>
</div>
<div className="settings-content">
@@ -180,6 +193,6 @@ export default class SettingsManager extends React.Component<{}> {
isDisplayed={this.isOpen}
interactive={true}
closeOnExternalClick={this.close}
- dialogueBoxStyle={{ width: "600px", height: "340px" }} />;
+ dialogueBoxStyle={{ width: "600px" }} />;
}
} \ No newline at end of file
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 7912db74d..b756716bf 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -62,6 +62,7 @@
input {
height: 10px;
+ cursor: pointer;
}
label {
@@ -69,11 +70,29 @@
font-style: italic;
}
}
+
+ .layoutDoc-acls {
+ display: flex;
+ flex-direction: column;
+ float: right;
+ margin-right: 12;
+ margin-top: -15;
+ align-items: center;
+
+ label {
+ font-weight: normal;
+ font-style: italic;
+ }
+
+ input {
+ cursor: pointer;
+ }
+ }
}
.main-container {
display: flex;
- margin-top: -10px;
+ margin-top: -25px;
.individual-container,
.group-container {
@@ -102,7 +121,7 @@
overflow-y: scroll;
overflow-x: hidden;
text-align: left;
- display: flex;
+ display: block;
align-content: center;
align-items: center;
text-align: center;
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 48a3c023f..6fdbac41b 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,30 +1,26 @@
-import { observable, runInAction, action } from "mobx";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
import * as React from "react";
-import MainViewModal from "../views/MainViewModal";
-import { Doc, Opt, AclAdmin, AclPrivate, DocListCast } from "../../fields/Doc";
-import { DocServer } from "../DocServer";
-import { Cast, StrCast } from "../../fields/Types";
+import Select from "react-select";
import * as RequestPromise from "request-promise";
+import { AclAdmin, AclPrivate, DataSym, Doc, DocListCast, Opt, AclSym } from "../../fields/Doc";
+import { List } from "../../fields/List";
+import { Cast, StrCast } from "../../fields/Types";
+import { distributeAcls, GetEffectiveAcl, SharingPermissions } from "../../fields/util";
import { Utils } from "../../Utils";
-import "./SharingManager.scss";
-import { observer } from "mobx-react";
-import * as fa from '@fortawesome/free-solid-svg-icons';
-import { DocumentView } from "../views/nodes/DocumentView";
-import { SelectionManager } from "./SelectionManager";
-import { DocumentManager } from "./DocumentManager";
+import { DocServer } from "../DocServer";
import { CollectionView } from "../views/collections/CollectionView";
import { DictationOverlay } from "../views/DictationOverlay";
-import GroupManager, { UserOptions } from "./GroupManager";
-import GroupMemberView from "./GroupMemberView";
-import Select from "react-select";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { List } from "../../fields/List";
-import { distributeAcls, SharingPermissions, GetEffectiveAcl } from "../../fields/util";
+import { MainViewModal } from "../views/MainViewModal";
+import { DocumentView } from "../views/nodes/DocumentView";
import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
-import { library } from "@fortawesome/fontawesome-svg-core";
-
-library.add(fa.faInfoCircle, fa.faCaretUp, fa.faCaretRight, fa.faCaretDown);
-
+import { DocumentManager } from "./DocumentManager";
+import { GroupManager, UserOptions } from "./GroupManager";
+import { GroupMemberView } from "./GroupMemberView";
+import "./SharingManager.scss";
+import { SelectionManager } from "./SelectionManager";
+import { intersection } from "lodash";
export interface User {
email: string;
@@ -47,6 +43,8 @@ interface GroupedOptions {
const indType = "!indType/";
const groupType = "!groupType/";
+const storage = "data";
+
/**
* A user who also has a notificationDoc.
*/
@@ -55,10 +53,9 @@ interface ValidatedUser {
notificationDoc: Doc;
}
-const storage = "data";
@observer
-export default class SharingManager extends React.Component<{}> {
+export class SharingManager extends React.Component<{}> {
public static Instance: SharingManager;
@observable private isOpen = false; // whether the SharingManager modal is open or not
@observable private users: ValidatedUser[] = []; // the list of users with notificationDocs
@@ -75,29 +72,28 @@ export default class SharingManager extends React.Component<{}> {
// if both showUserOptions and showGroupOptions are false then both are displayed
@observable private showUserOptions: boolean = false; // whether to show individuals as options when sharing (in the react-select component)
@observable private showGroupOptions: boolean = false; // // whether to show groups as options when sharing (in the react-select component)
- private populating: boolean = false;
+ private populating: boolean = false; // whether the list of users is populating or not
+ @observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
// private get linkVisible() {
// return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false;
// }
- public open = (target: DocumentView) => {
+ public open = (target?: DocumentView, target_doc?: Doc) => {
runInAction(() => this.users = []);
- // SelectionManager.DeselectAll();
this.populateUsers();
runInAction(() => {
this.targetDocView = target;
- this.targetDoc = target.props.Document;
+ this.targetDoc = target_doc || target?.props.Document;
DictationOverlay.Instance.hasActiveModal = true;
- this.isOpen = true;
+ this.isOpen = this.targetDoc !== undefined;
this.permissions = SharingPermissions.Edit;
});
- this.targetDoc!.author === Doc.CurrentUserEmail && !this.targetDoc![`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, this.targetDoc!);
}
public close = action(() => {
this.isOpen = false;
- this.selectedUsers = null; // resets the list of users and seleected users (in the react-select component)
+ this.selectedUsers = null; // resets the list of users and selected users (in the react-select component)
TaskCompletionBox.taskCompleted = false;
setTimeout(action(() => {
// this.copied = false;
@@ -119,7 +115,7 @@ export default class SharingManager extends React.Component<{}> {
}
/**
- * Populates the list of validated users (this.users) by adding registered users which have a sidebar-sharing.
+ * Populates the list of validated users (this.users) by adding registered users which have a mySharedDocs.
*/
populateUsers = async () => {
if (!this.populating) {
@@ -132,7 +128,7 @@ export default class SharingManager extends React.Component<{}> {
if (isCandidate) {
const userDocument = await DocServer.GetRefField(user.userDocumentId);
if (userDocument instanceof Doc) {
- const notificationDoc = await Cast(userDocument["sidebar-sharing"], Doc);
+ const notificationDoc = await Cast(userDocument.mySharedDocs, Doc);
runInAction(() => {
if (notificationDoc instanceof Doc) {
this.users.push({ user, notificationDoc });
@@ -151,20 +147,29 @@ export default class SharingManager extends React.Component<{}> {
* @param permission
*/
setInternalGroupSharing = (group: Doc, permission: string, targetDoc?: Doc) => {
- const members: string[] = JSON.parse(StrCast(group.members));
- const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
const target = targetDoc || this.targetDoc!;
- const ACL = `ACL-${StrCast(group.groupName)}`;
+ const key = StrCast(group.groupName).replace(".", "_");
+ const ACL = `ACL-${key}`;
+
+ const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document);
+
+ docs.forEach(doc => {
+ doc.author === Doc.CurrentUserEmail && !doc[`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, doc);
+ GetEffectiveAcl(doc) === AclAdmin && distributeAcls(ACL, permission as SharingPermissions, doc);
- target.author === Doc.CurrentUserEmail && distributeAcls(ACL, permission as SharingPermissions, target);
+ if (key !== "Public") {
+ const members: string[] = JSON.parse(StrCast(group.members));
+ const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
- // if documents have been shared, add the target to that list if it doesn't already exist, otherwise create a new list with the target
- group.docsShared ? Doc.IndexOf(target, DocListCast(group.docsShared)) === -1 && (group.docsShared as List<Doc>).push(target) : group.docsShared = new List<Doc>([target]);
+ // if documents have been shared, add the doc to that list if it doesn't already exist, otherwise create a new list with the doc
+ group.docsShared ? Doc.IndexOf(doc, DocListCast(group.docsShared)) === -1 && (group.docsShared as List<Doc>).push(doc) : group.docsShared = new List<Doc>([doc]);
- users.forEach(({ notificationDoc }) => {
- if (permission !== SharingPermissions.None) Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target); // add the target to the notificationDoc if it hasn't already been added
- else Doc.IndexOf(target, DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); // remove the target from the list if it already exists
+ users.forEach(({ user, notificationDoc }) => {
+ if (permission !== SharingPermissions.None) Doc.IndexOf(doc, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, doc); // add the doc to the notificationDoc if it hasn't already been added
+ else GetEffectiveAcl(doc, undefined, user.email) === AclPrivate && Doc.IndexOf((doc.aliasOf as Doc || doc), DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, (doc.aliasOf as Doc || doc)); // remove the doc from the list if it already exists
+ });
+ }
});
}
@@ -175,17 +180,25 @@ export default class SharingManager extends React.Component<{}> {
*/
shareWithAddedMember = (group: Doc, emailId: string) => {
const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!;
-
if (group.docsShared) DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc));
}
/**
* Called from the properties sidebar to change permissions of a user.
*/
- shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, target: Doc) => {
- const user = this.users.find(({ user: { email } }) => email === (shareWith === "Me" ? Doc.CurrentUserEmail : shareWith));
- if (user) this.setInternalSharing(user, permission, target);
- else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, target);
+ shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, docs: Doc[]) => {
+ if (shareWith !== "Public") {
+ const user = this.users.find(({ user: { email } }) => email === (shareWith === "Me" ? Doc.CurrentUserEmail : shareWith));
+ docs.forEach(doc => {
+ if (user) this.setInternalSharing(user, permission, doc);
+ else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc);
+ });
+ }
+ else {
+ docs.forEach(doc => {
+ if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls("ACL-Public", permission, doc);
+ });
+ }
}
/**
@@ -231,14 +244,16 @@ export default class SharingManager extends React.Component<{}> {
const key = user.email.replace('.', '_');
const ACL = `ACL-${key}`;
- target.author === Doc.CurrentUserEmail && distributeAcls(ACL, permission as SharingPermissions, target);
- if (permission !== SharingPermissions.None) {
- Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target);
- }
- else {
- Doc.IndexOf(target, DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target);
- }
+ const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document);
+
+ docs.forEach(doc => {
+ doc.author === Doc.CurrentUserEmail && !doc[`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, doc);
+ GetEffectiveAcl(doc) === AclAdmin && distributeAcls(ACL, permission as SharingPermissions, doc);
+
+ if (permission !== SharingPermissions.None) Doc.IndexOf(doc, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, doc);
+ else GetEffectiveAcl(doc, undefined, user.email) === AclPrivate && Doc.IndexOf((doc.aliasOf as Doc || doc), DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, (doc.aliasOf as Doc || doc));
+ });
}
@@ -268,8 +283,10 @@ export default class SharingManager extends React.Component<{}> {
/**
* Returns the SharingPermissions (Admin, Can Edit etc) access that's used to share
*/
- private get sharingOptions() {
- return Object.values(SharingPermissions).map(permission =>
+ private sharingOptions(uniform: boolean) {
+ const dropdownValues: string[] = Object.values(SharingPermissions);
+ if (!uniform) dropdownValues.unshift("-multiple-");
+ return dropdownValues.map(permission =>
(
<option key={permission} value={permission}>
{permission}
@@ -280,27 +297,30 @@ export default class SharingManager extends React.Component<{}> {
private focusOn = (contents: string) => {
const title = this.targetDoc ? StrCast(this.targetDoc.title) : "";
+ const docs = SelectionManager.SelectedDocuments().length > 1 ? SelectionManager.SelectedDocuments().map(docView => docView.props.Document) : [this.targetDoc];
return (
<span
className={"focus-span"}
title={title}
onClick={() => {
let context: Opt<CollectionView>;
- if (this.targetDoc && this.targetDocView && (context = this.targetDocView.props.ContainingCollectionView)) {
+ if (this.targetDoc && this.targetDocView && docs.length === 1 && (context = this.targetDocView.props.ContainingCollectionView)) {
DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, context.props.Document);
}
}}
onPointerEnter={action(() => {
- if (this.targetDoc) {
- Doc.BrushDoc(this.targetDoc);
+ if (docs.length) {
+ docs.forEach(doc => doc && Doc.BrushDoc(doc));
this.dialogueBoxOpacity = 0.1;
this.overlayOpacity = 0.1;
}
})}
onPointerLeave={action(() => {
- this.targetDoc && Doc.UnBrushDoc(this.targetDoc);
- this.dialogueBoxOpacity = 1;
- this.overlayOpacity = 0.4;
+ if (docs.length) {
+ docs.forEach(doc => doc && Doc.UnBrushDoc(doc));
+ this.dialogueBoxOpacity = 1;
+ this.overlayOpacity = 0.4;
+ }
})}
>
{contents}
@@ -372,8 +392,8 @@ export default class SharingManager extends React.Component<{}> {
* @returns the main interface of the SharingManager.
*/
private get sharingInterface() {
- const groupList = GroupManager.Instance?.getAllGroups() || [];
+ const groupList = GroupManager.Instance?.getAllGroups() || [];
const sortedUsers = this.users.slice().sort(this.sortUsers)
.map(({ user: { email } }) => ({ label: email, value: indType + email }));
const sortedGroups = groupList.slice().sort(this.sortGroups)
@@ -406,30 +426,42 @@ export default class SharingManager extends React.Component<{}> {
}
}
- const users = this.individualSort === "ascending" ? this.users.sort(this.sortUsers) : this.individualSort === "descending" ? this.users.sort(this.sortUsers).reverse() : this.users;
- const groups = this.groupSort === "ascending" ? groupList.sort(this.sortGroups) : this.groupSort === "descending" ? groupList.sort(this.sortGroups).reverse() : groupList;
+ const users = this.individualSort === "ascending" ? this.users.slice().sort(this.sortUsers) : this.individualSort === "descending" ? this.users.slice().sort(this.sortUsers).reverse() : this.users;
+ const groups = this.groupSort === "ascending" ? groupList.slice().sort(this.sortGroups) : this.groupSort === "descending" ? groupList.slice().sort(this.sortGroups).reverse() : groupList;
+
+ // handles the case where multiple documents are selected
+ const docs = SelectionManager.SelectedDocuments().length < 2 ?
+ [this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DataSym]]
+ : SelectionManager.SelectedDocuments().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DataSym]);
+
+ const targetDoc = docs[0];
+
+ // tslint:disable-next-line: no-unnecessary-callback-wrapper
+ const admin = docs.map(doc => GetEffectiveAcl(doc)).every(acl => acl === AclAdmin); // if the user has admin access to all selected docs
- const effectiveAcl = this.targetDoc ? GetEffectiveAcl(this.targetDoc) : AclPrivate;
+ // users in common between all docs
+ const commonKeys = intersection(...docs.map(doc => this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym]?.[AclSym] && Object.keys(doc[DataSym][AclSym])));
// the list of users shared with
- const userListContents: (JSX.Element | null)[] = users.map(({ user, notificationDoc }) => {
- const userKey = user.email.replace('.', '_');
- const permissions = StrCast(this.targetDoc?.[`ACL-${userKey}`]);
+ const userListContents: (JSX.Element | null)[] = users.filter(({ user }) => docs.length > 1 ? commonKeys.includes(`ACL-${user.email.replace('.', '_')}`) : true).map(({ user, notificationDoc }) => {
+ const userKey = `ACL-${user.email.replace('.', '_')}`;
+ const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey] : doc?.[DataSym]?.[AclSym]?.[userKey] === docs[0]?.[DataSym]?.[AclSym]?.[userKey]);
+ const permissions = uniform ? StrCast(targetDoc?.[userKey]) : "-multiple-";
- return !permissions || user.email === this.targetDoc?.author ? null : (
+ return !permissions ? (null) : (
<div
key={userKey}
className={"container"}
>
<span className={"padding"}>{user.email}</span>
<div className="edit-actions">
- {effectiveAcl === AclAdmin ? (
+ {admin ? (
<select
className={"permissions-dropdown"}
value={permissions}
onChange={e => this.setInternalSharing({ user, notificationDoc }, e.currentTarget.value)}
>
- {this.sharingOptions}
+ {this.sharingOptions(uniform)}
</select>
) : (
<div className={"permissions-dropdown"}>
@@ -441,22 +473,26 @@ export default class SharingManager extends React.Component<{}> {
);
});
+ // checks if every doc has the same author
+ const sameAuthor = docs.every(doc => doc?.author === docs[0]?.author);
+
// the owner of the doc and the current user are placed at the top of the user list.
userListContents.unshift(
- (
- <div
- key={"owner"}
- className={"container"}
- >
- <span className={"padding"}>{this.targetDoc?.author === Doc.CurrentUserEmail ? "Me" : this.targetDoc?.author}</span>
- <div className="edit-actions">
- <div className={"permissions-dropdown"}>
- Owner
+ sameAuthor ?
+ (
+ <div
+ key={"owner"}
+ className={"container"}
+ >
+ <span className={"padding"}>{targetDoc?.author === Doc.CurrentUserEmail ? "Me" : targetDoc?.author}</span>
+ <div className="edit-actions">
+ <div className={"permissions-dropdown"}>
+ Owner
+ </div>
</div>
</div>
- </div>
- ),
- this.targetDoc?.author !== Doc.CurrentUserEmail ?
+ ) : null,
+ sameAuthor && targetDoc?.author !== Doc.CurrentUserEmail ?
(
<div
key={"me"}
@@ -465,42 +501,54 @@ export default class SharingManager extends React.Component<{}> {
<span className={"padding"}>Me</span>
<div className="edit-actions">
<div className={"permissions-dropdown"}>
- {this.targetDoc?.[`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`]}
+ {targetDoc?.[`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`]}
</div>
</div>
</div>
) : null
);
+ // dummy doc for public acl
+ const publicDoc = new Doc;
+ publicDoc.groupName = "Public";
// the list of groups shared with
- const groupListContents = groups.map(group => {
- const permissions = StrCast(this.targetDoc?.[`ACL-${StrCast(group.groupName)}`]);
-
- return !permissions ? null : (
+ const groupListMap = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`ACL-${StrCast(groupName).replace('.', '_')}`) : true);
+ groupListMap.unshift(publicDoc);
+ const groupListContents = groupListMap.map(group => {
+ const groupKey = `ACL-${StrCast(group.groupName)}`;
+ const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]);
+ const permissions = uniform ? StrCast(targetDoc?.[`ACL-${StrCast(group.groupName)}`]) : "-multiple-";
+
+ return !permissions ? (null) : (
<div
- key={StrCast(group.groupName)}
+ key={groupKey}
className={"container"}
>
<div className={"padding"}>{group.groupName}</div>
- <div className="group-info" onClick={action(() => GroupManager.Instance.currentGroup = group)}>
- <FontAwesomeIcon icon={fa.faInfoCircle} color={"#e8e8e8"} size={"sm"} style={{ backgroundColor: "#1e89d7", borderRadius: "100%", border: "1px solid #1e89d7" }} />
- </div>
+ {group.groupName !== "Public" ?
+ (<div className="group-info" onClick={action(() => GroupManager.Instance.currentGroup = group)}>
+ <FontAwesomeIcon icon={"info-circle"} color={"#e8e8e8"} size={"sm"} style={{ backgroundColor: "#1e89d7", borderRadius: "100%", border: "1px solid #1e89d7" }} />
+ </div>)
+ : (null)}
<div className="edit-actions">
- <select
- className={"permissions-dropdown"}
- value={permissions}
- onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}
- >
- {this.sharingOptions}
- </select>
+ {admin ? (
+ <select
+ className={"permissions-dropdown"}
+ value={permissions}
+ onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}
+ >
+ {this.sharingOptions(uniform)}
+ </select>
+ ) : (
+ <div className={"permissions-dropdown"}>
+ {permissions}
+ </div>
+ )}
</div>
</div>
);
});
- // don't display the group list if all groups are null
- const displayGroupList = !groupListContents?.every(group => group === null);
-
return (
<div className={"sharing-interface"}>
{GroupManager.Instance?.currentGroup ?
@@ -519,7 +567,7 @@ export default class SharingManager extends React.Component<{}> {
style={{ backgroundColor: this.copied ? "lawngreen" : "gainsboro" }}
onClick={this.copy}
>
- <FontAwesomeIcon icon={fa.faCopy} />
+ <FontAwesomeIcon icon={"copy"} />
</div>
</div>
}
@@ -540,7 +588,7 @@ export default class SharingManager extends React.Component<{}> {
</div>
<div className={"hr-substitute"} /> */}
<div className="sharing-contents">
- <p className={"share-title"}><b>Share </b>{this.focusOn(StrCast(this.targetDoc?.title, "this document"))}</p>
+ <p className={"share-title"}><b>Share </b>{this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, "this document") : "-multiple-")}</p>
<div className={"close-button"} onClick={this.close}>
<FontAwesomeIcon icon={"times"} color={"black"} size={"lg"} />
</div>
@@ -561,7 +609,7 @@ export default class SharingManager extends React.Component<{}> {
}}
/>
<select className="permissions-select" onChange={this.handlePermissionsChange} value={this.permissions}>
- {this.sharingOptions}
+ {this.sharingOptions(true)}
</select>
<button ref={this.shareDocumentButtonRef} className="share-button" onClick={this.share}>
Share
@@ -571,6 +619,9 @@ export default class SharingManager extends React.Component<{}> {
<input type="checkbox" onChange={action(() => this.showUserOptions = !this.showUserOptions)} /> <label style={{ marginRight: 10 }}>Individuals</label>
<input type="checkbox" onChange={action(() => this.showGroupOptions = !this.showGroupOptions)} /> <label>Groups</label>
</div>
+ <div className="layoutDoc-acls">
+ <input type="checkbox" onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)} checked={this.layoutDocAcls} /> <label>Layout</label>
+ </div>
</div>
}
<div className="main-container">
@@ -578,11 +629,11 @@ export default class SharingManager extends React.Component<{}> {
<div
className="user-sort"
onClick={action(() => this.individualSort = this.individualSort === "ascending" ? "descending" : this.individualSort === "descending" ? "none" : "ascending")}>
- Individuals {this.individualSort === "ascending" ? <FontAwesomeIcon icon={fa.faCaretUp} size={"xs"} />
- : this.individualSort === "descending" ? <FontAwesomeIcon icon={fa.faCaretDown} size={"xs"} />
- : <FontAwesomeIcon icon={fa.faCaretRight} size={"xs"} />}
+ Individuals {this.individualSort === "ascending" ? <FontAwesomeIcon icon={"caret-up"} size={"xs"} />
+ : this.individualSort === "descending" ? <FontAwesomeIcon icon={"caret-down"} size={"xs"} />
+ : <FontAwesomeIcon icon={"caret-right"} size={"xs"} />}
</div>
- <div className={"users-list"} style={{ display: "block" }}>{/*200*/}
+ <div className={"users-list"}>
{userListContents}
</div>
</div>
@@ -590,23 +641,13 @@ export default class SharingManager extends React.Component<{}> {
<div
className="user-sort"
onClick={action(() => this.groupSort = this.groupSort === "ascending" ? "descending" : this.groupSort === "descending" ? "none" : "ascending")}>
- Groups {this.groupSort === "ascending" ? <FontAwesomeIcon icon={fa.faCaretUp} size={"xs"} />
- : this.groupSort === "descending" ? <FontAwesomeIcon icon={fa.faCaretDown} size={"xs"} />
- : <FontAwesomeIcon icon={fa.faCaretRight} size={"xs"} />}
+ Groups {this.groupSort === "ascending" ? <FontAwesomeIcon icon={"caret-up"} size={"xs"} />
+ : this.groupSort === "descending" ? <FontAwesomeIcon icon={"caret-down"} size={"xs"} />
+ : <FontAwesomeIcon icon={"caret-right"} size={"xs"} />}
</div>
- <div className={"groups-list"} style={{ display: !displayGroupList ? "flex" : "block" }}>{/*200*/}
- {
- !displayGroupList ?
- <div
- className={"none"}
- >
- There are no groups this document has been shared with.
- </div>
- :
- groupListContents
- }
-
+ <div className={"groups-list"}>
+ {groupListContents}
</div>
</div>
</div>