aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts3
-rw-r--r--src/client/util/CurrentUserUtils.ts20
-rw-r--r--src/client/views/DashboardView.scss18
-rw-r--r--src/client/views/DashboardView.tsx162
-rw-r--r--src/client/views/MainView.tsx10
-rw-r--r--src/client/views/nodes/button/FontIconBadge.tsx2
-rw-r--r--src/client/views/topbar/TopBar.scss2
-rw-r--r--src/client/views/topbar/TopBar.tsx55
8 files changed, 205 insertions, 67 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 5f009573e..fdc8690cd 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -254,7 +254,7 @@ export class DocumentOptions {
numBtnType?: string;
numBtnMax?: number;
numBtnMin?: number;
- switchToggle?: boolean;
+ switchToggle?: boolean;
badgeValue?: ScriptField;
//LINEAR VIEW
@@ -300,7 +300,6 @@ export class DocumentOptions {
treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
treeViewGrowsHorizontally?: boolean; // whether an embedded tree view of the document can grow horizontally without growing vertically
treeViewChildDoubleClick?: ScriptField; //
-
// Action Button
buttonMenu?: boolean; // whether a action button should be displayed
buttonMenuDoc?: Doc;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b4e18a8bb..f1343e472 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -23,6 +23,7 @@ import { Colors } from "../views/global/globalEnums";
import { MainView } from "../views/MainView";
import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView";
+import { DocumentView } from "../views/nodes/DocumentView";
import { OverlayView } from "../views/OverlayView";
import { DocumentManager } from "./DocumentManager";
import { DragManager } from "./DragManager";
@@ -1082,6 +1083,7 @@ export class CurrentUserUtils {
// undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme;
}
+
return doc;
}
@@ -1235,7 +1237,15 @@ export class CurrentUserUtils {
Doc.UserDoc().activeDashboard = undefined;
}
- public static createNewDashboard = async (userDoc: Doc, id?: string) => {
+ public static async removeDashboard(dashboard: Doc) {
+ const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data);
+ if (dashboards && dashboards.length > 0) {
+ Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard);
+ }
+ }
+
+ public static createNewDashboard = async (userDoc: Doc, id?: string, name?: string) => {
+ console.log(name)
const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true);
const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc;
const dashboardCount = DocListCast(dashboards.data).length + 1;
@@ -1248,8 +1258,9 @@ export class CurrentUserUtils {
_backgroundGridShow: true,
title: `Untitled Tab 1`,
};
+ const title = name ? name : `Dashboard ${dashboardCount}`
const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
- const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row");
+ const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, "row");
freeformDoc.context = dashboardDoc;
// switching the tabs from the datadoc to the regular doc
@@ -1260,7 +1271,9 @@ export class CurrentUserUtils {
userDoc.activePresentation = presentation;
Doc.AddDocToList(dashboards, "data", dashboardDoc);
- // CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
+ // open this new dashboard
+ Doc.UserDoc().activeDashboard = dashboardDoc;
+ Doc.UserDoc().activePage = "dashboard";
}
public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) {
@@ -1284,6 +1297,7 @@ export class CurrentUserUtils {
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 MySharedDocs() { return Cast(Doc.UserDoc().mySharedDocs, Doc, null); }
public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); }
public static get OverlayDocs() { return DocListCast((Doc.UserDoc().myOverlayDocs as Doc)?.data); }
public static set SelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; }
diff --git a/src/client/views/DashboardView.scss b/src/client/views/DashboardView.scss
index 67587dd2b..3db23b86f 100644
--- a/src/client/views/DashboardView.scss
+++ b/src/client/views/DashboardView.scss
@@ -2,6 +2,8 @@
padding: 50px;
display: flex;
flex-direction: row;
+ width: 100%;
+ position: absolute;
.left-menu {
display: flex;
@@ -45,4 +47,20 @@
margin: 10px;
font-weight: 500;
}
+
+ img {
+ width: auto;
+ height: 80%;
+ }
+
+ .info {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .more {
+ z-index: 100;
+ }
} \ No newline at end of file
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index efc1644fe..a105e0790 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -1,4 +1,4 @@
-import { action, observable } from "mobx";
+import { action, computed, observable } from "mobx";
import { extname } from 'path';
import { observer } from "mobx-react";
import * as React from 'react';
@@ -8,30 +8,37 @@ import { Cast, ImageCast, StrCast } from "../../fields/Types";
import { CurrentUserUtils } from "../util/CurrentUserUtils";
import { UndoManager } from "../util/UndoManager";
import "./DashboardView.scss"
+import { MainViewModal } from "./MainViewModal";
+import { ContextMenu } from "./ContextMenu";
+import { DocumentManager } from "../util/DocumentManager";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { ContextMenuProps } from "./ContextMenuItem";
+import { simulateMouseClick } from "../../Utils";
+import { SharingManager } from "../util/SharingManager";
+import { CollectionViewType } from "./collections/CollectionView";
enum DashboardGroup {
MyDashboards, SharedDashboards
}
+// DashboardView is the view with the dashboard previews, rendered when the app first loads
+
@observer
export class DashboardView extends React.Component {
//TODO: delete dashboard, share dashboard, etc.
- @observable
- private selectedDashboardGroup = DashboardGroup.MyDashboards;
+ @observable private selectedDashboardGroup = DashboardGroup.MyDashboards;
+
+ @observable private newDashboardName: string | undefined = undefined;
+ @action abortCreateNewDashboard = () => { this.newDashboardName = undefined }
+ @action setNewDashboardName(name: string) { this.newDashboardName = name }
@action
selectDashboardGroup = (group: DashboardGroup) => {
this.selectedDashboardGroup = group
}
- newDashboard = async () => {
- const batch = UndoManager.StartBatch("new dash");
- await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
- batch.end();
- }
-
clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
if (e.detail === 2) {
Doc.UserDoc().activeDashboard = dashboard;
@@ -40,30 +47,129 @@ export class DashboardView extends React.Component {
}
getDashboards = () => {
- const allDashbaords = DocListCast(CurrentUserUtils.MyDashboards.data);
- // TODO: filter the dashboards
- // return allDashbaords.filter(...)
- return allDashbaords
+ const allDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+ if (this.selectedDashboardGroup === DashboardGroup.MyDashboards) {
+ return allDashboards.filter((dashboard) => Doc.GetProto(dashboard).author === Doc.CurrentUserEmail)
+ } else {
+ return allDashboards.filter((dashboard) => Doc.GetProto(dashboard).author !== Doc.CurrentUserEmail)
+ }
+ }
+
+ getSharedDashboards = () => {
+ const sharedDashs = DocListCast(CurrentUserUtils.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
+ return sharedDashs.filter((dashboard) => !DocListCast(CurrentUserUtils.MySharedDocs.viewed).includes(dashboard))
+ }
+
+ createNewDashboard = async (name: string) => {
+ const batch = UndoManager.StartBatch("new dash");
+ await CurrentUserUtils.createNewDashboard(Doc.UserDoc(), undefined, name);
+ batch.end();
+ this.abortCreateNewDashboard();
+ }
+
+ @computed
+ get namingInterface() {
+ return <div>
+ <input className="password-inputs" placeholder="Untitled Dashboard" onChange={e => this.setNewDashboardName((e.target as any).value)} />
+ <button className="password-submit" onClick={this.abortCreateNewDashboard}>Cancel</button>
+ <button className="password-submit" onClick={() => { this.createNewDashboard(this.newDashboardName!) }}>Create</button>
+ </div>;
}
+
+ _downX: number = 0;
+ _downY: number = 0;
+ @action
+ onContextMenu = (dashboard: Doc, e?: React.MouseEvent, pageX?: number, pageY?: number) => {
+ // the touch onContextMenu is button 0, the pointer onContextMenu is button 2
+ if (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ e.persist();
+
+ if (!navigator.userAgent.includes("Mozilla") && (Math.abs(this._downX - e?.clientX) > 3 || Math.abs(this._downY - e?.clientY) > 3)) {
+ return;
+ }
+ const cm = ContextMenu.Instance;
+ cm.addItem({
+ description: "Share Dashboard", event: async () => {
+ SharingManager.Instance.open(undefined, dashboard)
+ }, icon: "edit"
+ });
+ cm.addItem({
+ description: "Delete Dashboard", event: async () => {
+ CurrentUserUtils.removeDashboard(dashboard)
+ }, icon: "trash"
+ });
+ cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
+ }
+ }
+
render() {
- return <div className="dashboard-view">
- <div className="left-menu">
- <div className="text-button" onClick={this.newDashboard}>New</div>
- <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.MyDashboards && 'selected'}`}onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards) }>My Dashboards</div>
- <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.SharedDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.SharedDashboards) }>Shared Dashboards</div>
- </div>
- <div className="all-dashboards">
- {this.getDashboards().map((dashboard) => {
- const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href;
- return <div className="dashboard-container" key={dashboard[Id]} onClick={e => this.clickDashboard(e, dashboard)}>
- <img src={href ?? "https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU="}></img>
- <div className="title"> {StrCast(dashboard.title)} </div>
- </div>
+ return <>
+ <div className="dashboard-view">
+ <div className="left-menu">
+ <div className="text-button" onClick={() => { this.setNewDashboardName("") }}>New</div>
+ <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.MyDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards)}>My Dashboards</div>
+ <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.SharedDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.SharedDashboards)}>Shared Dashboards</div>
+ </div>
+ <div className="all-dashboards">
+ {this.getDashboards().map((dashboard) => {
+ const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href;
+ return <div className="dashboard-container" key={dashboard[Id]}
+ onContextMenu={(e) => {this.onContextMenu(dashboard, e)}}
+ onClick={e => this.clickDashboard(e, dashboard)}>
+ <img src={href ?? "https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU="}></img>
+ <div className="info">
+ <div className="title"> {StrCast(dashboard.title)} </div>
+ <div className="more" onPointerDown={e => {
+ this._downX = e.clientX;
+ this._downY = e.clientY;
+ }}
+ onClick={(e) => {this.onContextMenu(dashboard, e)}}
+ >
+ <FontAwesomeIcon color="black" size="lg" icon="bars" />
+ </div>
+ </div>
- })}
+ </div>
+ })}
+ </div>
+
+ <div style={{display:"block"}} > UNVIEWED SHARED DASHBOARDS </div>
+ <div className="all-dashboards">
+ {this.getSharedDashboards().map((dashboard) => {
+ const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href;
+ return <div className="dashboard-container" key={dashboard[Id]}
+ onContextMenu={(e) => {this.onContextMenu(dashboard, e)}}
+ onClick={e => this.clickDashboard(e, dashboard)}>
+ <img src={href ?? "https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU="}></img>
+ <div className="info">
+ <div className="title"> {StrCast(dashboard.title)} </div>
+ <div className="more" onPointerDown={e => {
+ this._downX = e.clientX;
+ this._downY = e.clientY;
+ }}
+ onClick={(e) => {this.onContextMenu(dashboard, e)}}
+ >
+ <FontAwesomeIcon color="black" size="lg" icon="bars" />
+ </div>
+ </div>
+
+ </div>
+
+ })}
+
+ </div>
</div>
- </div>
+ <MainViewModal
+ contents={this.namingInterface}
+ isDisplayed={this.newDashboardName !== undefined}
+ interactive={true}
+ closeOnExternalClick={this.abortCreateNewDashboard}
+ dialogueBoxStyle={{ width: "500px", height: "300px", background: Cast(Doc.SharingDoc().userColor, "string", null) }} />;
+ </>
+
}
}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 5fd76c388..c32198f68 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -222,16 +222,10 @@ export class MainView extends React.Component {
}
initAuthenticationRouters = async () => {
- // Load the user's active dashboard, or create a new one if initial session after signup
const received = CurrentUserUtils.MainDocId;
if (received && !this.userDoc) {
- reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true });
- } else {
- PromiseValue(this.userDoc.activeDashboard).then(dash => {
- if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash);
- else CurrentUserUtils.createNewDashboard(this.userDoc);
- });
- }
+ reaction(() => CurrentUserUtils.GuestTarget, target => target);
+ }
}
@action
diff --git a/src/client/views/nodes/button/FontIconBadge.tsx b/src/client/views/nodes/button/FontIconBadge.tsx
index df17d603f..3b5aac221 100644
--- a/src/client/views/nodes/button/FontIconBadge.tsx
+++ b/src/client/views/nodes/button/FontIconBadge.tsx
@@ -6,7 +6,7 @@ import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils
import { DragManager } from "../../../util/DragManager";
import "./FontIconBadge.scss";
-interface FontIconBadgeProps {
+interface FontIconBadgeProps {
value: string | undefined;
}
diff --git a/src/client/views/topbar/TopBar.scss b/src/client/views/topbar/TopBar.scss
index c5b340514..214b20193 100644
--- a/src/client/views/topbar/TopBar.scss
+++ b/src/client/views/topbar/TopBar.scss
@@ -2,7 +2,6 @@
.topbar-container {
- display: flex;
flex-direction: column;
font-size: 10px;
line-height: 1;
@@ -11,6 +10,7 @@
background: $dark-gray;
overflow: visible;
z-index: 1000;
+ align-items: center;
height: $topbar-height;
background-color: $dark-gray;
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 0db3950a2..e4f48adce 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -27,8 +27,8 @@ import "./TopBar.scss";
export class TopBar extends React.Component {
navigateToHome = () => {
CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => {
- Doc.UserDoc().activePage = "home";
- CurrentUserUtils.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use
+ Doc.UserDoc().activePage = "home";
+ CurrentUserUtils.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use
});
}
render() {
@@ -38,39 +38,46 @@ export class TopBar extends React.Component {
<div style={{ pointerEvents: "all", background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }} className="topbar-container">
<div className="topbar-inner-container">
<div className="topbar-left">
- {activeDashboard ? <div className="topbar-button-text" onClick={e => {
- ContextMenu.Instance.addItem({ description: "Logout", event: () => window.location.assign(Utils.prepend("/logout")), icon: "edit" });
- ContextMenu.Instance.displayMenu(e.clientX +5, e.clientY + 10);
- }}>{Doc.CurrentUserEmail}</div> : (null)}
+ {activeDashboard ?
+ <>
+ <div className="topbar-button-text" onClick={e => {
+ ContextMenu.Instance.addItem({ description: "Logout", event: () => window.location.assign(Utils.prepend("/logout")), icon: "edit" });
+ ContextMenu.Instance.displayMenu(e.clientX + 5, e.clientY + 10);
+ }}>{Doc.CurrentUserEmail}</div>
+ <div className="topbar-button-icon" onClick={this.navigateToHome}>
+ <FontAwesomeIcon icon="home" />
+ </div>
+ </>
+ : (null)}
+
</div>
<div className="topbar-center" >
<div className="topbar-title" onClick={() => SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}>
{activeDashboard ? StrCast(activeDashboard.title) : "Dash"}
</div>
<div className="topbar-button-icon" onClick={e => {
- const dashView = DocumentManager.Instance.getDocumentView(activeDashboard);
- ContextMenu.Instance.addItem({ description: "Open Dashboard View", event: this.navigateToHome, icon: "edit" });
- ContextMenu.Instance.addItem({ description: "Snapshot Dashboard", event: async () => {
- const batch = UndoManager.StartBatch("snapshot");
- await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
- batch.end();
- }, icon: "edit" });
- dashView?.showContextMenu(e.clientX+20, e.clientY+30);
+ const dashView = DocumentManager.Instance.getDocumentView(activeDashboard);
+ ContextMenu.Instance.addItem({
+ description: "Snapshot Dashboard", event: async () => {
+ const batch = UndoManager.StartBatch("snapshot");
+ await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
+ batch.end();
+ }, icon: "edit"
+ });
+ dashView?.showContextMenu(e.clientX + 20, e.clientY + 30);
}}>
- <FontAwesomeIcon color="white" size="lg" icon="bars" />
+ <FontAwesomeIcon color="white" size="lg" icon="bars" />
</div>
<Tooltip title={<div className="dash-tooltip">Browsing mode for directly navigating to documents</div>} placement="bottom">
- <div className="topbar-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}>
- <FontAwesomeIcon color={MainView.Instance._exploreMode ? "red":"white"} icon="eye" size="lg" />
- </div>
+ <div className="topbar-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}>
+ <FontAwesomeIcon color={MainView.Instance._exploreMode ? "red" : "white"} icon="eye" size="lg" />
+ </div>
</Tooltip>
</div>
<div className="topbar-right" >
- <div className="topbar-button-text" onClick={() => {SharingManager.Instance.open(undefined, activeDashboard)}}>
- {/* TODO: if this is my dashboard, display share
- if this is a shared dashboard, display "view original or view annotated" */}
- { Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author === Doc.CurrentUserEmail ? "Share": "view original" }
- </div>
+ {CurrentUserUtils.ActiveDashboard ? <div className="topbar-button-text" onClick={() => { SharingManager.Instance.open(undefined, activeDashboard) }}>
+ {GetEffectiveAcl(Doc.GetProto(CurrentUserUtils.ActiveDashboard)) === AclAdmin ? "Share" : "view original"}
+ </div> : (null)}
<div className="topbar-button-icon" onClick={() => window.open(
"https://brown-dash.github.io/Dash-Documentation/", "_blank")}>
<FontAwesomeIcon icon="question-circle" />
@@ -81,7 +88,7 @@ export class TopBar extends React.Component {
</div>
</div>
</div>
-
+
);
}
} \ No newline at end of file