aboutsummaryrefslogtreecommitdiff
path: root/src/client/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util')
-rw-r--r--src/client/util/CurrentUserUtils.ts29
-rw-r--r--src/client/util/DictationManager.ts16
-rw-r--r--src/client/util/DocumentManager.ts41
-rw-r--r--src/client/util/DragManager.ts45
-rw-r--r--src/client/util/HypothesisUtils.ts2
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx1
-rw-r--r--src/client/util/InteractionUtils.tsx9
-rw-r--r--src/client/util/LinkManager.ts76
-rw-r--r--src/client/util/SelectionManager.ts58
-rw-r--r--src/client/util/SettingsManager.scss194
-rw-r--r--src/client/util/SettingsManager.tsx172
-rw-r--r--src/client/util/SharingManager.tsx28
12 files changed, 441 insertions, 230 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 4f054269f..382b225a4 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -63,7 +63,7 @@ export class CurrentUserUtils {
[this.ficon({
ignoreClick: true,
icon: "mobile",
- backgroundColor: "rgba(0,0,0,0)"
+ backgroundColor: "transparent"
}),
this.mobileTextContainer({},
[this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])]);
@@ -252,7 +252,7 @@ export class CurrentUserUtils {
doc["template-note-Idea"] = new PrefetchProxy(noteView);
}
if (doc["template-note-Topic"] === undefined) {
- const noteView = Docs.Create.TextDocument("", { title: "text", style: "Topic", backgroundColor: "lightBlue", system: true });
+ const noteView = Docs.Create.TextDocument("", { title: "text", style: "Topic", backgroundColor: "lightblue", system: true });
noteView.isTemplateDoc = makeTemplate(noteView, true, "Topic");
doc["template-note-Topic"] = new PrefetchProxy(noteView);
}
@@ -378,10 +378,8 @@ export class CurrentUserUtils {
((doc.emptyPane as Doc).proto as Doc)["dragFactory-count"] = 0;
}
if (doc.emptySlide === undefined) {
- const textDoc = Docs.Create.TextDocument("Slide", { title: "Slide", _viewType: CollectionViewType.Tree, _fontSize: "20px", treeViewOutlineMode: true, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, _backgroundColor: "transparent", system: true, cloneFieldFilter: new List<string>(["system"]) });
- Doc.GetProto(textDoc).layout = CollectionView.LayoutString("data");
+ const textDoc = Docs.Create.TreeDocument([], { title: "Slide", _viewType: CollectionViewType.Tree, _fontSize: "20px", treeViewOutlineMode: true, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, _backgroundColor: "transparent", system: true, cloneFieldFilter: new List<string>(["system"]) });
Doc.GetProto(textDoc).title = ComputedField.MakeFunction('self.text?.Text');
- Doc.GetProto(textDoc).data = new List<Doc>([]);
FormattedTextBox.SelectOnLoad = textDoc[Id];
doc.emptySlide = textDoc;
}
@@ -496,7 +494,7 @@ export class CurrentUserUtils {
activeInkPen,
backgroundColor,
_hideContextMenu: true,
- removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
+ removeDropProperties: new List<string>(["_stayInCollection"]),
_stayInCollection: true,
dragFactory,
clickFactory,
@@ -797,12 +795,12 @@ export class CurrentUserUtils {
treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, ignoreClick: true,
lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true
}));
- const clearAll = ScriptField.MakeScript(`getProto(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"]);
}
- }
+ const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([]); scriptContext._docFilters = scriptContext._docRangeFilters = undefined;`, { scriptContext: Doc.name });
+ (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.myUserDoc === undefined) {
@@ -913,7 +911,7 @@ export class CurrentUserUtils {
}
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", _stayInCollection: true, _hideContextMenu: true, title: "Import", icon: "upload", system: true });
+ const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", icon: "upload", system: true });
doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, ignoreClick: true, _stayInCollection: true, _hideContextMenu: true, lockedPosition: true, system: true }));
}
}
@@ -1052,7 +1050,12 @@ export class CurrentUserUtils {
Docs.newAccount = !(field instanceof Doc);
await Docs.Prototypes.initialize();
const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc;
- return this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
+ const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
+ (await DocListCastAsync(Cast(Doc.UserDoc().myLinkDatabase, Doc, null)?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
+ const a1 = await Cast(link?.anchor1, Doc, null);
+ const a2 = await Cast(link?.anchor2, Doc, null);
+ });
+ return updated;
});
} else {
throw new Error("There should be a user id! Why does Dash think there isn't one?");
@@ -1201,7 +1204,7 @@ Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
if (copy) {
CollectionDockingView.AddSplit(copy, "right");
const view = DocumentManager.Instance.getFirstDocumentView(copy);
- view && SelectionManager.SelectDoc(view, false);
+ view && SelectionManager.SelectView(view, false);
}
});
Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); },
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index 231e1fa8d..c6b654dda 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -1,17 +1,17 @@
-import { SelectionManager } from "./SelectionManager";
-import { DocumentView } from "../views/nodes/DocumentView";
-import { UndoManager } from "./UndoManager";
import * as interpreter from "words-to-numbers";
-import { DocumentType } from "../documents/DocumentTypes";
import { Doc, Opt } from "../../fields/Doc";
import { List } from "../../fields/List";
-import { Docs } from "../documents/Documents";
-import { Cast, CastCtor } from "../../fields/Types";
+import { RichTextField } from "../../fields/RichTextField";
import { listSpec } from "../../fields/Schema";
+import { Cast, CastCtor } from "../../fields/Types";
import { AudioField, ImageField } from "../../fields/URLField";
import { Utils } from "../../Utils";
-import { RichTextField } from "../../fields/RichTextField";
+import { Docs } from "../documents/Documents";
+import { DocumentType } from "../documents/DocumentTypes";
import { DictationOverlay } from "../views/DictationOverlay";
+import { DocumentView } from "../views/nodes/DocumentView";
+import { SelectionManager } from "./SelectionManager";
+import { UndoManager } from "./UndoManager";
/**
* This namespace provides a singleton instance of a manager that
@@ -235,7 +235,7 @@ export namespace DictationManager {
export const execute = async (phrase: string) => {
return UndoManager.RunInBatch(async () => {
- const targets = SelectionManager.SelectedDocuments();
+ const targets = SelectionManager.Views();
if (!targets || !targets.length) {
return;
}
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index a6816c7f9..1f2dd350b 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,7 +1,7 @@
import { action, observable } from 'mobx';
import { Doc, DocListCast, DocListCastAsync, Opt } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
-import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { Cast, NumCast } from '../../fields/Types';
import { returnFalse } from '../../Utils';
import { DocumentType } from '../documents/DocumentTypes';
import { CollectionDockingView } from '../views/collections/CollectionDockingView';
@@ -9,9 +9,6 @@ import { CollectionView } from '../views/collections/CollectionView';
import { DocumentView } from '../views/nodes/DocumentView';
import { LinkManager } from './LinkManager';
import { Scripting } from './Scripting';
-import { SelectionManager } from './SelectionManager';
-import { LinkDocPreview } from '../views/nodes/LinkDocPreview';
-import { FormattedTextBoxComment } from '../views/nodes/formattedText/FormattedTextBoxComment';
export type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
@@ -47,12 +44,12 @@ export class DocumentManager {
});
this.DocumentViews.push(view);
}
- public RemoveView = (view: DocumentView) => {
+ public RemoveView = action((view: DocumentView) => {
const index = this.DocumentViews.indexOf(view);
index !== -1 && this.DocumentViews.splice(index, 1);
this.LinkedDocumentViews.slice().forEach(action((pair, i) => pair.a === view || pair.b === view ? this.LinkedDocumentViews.splice(i, 1) : null));
- }
+ });
//gets all views
public getDocumentViewsById(id: string) {
@@ -224,37 +221,5 @@ export class DocumentManager {
}
}
- public async FollowLink(link: Opt<Doc>, doc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
- LinkDocPreview.TargetDoc = undefined;
- FormattedTextBoxComment.linkDoc = undefined;
- const linkDocs = link ? [link] : DocListCast(doc.links);
- 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;
- const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : (traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs);
- const followLinks = linkDocList.length ? (doc.isPushpin ? linkDocList : [linkDocList[0]]) : [];
- followLinks.forEach(async linkDoc => {
- if (linkDoc) {
- const target = (doc === linkDoc.anchor1 ? linkDoc.anchor2 : doc === linkDoc.anchor2 ? linkDoc.anchor1 :
- (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || 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) || 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;
- const targetContext = await target?.context as Doc;
- const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined;
- DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "add:right"), finished), targetNavContext, linkDoc, undefined, doc, finished);
- } else {
- finished?.();
- }
- } else {
- finished?.();
- }
- });
- }
}
Scripting.addGlobal(function DocFocus(doc: any) { DocumentManager.Instance.getDocumentViews(Doc.GetProto(doc)).map(view => view.props.focus(doc, true)); }); \ No newline at end of file
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 86e2d339e..d24348746 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -109,6 +109,7 @@ export namespace DragManager {
this.linkDragData = dragData instanceof LinkDragData ? dragData : undefined;
this.columnDragData = dragData instanceof ColumnDragData ? dragData : undefined;
}
+ linkDocument?: Doc;
aborted: boolean;
docDragData?: DocumentDragData;
annoDragData?: PdfAnnoDragData;
@@ -125,9 +126,7 @@ export namespace DragManager {
}
draggedDocuments: Doc[];
droppedDocuments: Doc[];
- dragDivName?: string;
treeViewDoc?: Doc;
- dontHideOnDrop?: boolean;
offset: number[];
canEmbed?: boolean;
userDropAction: dropActionType; // the user requested drop action -- this will be honored as specified by modifier keys
@@ -145,8 +144,7 @@ export namespace DragManager {
droppedDocuments: Doc[] = [];
linkSourceDocument: Doc;
dontClearTextBox?: boolean;
- linkDocument?: Doc;
- linkDropCallback?: (data: { linkDocument?: Doc }) => void;
+ linkDropCallback?: (data: { linkDocument: Doc }) => void;
}
export class ColumnDragData {
constructor(colKey: SchemaHeaderField) {
@@ -163,7 +161,6 @@ export namespace DragManager {
this.annotationDocument = annotationDoc;
this.offset = [0, 0];
}
- linkDocument?: Doc;
targetContext: Doc | undefined;
dragDocument: Doc;
annotationDocument: Doc;
@@ -171,7 +168,7 @@ export namespace DragManager {
offset: number[];
dropAction: dropActionType;
userDropAction: dropActionType;
- linkDropCallback?: (data: { linkDocument?: Doc }) => void;
+ linkDropCallback?: (data: { linkDocument: Doc }) => void;
}
export function MakeDropTarget(
@@ -216,7 +213,7 @@ export namespace DragManager {
};
const finishDrag = (e: DragCompleteEvent) => {
const docDragData = e.docDragData;
- if (dropEvent) dropEvent(); // glr: optional additional function to be called - in this case with presentation trails
+ dropEvent?.(); // glr: optional additional function to be called - in this case with presentation trails
if (docDragData && !docDragData.droppedDocuments.length) {
docDragData.dropAction = dragData.userDropAction || dragData.dropAction;
docDragData.droppedDocuments =
@@ -343,11 +340,14 @@ export namespace DragManager {
dragLabel.style.zIndex = "100001";
dragLabel.style.fontSize = "10px";
dragLabel.style.position = "absolute";
- // dragLabel.innerText = "press 'a' to embed on drop"; // bcz: need to move this to a status bar
+ dragLabel.innerText = "press 'a' to embed on drop"; // bcz: need to move this to a status bar
dragDiv.appendChild(dragLabel);
DragManager.Root().appendChild(dragDiv);
}
- dragLabel.style.display = "";
+ dragDiv.style.width = "";
+ dragDiv.style.height = "";
+ dragDiv.style.overflow = "";
+ dragDiv.hidden = false;
const scaleXs: number[] = [];
const scaleYs: number[] = [];
const xs: number[] = [];
@@ -415,14 +415,12 @@ export namespace DragManager {
return dragElement;
});
- const hideSource = options?.hideSource ? true : false;
- eles.forEach(ele => {
- if (ele.parentElement && ele.parentElement?.className === dragData.dragDivName) {
- ele.parentElement.hidden = hideSource;
- } else {
- ele.hidden = hideSource;
- }
- });
+ const hideDragShowOriginalElements = (hide: boolean) => {
+ dragLabel.style.display = hide ? "" : "none";
+ !hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
+ eles.forEach(ele => ele.hidden = hide);
+ };
+ options?.hideSource && hideDragShowOriginalElements(true);
SnappingManager.SetIsDragging(true);
let lastX = downX;
@@ -517,13 +515,8 @@ export namespace DragManager {
);
};
- const hideDragShowOriginalElements = () => {
- dragLabel.style.display = "none";
- dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
- eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.hidden = ele.parentElement.hidden = false) : (ele.hidden = false));
- };
const endDrag = action(() => {
- hideDragShowOriginalElements();
+ hideDragShowOriginalElements(false);
document.removeEventListener("pointermove", moveHandler, true);
document.removeEventListener("pointerup", upHandler);
SnappingManager.SetIsDragging(false);
@@ -546,13 +539,17 @@ export namespace DragManager {
function dispatchDrag(dragEles: HTMLElement[], e: PointerEvent, dragData: { [index: string]: any },
xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number, options?: DragOptions, finishDrag?: (e: DragCompleteEvent) => void) {
- const removed = dragData.dontHideOnDrop ? [] : dragEles.map(dragEle => {
+ const removed = dragEles.map(dragEle => {
const ret = { ele: dragEle, w: dragEle.style.width, h: dragEle.style.height, o: dragEle.style.overflow };
dragEle.style.width = "0";
dragEle.style.height = "0";
dragEle.style.overflow = "hidden";
return ret;
});
+ dragDiv.hidden = true;
+ dragDiv.style.width = "0";
+ dragDiv.style.height = "0";
+ dragDiv.style.overflow = "hidden";
const target = document.elementFromPoint(e.x, e.y);
removed.map(r => {
r.ele.style.width = r.w;
diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts
index f4cf336e2..7a449b882 100644
--- a/src/client/util/HypothesisUtils.ts
+++ b/src/client/util/HypothesisUtils.ts
@@ -29,7 +29,7 @@ export namespace Hypothesis {
* Search for a WebDocument whose url field matches the given uri, return undefined if not found
*/
export const findWebDoc = async (uri: string) => {
- const currentDoc = SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0].props.Document;
+ const currentDoc = SelectionManager.Views().length && SelectionManager.Views()[0].props.Document;
if (currentDoc && Cast(currentDoc.data, WebField)?.url.href === uri) return currentDoc; // always check first whether the currently selected doc is the annotation's source, only use Search otherwise
const results: Doc[] = [];
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 7f01966b9..d9f010557 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -158,7 +158,6 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
importContainer = Docs.Create.SchemaDocument(headers, docs, options);
}
runInAction(() => this.phase = 'External: uploading files to Google Photos...');
- importContainer._columnsStack = false;
await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer });
Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
!this.persistent && this.props.removeDocument && this.props.removeDocument(doc);
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index f58277717..01d00db30 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -364,12 +364,9 @@ export namespace InteractionUtils {
export function IsType(e: PointerEvent | React.PointerEvent, type: string): boolean {
switch (type) {
// pen and eraser are both pointer type 'pen', but pen is button 0 and eraser is button 5. -syip2
- case PENTYPE:
- return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0);
- case ERASERTYPE:
- return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON);
- default:
- return e.pointerType === type;
+ case PENTYPE: return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0);
+ case ERASERTYPE: return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON);
+ default: return e.pointerType === type;
}
}
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 802b8ae7b..accf53676 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,7 +1,12 @@
+import { computedFn } from "mobx-utils";
import { Doc, DocListCast, Opt } from "../../fields/Doc";
-import { Cast, StrCast } from "../../fields/Types";
+import { BoolCast, Cast, StrCast } from "../../fields/Types";
+import { DocFocusFunc, DocumentViewSharedProps } from "../views/nodes/DocumentView";
+import { FormattedTextBoxComment } from "../views/nodes/formattedText/FormattedTextBoxComment";
+import { LinkDocPreview } from "../views/nodes/LinkDocPreview";
+import { CreateViewFunc, DocumentManager } from "./DocumentManager";
import { SharingManager } from "./SharingManager";
-import { computedFn } from "mobx-utils";
+import { UndoManager } from "./UndoManager";
/*
* link doc:
@@ -34,7 +39,8 @@ export class LinkManager {
public getAllLinks(): Doc[] { return this.allLinks(); }
allLinks = computedFn(function allLinks(this: any): Doc[] {
- const lset = new Set<Doc>(DocListCast(Doc.LinkDBDoc().data));
+ const linkData = Doc.LinkDBDoc().data;
+ const lset = new Set<Doc>(DocListCast(linkData));
SharingManager.Instance.users.forEach(user => DocListCast(user.linkDatabase?.data).forEach(doc => lset.add(doc)));
return Array.from(lset);
}, true);
@@ -88,4 +94,68 @@ export class LinkManager {
if (Doc.AreProtosEqual(anchor, a2.annotationOn as Doc)) return a1;
if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc;
}
+
+
+ // follows a link - if the target is on screen, it highlights/pans to it.
+ // if the target isn't onscreen, then it will open up the target in a tab, on the right, or in place
+ // depending on the followLinkLocation property of the source (or the link itself as a fallback);
+ public static FollowLink = async (linkDoc: Opt<Doc>, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean) => {
+ const batch = UndoManager.StartBatch("follow link click");
+ // open up target if it's not already in view ...
+ const createViewFunc = (doc: Doc, followLoc: string, finished: Opt<() => void>) => {
+ const targetFocusAfterDocFocus = () => {
+ const where = StrCast(sourceDoc.followLinkLocation) || followLoc;
+ const hackToCallFinishAfterFocus = () => {
+ finished && setTimeout(finished, 0); // finished() needs to be called right after hackToCallFinishAfterFocus(), but there's no callback for that so we use the hacky timeout.
+ return false; // we must return false here so that the zoom to the document is not reversed. If it weren't for needing to call finished(), we wouldn't need this function at all since not having it is equivalent to returning false
+ };
+ const addTab = docViewProps.addDocTab(doc, where);
+ addTab && setTimeout(() => {
+ const targDocView = DocumentManager.Instance.getFirstDocumentView(doc);
+ targDocView?.props.focus(doc, BoolCast(sourceDoc.followLinkZoom, false), undefined, hackToCallFinishAfterFocus);
+ }); // add the target and focus on it.
+ return where !== "inPlace" || addTab; // return true to reset the initial focus&zoom (return false for 'inPlace' since resetting the initial focus&zoom will negate the zoom into the target)
+ };
+ if (!sourceDoc.followLinkZoom) {
+ targetFocusAfterDocFocus();
+ } else {
+ // first focus & zoom onto this (the clicked document). Then execute the function to focus on the target
+ docViewProps.focus(sourceDoc, BoolCast(sourceDoc.followLinkZoom, true), 1, targetFocusAfterDocFocus);
+ }
+ };
+ await LinkManager.traverseLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, false), docViewProps.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
+ }
+ public static async traverseLink(link: Opt<Doc>, doc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
+ LinkDocPreview.TargetDoc = undefined;
+ FormattedTextBoxComment.linkDoc = undefined;
+ const linkDocs = link ? [link] : DocListCast(doc.links);
+ 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;
+ const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : (traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs);
+ const followLinks = linkDocList.length ? (doc.isPushpin ? linkDocList : [linkDocList[0]]) : [];
+ followLinks.forEach(async linkDoc => {
+ if (linkDoc) {
+ const target = (doc === linkDoc.anchor1 ? linkDoc.anchor2 : doc === linkDoc.anchor2 ? linkDoc.anchor1 :
+ (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || 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) || 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;
+ const targetContext = await target?.context as Doc;
+ const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined;
+ DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "add:right"), finished), targetNavContext, linkDoc, undefined, doc, finished);
+ } else {
+ finished?.();
+ }
+ } else {
+ finished?.();
+ }
+ });
+ }
+
} \ No newline at end of file
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 34e88c7b0..f657e5b40 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,47 +1,47 @@
-import { observable, action, runInAction, ObservableMap } from "mobx";
-import { Doc, Opt } from "../../fields/Doc";
-import { DocumentView } from "../views/nodes/DocumentView";
+import { action, observable, ObservableMap } from "mobx";
import { computedFn } from "mobx-utils";
+import { Doc, Opt } from "../../fields/Doc";
import { CollectionSchemaView } from "../views/collections/CollectionSchemaView";
import { CollectionViewType } from "../views/collections/CollectionView";
+import { DocumentView } from "../views/nodes/DocumentView";
export namespace SelectionManager {
class Manager {
@observable IsDragging: boolean = false;
- SelectedDocuments: ObservableMap<DocumentView, boolean> = new ObservableMap();
+ SelectedViews: ObservableMap<DocumentView, boolean> = new ObservableMap();
@observable SelectedSchemaDocument: Doc | undefined;
@observable SelectedSchemaCollection: CollectionSchemaView | undefined;
@action
- SelectSchemaDoc(collectionView: Opt<CollectionSchemaView>, doc: Opt<Doc>) {
+ SelectSchemaView(collectionView: Opt<CollectionSchemaView>, doc: Opt<Doc>) {
manager.SelectedSchemaDocument = doc;
manager.SelectedSchemaCollection = collectionView;
}
@action
- SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
+ SelectView(docView: DocumentView, ctrlPressed: boolean): void {
// if doc is not in SelectedDocuments, add it
- if (!manager.SelectedDocuments.get(docView)) {
+ if (!manager.SelectedViews.get(docView)) {
if (!ctrlPressed) {
this.DeselectAll();
}
- manager.SelectedDocuments.set(docView, true);
+ manager.SelectedViews.set(docView, true);
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));
+ } else if (!ctrlPressed && Array.from(manager.SelectedViews.entries()).length > 1) {
+ Array.from(manager.SelectedViews.keys()).map(dv => dv !== docView && dv.props.whenActiveChanged(false));
manager.SelectedSchemaDocument = undefined;
manager.SelectedSchemaCollection = undefined;
- manager.SelectedDocuments.clear();
- manager.SelectedDocuments.set(docView, true);
+ manager.SelectedViews.clear();
+ manager.SelectedViews.set(docView, true);
}
}
@action
- DeselectDoc(docView: DocumentView): void {
+ DeselectView(docView: DocumentView): void {
- if (manager.SelectedDocuments.get(docView)) {
- manager.SelectedDocuments.delete(docView);
+ if (manager.SelectedViews.get(docView)) {
+ manager.SelectedViews.delete(docView);
docView.props.whenActiveChanged(false);
}
}
@@ -49,49 +49,49 @@ export namespace SelectionManager {
DeselectAll(): void {
manager.SelectedSchemaCollection = undefined;
manager.SelectedSchemaDocument = undefined;
- Array.from(manager.SelectedDocuments.keys()).map(dv => dv.props.whenActiveChanged(false));
- manager.SelectedDocuments.clear();
+ Array.from(manager.SelectedViews.keys()).map(dv => dv.props.whenActiveChanged(false));
+ manager.SelectedViews.clear();
}
}
const manager = new Manager();
- export function DeselectDoc(docView: DocumentView): void {
- manager.DeselectDoc(docView);
+ export function DeselectView(docView: DocumentView): void {
+ manager.DeselectView(docView);
}
- export function SelectDoc(docView: DocumentView, ctrlPressed: boolean): void {
- manager.SelectDoc(docView, ctrlPressed);
+ export function SelectView(docView: DocumentView, ctrlPressed: boolean): void {
+ manager.SelectView(docView, ctrlPressed);
}
- export function SelectSchemaDoc(colSchema: Opt<CollectionSchemaView>, document: Opt<Doc>): void {
- manager.SelectSchemaDoc(colSchema, document);
+ export function SelectSchemaView(colSchema: Opt<CollectionSchemaView>, document: Opt<Doc>): void {
+ manager.SelectSchemaView(colSchema, document);
}
const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) { // wraapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed
- return manager.SelectedDocuments.get(doc) ? true : false;
+ return manager.SelectedViews.get(doc) ? true : false;
});
// 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
// to avoid unnecessary mobx invalidations when running inside a reaction.
export function IsSelected(doc: DocumentView | undefined, outsideReaction?: boolean): boolean {
return !doc ? false : outsideReaction ?
- manager.SelectedDocuments.get(doc) ? true : false : // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get()
+ manager.SelectedViews.get(doc) ? true : false : // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get()
IsSelectedCache(doc);
}
export function DeselectAll(except?: Doc): void {
let found: DocumentView | undefined = undefined;
if (except) {
- for (const view of Array.from(manager.SelectedDocuments.keys())) {
+ for (const view of Array.from(manager.SelectedViews.keys())) {
if (view.props.Document === except) found = view;
}
}
manager.DeselectAll();
- if (found) manager.SelectDoc(found, false);
+ if (found) manager.SelectView(found, false);
}
- export function SelectedDocuments(): Array<DocumentView> {
- return Array.from(manager.SelectedDocuments.keys()).filter(dv => dv.props.Document._viewType !== CollectionViewType.Docking);
+ export function Views(): Array<DocumentView> {
+ return Array.from(manager.SelectedViews.keys()).filter(dv => dv.props.Document._viewType !== CollectionViewType.Docking);
}
export function SelectedSchemaDoc(): Doc | undefined {
return manager.SelectedSchemaDocument;
diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss
index badba35f4..5ca54517c 100644
--- a/src/client/util/SettingsManager.scss
+++ b/src/client/util/SettingsManager.scss
@@ -33,9 +33,10 @@
padding-right: 15px;
color: black;
margin-top: 10px;
+ margin-bottom: 10px;
/* right: 135; */
- position: absolute;
- left: 243;
+ // position: absolute;
+ // left: 243;
}
.settings-section {
@@ -61,30 +62,38 @@
.password-content {
display: flex;
+ flex-direction: column;
.password-content-inputs {
width: 100;
+ // margin-bottom: 10px;
+ font-size: 10px;
.password-inputs {
- border: none;
+ border: 1px solid rgb(160, 160, 160);
margin-bottom: 8px;
- width: 180;
+ width: 130;
color: black;
border-radius: 5px;
+ padding:7px;
+
}
}
.password-content-buttons {
- margin-left: 84px;
- width: 100;
+ //margin-left: 84px;
+ //width: 100;
+ padding: 7px;
.password-submit {
- margin-left: 85px;
+ //margin-left: 85px;
+ margin-top: 5px;
}
.password-forgot {
- margin-left: 65px;
- margin-top: -20px;
+ //margin-left: 65px;
+ //margin-top: -20px;
+ font-size: 12px;
white-space: nowrap;
}
}
@@ -97,10 +106,12 @@
.modes-content {
display: flex;
margin-left: 10px;
- font-size: 12;
+ font-size: 12px;
.modes-select {
// width: 170px;
+ width: 80%;
+ height: 35px;
margin-right: 10px;
color: black;
border-radius: 5px;
@@ -114,7 +125,8 @@
.default-acl {
display: flex;
margin-left: 10px;
- font-size: 12;
+ margin-top: 10px;
+ font-size: 10px;
.playground-check,
.acl-check {
@@ -134,6 +146,7 @@
.acl-text {
color: black;
margin-top: 2;
+ text-align: left;
}
}
@@ -141,7 +154,7 @@
.colorFlyout {
margin-top: 2px;
- margin-right: 18px;
+ //margin-right: 18px;
&:hover {
cursor: pointer;
@@ -156,67 +169,85 @@
}
}
-.preferences-content {
+.prefs-content{
+ text-align: left;
+}
+
+.appearances-content {
display: flex;
margin-top: 4px;
color: black;
- font-size: 11;
+ font-size: 10px;
.preferences-color {
display: flex;
margin-top: 2px;
- width: 55;
.preferences-color-text {
- margin-top: 4;
+ margin-top: 3px;
margin-right: 4;
+ flex: 1 1 auto;
+ text-align: left;
+ }
+
+ .colorFlyout {
+ align-self: flex-end;
}
}
.preferences-font {
- display: flex;
- height: 23px;
+ //height: 23px;
margin-top: 2px;
.preferences-font-text {
color: black;
margin-top: 4;
margin-right: 4;
+ margin-bottom: 2px;
+ text-align: left;
+ }
+
+ .preferences-font-controls {
+ display: flex;
+ justify-content: space-between;
}
.font-select {
- width: 100px;
+ height: 35px;
color: black;
font-size: 9;
margin-right: 6;
border-radius: 5px;
+ width: 65%;
&:hover {
cursor: pointer;
}
}
- .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;
+ height: 35px;
color: black;
font-size: 9;
border-radius: 5px;
+ width: 30%;
&:hover {
cursor: pointer;
}
}
}
+
+ .preferences-check {
+ color: black;
+ margin-right: 4;
+ margin-bottom: -3;
+ margin-left: 5;
+ margin-top: -1px;
+ display: inline-block;
+ padding-left: 5px;
+ text-align: left;
+ }
}
.settings-interface {
@@ -247,16 +278,17 @@
cursor: pointer;
}
- .logout-button {
- right: 355;
- position: absolute;
- }
+ // .logout-button {
+ // right: 355;
+ // position: absolute;
+ // }
.settings-content {
background: #e4e4e4;
- border-radius: 6px;
+ //border-radius: 6px;
padding: 10px;
- width: 560px;
+ //width: 560px;
+ flex: 1 1 auto;
}
.settings-top {
@@ -323,6 +355,94 @@
}
}
+.settings-interface {
+ flex-direction: row;
+ position: relative;
+ min-height: 250px;
+ width: 100%;
+
+ .settings-content {
+ background-color: #fdfdfd;
+ }
+}
+
+.settings-panel {
+ position: relative;
+ min-width: 150px;
+ background-color: #e4e4e4;
+
+ .settings-user {
+ position: absolute;
+ bottom: 10px;
+ text-align: center;
+ left: 0;
+ right: 0;
+
+ .settings-username {
+ padding-right: 0px;
+ }
+
+ .logout-button {
+ margin-right: 2px;
+ }
+ }
+}
+
+.settings-tabs {
+ // font-size: 16px;
+ font-weight: 600;
+ color: black;
+
+ .tab-control {
+ padding: 10px;
+ border-bottom: 1px solid #9f9e9e;
+ cursor: pointer;
+
+ &.active {
+ background-color: #fdfdfd;
+ }
+ }
+}
+
+.settings-section-context {
+ width: 100%;
+}
+
+.tab-section {
+ display: none;
+ height: 200px;
+
+ &.active {
+ display: block;
+ }
+}
+
+.tab-content {
+ display: flex;
+ margin: 20px 0;
+
+ .tab-column {
+ flex: 0 0 50%;
+
+ .tab-column-title {
+ color: black;
+ font-size: 16px;
+ font-weight: bold;
+ margin-bottom: 16px;
+ }
+
+ .tab-column-title, .tab-column-content {
+ padding-left: 16px;
+ }
+
+ }
+
+}
+
+.tab-column button {
+ font-size: 9px;
+}
+
@media only screen and (max-device-width: 480px) {
.settings-interface {
width: 80vw;
@@ -342,4 +462,4 @@
.settings-interface .settings-heading {
font-size: 25;
}
-} \ No newline at end of file
+}
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 9934f26d3..ff7ce68ee 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -29,9 +29,11 @@ export class SettingsManager extends React.Component<{}> {
@observable private curr_password = "";
@observable private new_password = "";
@observable private new_confirm = "";
+ @observable activeTab = "Accounts";
@computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; }
+
constructor(props: {}) {
super(props);
SettingsManager.Instance = this;
@@ -67,7 +69,7 @@ export class SettingsManager extends React.Component<{}> {
else DocServer.Control.makeEditable();
});
- @computed get preferencesContent() {
+ @computed get colorsContent() {
const colorBox = (func: (color: ColorState) => void) => <SketchPicker onChange={func} color={StrCast(this.backgroundColor)}
presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
'#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
@@ -91,41 +93,62 @@ export class SettingsManager extends React.Component<{}> {
const fontFamilies = ["Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
const fontSizes = ["7px", "8px", "9px", "10px", "12px", "14px", "16px", "18px", "20px", "24px", "32px", "48px", "72px"];
- return <div className="preferences-content">
+ return <div className="colors-content">
<div className="preferences-color">
- <div className="preferences-color-text">Back. Color</div>
+ <div className="preferences-color-text">Background Color</div>
{colorFlyout}
</div>
<div className="preferences-color">
- <div className="preferences-color-text">User Color</div>
+ <div className="preferences-color-text">Border/Header Color</div>
{userColorFlyout}
</div>
<div className="preferences-font">
- <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" style={{ marginRight: "10px" }} onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7px")}>
- {fontSizes.map(size => <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}> {size} </option>)}
- </select>
- <div>
- <div className="preferences-check">Show header</div>
- <input type="checkbox" onChange={e => Doc.UserDoc().showTitle = Doc.UserDoc().showTitle ? undefined : "creationDate"} checked={Doc.UserDoc().showTitle !== undefined} />
- </div>
- <div>
- <div className="preferences-check">Full Toolbar</div>
- <input type="checkbox" onChange={e => Doc.UserDoc()["documentLinksButton-fullMenu"] = !Doc.UserDoc()["documentLinksButton-fullMenu"]}
- checked={BoolCast(Doc.UserDoc()["documentLinksButton-fullMenu"])} />
- </div>
- <div>
- <div className="preferences-check">Raise on drag</div>
- <input type="checkbox" onChange={e => Doc.UserDoc()._raiseWhenDragged = !Doc.UserDoc()._raiseWhenDragged}
- checked={BoolCast(Doc.UserDoc()._raiseWhenDragged)} />
+ <div className="preferences-font-text">Default Font</div>
+ <div className="preferences-font-controls">
+ <select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7px")}>
+ {fontSizes.map(size => <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}> {size} </option>)}
+ </select>
+ <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>
</div>
</div>
</div>;
}
+ @computed get formatsContent() {
+ return <div className="prefs-content">
+ <div>
+ <input type="checkbox" onChange={e => Doc.UserDoc().showTitle = Doc.UserDoc().showTitle ? undefined : "creationDate"} checked={Doc.UserDoc().showTitle !== undefined} />
+ <div className="preferences-check">Show doc header</div>
+ </div>
+ <div>
+ <input type="checkbox" onChange={e => Doc.UserDoc()["documentLinksButton-fullMenu"] = !Doc.UserDoc()["documentLinksButton-fullMenu"]}
+ checked={BoolCast(Doc.UserDoc()["documentLinksButton-fullMenu"])} />
+ <div className="preferences-check">Show full toolbar</div>
+ </div>
+ <div>
+ <input type="checkbox" onChange={e => Doc.UserDoc()._raiseWhenDragged = !Doc.UserDoc()._raiseWhenDragged}
+ checked={BoolCast(Doc.UserDoc()._raiseWhenDragged)} />
+ <div className="preferences-check">Raise on drag</div>
+ </div>
+ </div>;
+ }
+
+ @computed get appearanceContent() {
+
+ return <div className="tab-content appearances-content">
+ <div className="tab-column">
+ <div className="tab-column-title">Colors</div>
+ <div className="tab-column-content">{this.colorsContent}</div>
+ </div>
+ <div className="tab-column">
+ <div className="tab-column-title">Formats</div>
+ <div className="tab-column-content">{this.formatsContent}</div>
+ </div>
+ </div>;
+ }
+
@action
changeVal = (e: React.ChangeEvent, pass: string) => {
const value = (e.target as any).value;
@@ -145,58 +168,91 @@ export class SettingsManager extends React.Component<{}> {
</div>
<div className="password-content-buttons">
{!this.passwordResultText ? (null) : <div className={`${this.passwordResultText.startsWith("Error") ? "error" : "success"}-text`}>{this.passwordResultText}</div>}
- <button className="password-submit" onClick={this.changePassword}>submit</button>
<a className="password-forgot" href="/forgotPassword">forgot password?</a>
+ <button className="password-submit" onClick={this.changePassword}>submit</button>
</div>
</div>;
}
- @computed get modesContent() {
- return <div className="modes-content">
- <select className="modes-select" onChange={this.selectUserMode} defaultValue={Doc.UserDoc().noviceMode ? "Novice" : "Developer"}>
- <option key={"Novice"} value={"Novice"}> Novice </option>
- <option key={"Developer"} value={"Developer"}> Developer</option>
- </select>
- <div className="modes-playground">
- <input className="playground-check" type="checkbox" checked={this.playgroundMode} onChange={this.playgroundModeToggle} />
- <div className="playground-text">Playground Mode</div>
- </div>
- <div className="default-acl">
- <input className="acl-check" type="checkbox" checked={BoolCast(Doc.UserDoc()?.defaultAclPrivate)} onChange={action(() => Doc.UserDoc().defaultAclPrivate = !Doc.UserDoc().defaultAclPrivate)} />
- <div className="acl-text">Default access private</div>
- </div>
+ @computed get accountOthersContent() {
+ return <div className="account-others-content">
+ <button onClick={this.googleAuthorize} value="data">Authorize Google Acc</button>
</div>;
}
@computed get accountsContent() {
- return <div className="accounts-content">
- <button onClick={this.googleAuthorize} value="data">Link to Google</button>
- <button onClick={() => GroupManager.Instance?.open()}>Manage groups</button>
+ return <div className="tab-content accounts-content">
+ <div className="tab-column">
+ <div className="tab-column-title">Password</div>
+ <div className="tab-column-content">{this.passwordContent}</div>
+ </div>
+ <div className="tab-column">
+ <div className="tab-column-title">Others</div>
+ <div className="tab-column-content">{this.accountOthersContent}</div>
+ </div>
+ </div>;
+ }
+
+ @computed get modesContent() {
+ return <div className="tab-content modes-content">
+ <div className="tab-column">
+ <div className="tab-column-title">Modes</div>
+ <div className="tab-column-content">
+ <select className="modes-select" onChange={this.selectUserMode} defaultValue={Doc.UserDoc().noviceMode ? "Novice" : "Developer"}>
+ <option key={"Novice"} value={"Novice"}> Novice </option>
+ <option key={"Developer"} value={"Developer"}> Developer</option>
+ </select>
+ <div className="modes-playground">
+ <input className="playground-check" type="checkbox" checked={this.playgroundMode} onChange={this.playgroundModeToggle} />
+ <div className="playground-text">Playground Mode</div>
+ </div>
+ </div>
+ </div>
+ <div className="tab-column">
+ <div className="tab-column-title">Permissions</div>
+ <div className="tab-column-content">
+ <button onClick={() => GroupManager.Instance?.open()}>Manage groups</button>
+ <div className="default-acl">
+ <input className="acl-check" type="checkbox" checked={BoolCast(Doc.UserDoc()?.defaultAclPrivate)} onChange={action(() => Doc.UserDoc().defaultAclPrivate = !Doc.UserDoc().defaultAclPrivate)} />
+ <div className="acl-text">Default access private</div>
+ </div>
+ </div>
+ </div>
+
</div>;
}
+
private get settingsInterface() {
- const pairs = [{ title: "Password", ele: this.passwordContent }, { title: "Modes", ele: this.modesContent },
- { title: "Accounts", ele: this.accountsContent }, { title: "Preferences", ele: this.preferencesContent }];
+ // const pairs = [{ title: "Password", ele: this.passwordContent }, { title: "Modes", ele: this.modesContent },
+ // { title: "Accounts", ele: this.accountsContent }, { title: "Preferences", ele: this.preferencesContent }];
+
+ const tabs = [{ title: "Accounts", ele: this.accountsContent }, { title: "Modes", ele: this.modesContent },
+ { title: "Appearance", ele: this.appearanceContent }];
+
return <div className="settings-interface">
- <div className="settings-top">
- <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.GuestDashboard ? "Exit" : "Log Out"}
- </button>
- <div className="close-button" onClick={this.close}>
- <FontAwesomeIcon icon={"times"} color="black" size={"lg"} />
+ <div className="settings-panel">
+ <div className="settings-tabs">
+ {tabs.map(tab => <div key={tab.title} className={"tab-control " + (this.activeTab === tab.title ? "active" : "inactive")} onClick={action(() => this.activeTab = tab.title)}>{tab.title}</div>)}
+ </div>
+
+ <div className="settings-user">
+ <div className="settings-username">{Doc.CurrentUserEmail}</div>
+ <button className="logout-button" onClick={() => window.location.assign(Utils.prepend("/logout"))} >
+ {CurrentUserUtils.GuestDashboard ? "Exit" : "Log Out"}
+ </button>
</div>
</div>
+
+ <div className="close-button" onClick={this.close}>
+ <FontAwesomeIcon icon={"times"} color="black" size={"lg"} />
+ </div>
+
<div className="settings-content">
- {pairs.map(pair => <div className="settings-section" key={pair.title}>
- <div className="settings-section-title">{pair.title}</div>
- <div className="settings-section-context">{pair.ele}</div>
- </div>
- )}
+ {tabs.map(tab => <div key={tab.title} className={"tab-section " + (this.activeTab === tab.title ? "active" : "inactive")}>{tab.ele}</div>)}
</div>
</div>;
+
}
render() {
@@ -205,6 +261,6 @@ export class SettingsManager extends React.Component<{}> {
isDisplayed={this.isOpen}
interactive={true}
closeOnExternalClick={this.close}
- dialogueBoxStyle={{ width: "600px", background: Cast(Doc.SharingDoc().userColor, "string", null) }} />;
+ dialogueBoxStyle={{ width: "500px", height: "300px", background: Cast(Doc.SharingDoc().userColor, "string", null) }} />;
}
} \ No newline at end of file
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 2b13d2a44..2aea73528 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,13 +1,14 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable, runInAction, computed } from "mobx";
+import { intersection } from "lodash";
+import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as React from "react";
import Select from "react-select";
import * as RequestPromise from "request-promise";
-import { AclAdmin, AclPrivate, DataSym, Doc, DocListCast, Opt, AclSym, AclAddonly, AclEdit, AclReadonly, DocListCastAsync } from "../../fields/Doc";
+import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
import { List } from "../../fields/List";
import { Cast, StrCast } from "../../fields/Types";
-import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx, normalizeEmail } from "../../fields/util";
+import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from "../../fields/util";
import { Utils } from "../../Utils";
import { DocServer } from "../DocServer";
import { CollectionView } from "../views/collections/CollectionView";
@@ -15,13 +16,12 @@ import { DictationOverlay } from "../views/DictationOverlay";
import { MainViewModal } from "../views/MainViewModal";
import { DocumentView } from "../views/nodes/DocumentView";
import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
+import { SearchBox } from "../views/search/SearchBox";
import { DocumentManager } from "./DocumentManager";
import { GroupManager, UserOptions } from "./GroupManager";
import { GroupMemberView } from "./GroupMemberView";
-import "./SharingManager.scss";
import { SelectionManager } from "./SelectionManager";
-import { intersection } from "lodash";
-import { SearchBox } from "../views/search/SearchBox";
+import "./SharingManager.scss";
export interface User {
email: string;
@@ -134,6 +134,10 @@ export class SharingManager extends React.Component<{}> {
const linkDatabase = await DocServer.GetRefField(user.linkDatabaseId);
if (sharingDoc instanceof Doc && linkDatabase instanceof Doc) {
await DocListCastAsync(linkDatabase.data);
+ (await DocListCastAsync(Cast(linkDatabase, Doc, null).data))?.forEach(async link => { // makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager
+ const a1 = await Cast(link?.anchor1, Doc, null);
+ const a2 = await Cast(link?.anchor2, Doc, null);
+ });
sharingDocs.push({ user, sharingDoc, linkDatabase, userColor: StrCast(sharingDoc.color) });
}
}
@@ -162,7 +166,7 @@ export class SharingManager extends React.Component<{}> {
const key = normalizeEmail(StrCast(group.title));
const acl = `acl-${key}`;
- const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document);
+ const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
docs.forEach(doc => {
doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc);
@@ -267,7 +271,7 @@ export class SharingManager extends React.Component<{}> {
const acl = `acl-${normalizeEmail(user.email)}`;
const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
- const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document);
+ const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
docs.forEach(doc => {
doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc);
distributeAcls(acl, permission as SharingPermissions, doc);
@@ -319,7 +323,7 @@ export 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];
+ const docs = SelectionManager.Views().length > 1 ? SelectionManager.Views().map(docView => docView.props.Document) : [this.targetDoc];
return (
<span
className={"focus-span"}
@@ -403,7 +407,7 @@ export class SharingManager extends React.Component<{}> {
const target = targetDoc || this.targetDoc!;
- const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document);
+ const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
docs.forEach(doc => {
for (const [key, value] of Object.entries(doc[AclSym])) {
distributeAcls(key, AclMap.get(value)! as SharingPermissions, target);
@@ -454,9 +458,9 @@ export class SharingManager extends React.Component<{}> {
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
- let docs = SelectionManager.SelectedDocuments().length < 2 ?
+ let docs = SelectionManager.Views().length < 2 ?
[this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DataSym]]
- : SelectionManager.SelectedDocuments().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DataSym]);
+ : SelectionManager.Views().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DataSym]);
if (this.myDocAcls) {
const newDocs: Doc[] = [];