aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/DashboardView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/DashboardView.tsx')
-rw-r--r--src/client/views/DashboardView.tsx400
1 files changed, 263 insertions, 137 deletions
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 868d63a90..c59c37488 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -1,164 +1,290 @@
-import { action, computed, observable } from "mobx";
-import { extname } from 'path';
-import { observer } from "mobx-react";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, DocListCast } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { Cast, ImageCast, StrCast } from "../../fields/Types";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { undoBatch, 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";
+import { DataSym, Doc, DocListCast, DocListCastAsync } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { List } from '../../fields/List';
+import { Cast, ImageCast, StrCast } from '../../fields/Types';
+import { DocServer } from '../DocServer';
+import { Docs, DocumentOptions } from '../documents/Documents';
+import { CollectionViewType } from '../documents/DocumentTypes';
+import { HistoryUtil } from '../util/History';
+import { SharingManager } from '../util/SharingManager';
+import { undoBatch } from '../util/UndoManager';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { CollectionView } from './collections/CollectionView';
+import { ContextMenu } from './ContextMenu';
+import './DashboardView.scss';
+import { MainViewModal } from './MainViewModal';
enum DashboardGroup {
- MyDashboards, SharedDashboards
+ 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.
- //TODO: delete dashboard, share dashboard, etc.
+ public static _urlState: HistoryUtil.DocUrl;
- @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 }
+ @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
- }
+ @action
+ selectDashboardGroup = (group: DashboardGroup) => {
+ this.selectedDashboardGroup = group;
+ };
- clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
- if (e.detail === 2) {
- Doc.AddDocToList(CurrentUserUtils.MySharedDocs, "viewed", dashboard)
- CurrentUserUtils.ActiveDashboard = dashboard;
- CurrentUserUtils.ActivePage = "dashboard";
+ clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
+ if (e.detail === 2) {
+ Doc.AddDocToList(Doc.MySharedDocs, 'viewed', dashboard);
+ Doc.ActiveDashboard = dashboard;
+ Doc.ActivePage = 'dashboard';
+ }
+ };
+
+ getDashboards = () => {
+ const allDashboards = DocListCast(Doc.MyDashboards.data);
+ if (this.selectedDashboardGroup === DashboardGroup.MyDashboards) {
+ return allDashboards.filter(dashboard => Doc.GetProto(dashboard).author === Doc.CurrentUserEmail);
+ } else {
+ const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
+ return sharedDashboards;
+ }
+ };
+
+ isUnviewedSharedDashboard = (dashboard: Doc): boolean => {
+ // const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
+ return !DocListCast(Doc.MySharedDocs.viewed).includes(dashboard);
+ };
+
+ getSharedDashboards = () => {
+ const sharedDashs = DocListCast(Doc.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
+ return sharedDashs.filter(dashboard => !DocListCast(Doc.MySharedDocs.viewed).includes(dashboard));
+ };
+
+ @undoBatch
+ createNewDashboard = async (name: string) => {
+ DashboardView.createNewDashboard(undefined, name);
+ 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 () => {
+ DashboardView.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.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>
+ {this.selectedDashboardGroup === DashboardGroup.SharedDashboards && this.isUnviewedSharedDashboard(dashboard) ? <div>unviewed</div> : <div></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>
+ <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) }}
+ />
+ ;
+ </>
+ );
+ }
+
+ public static closeActiveDashboard() {
+ Doc.ActiveDashboard = undefined;
+ }
+ public static snapshotDashboard() {
+ return CollectionDockingView.TakeSnapshot(Doc.ActiveDashboard);
+ }
- getDashboards = () => {
- const allDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
- if (this.selectedDashboardGroup === DashboardGroup.MyDashboards) {
- return allDashboards.filter((dashboard) => Doc.GetProto(dashboard).author === Doc.CurrentUserEmail)
+ /// opens a dashboard as the ActiveDashboard (and adds the dashboard to the users list of dashboards if it's not already there).
+ /// this also sets the readonly state of the dashboard based on the current mode of dash (from its url)
+ public static openDashboard = (doc: Doc | undefined, fromHistory = false) => {
+ if (!doc) return false;
+ Doc.MainDocId = doc[Id];
+ Doc.AddDocToList(Doc.MyDashboards, 'data', doc);
+
+ // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
+ Doc.UserDoc() ? (Doc.ActiveDashboard = doc) : (Doc.GuestDashboard = doc);
+
+ const state = DashboardView._urlState;
+ if (state.sharing === true && !Doc.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 {
- const sharedDashboards = DocListCast(CurrentUserUtils.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
- return sharedDashboards
+ DocServer.Control.makeEditable();
}
- }
-
- isUnviewedSharedDashboard = (dashboard: Doc): boolean => {
- // const sharedDashboards = DocListCast(CurrentUserUtils.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
- return !DocListCast(CurrentUserUtils.MySharedDocs.viewed).includes(dashboard)
- }
-
- getSharedDashboards = () => {
- const sharedDashs = DocListCast(CurrentUserUtils.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
- return sharedDashs.filter((dashboard) => !DocListCast(CurrentUserUtils.MySharedDocs.viewed).includes(dashboard))
- }
-
- @undoBatch
- createNewDashboard = async (name: string) => {
- CurrentUserUtils.createNewDashboard(undefined, name);
- 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.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>
- {this.selectedDashboardGroup === DashboardGroup.SharedDashboards && this.isUnviewedSharedDashboard(dashboard) ?
- <div>unviewed</div> : <div></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>
+ return true;
+ };
- })}
- </div>
+ public static removeDashboard = async (dashboard: Doc) => {
+ const dashboards = await DocListCastAsync(Doc.MyDashboards.data);
+ if (dashboards?.length) {
+ if (dashboard === Doc.ActiveDashboard) DashboardView.openDashboard(dashboards.find(doc => doc !== dashboard));
+ Doc.RemoveDocFromList(Doc.MyDashboards, 'data', dashboard);
+ if (!dashboards.length) Doc.ActivePage = 'home';
+ }
+ };
- </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) }} />;
- </>
+ public static createNewDashboard = (id?: string, name?: string) => {
+ const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
+ const dashboards = Doc.MyDashboards;
+ const dashboardCount = DocListCast(dashboards.data).length + 1;
+ const freeformOptions: DocumentOptions = {
+ x: 0,
+ y: 400,
+ _width: 1500,
+ _height: 1000,
+ _fitWidth: true,
+ _backgroundGridShow: true,
+ title: `Untitled Tab 1`,
+ };
+ const title = name ? name : `Dashboard ${dashboardCount}`;
+ const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
+ 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
+ const dashboardTabs = DocListCast(dashboardDoc[DataSym].data);
+ dashboardDoc.data = new List<Doc>(dashboardTabs);
+ dashboardDoc['pane-count'] = 1;
+
+ Doc.ActivePresentation = presentation;
+
+ Doc.AddDocToList(dashboards, 'data', dashboardDoc);
+ // open this new dashboard
+ Doc.ActiveDashboard = dashboardDoc;
+ Doc.ActivePage = 'dashboard';
+ };
}
export function AddToList(MySharedDocs: Doc, arg1: string, dash: any) {
- throw new Error("Function not implemented.");
+ throw new Error('Function not implemented.');
}
-