Skip to content

Commit a668b28

Browse files
committed
Added share modal to application setup
1 parent 9fd589b commit a668b28

File tree

10 files changed

+201
-22
lines changed

10 files changed

+201
-22
lines changed

src/application/Application.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import NeoNotificationModal from '../modal/NotificationModal';
1010
import NeoWelcomeScreenModal from '../modal/WelcomeScreenModal';
1111
import { removeReportRequest } from '../page/PageThunks';
1212
import { connect } from 'react-redux';
13-
import { applicationGetConnection, applicationGetOldDashboard, applicationHasNeo4jDesktopConnection, applicationHasAboutModalOpen, applicationHasCachedDashboard, applicationHasConnectionModalOpen, applicationIsConnected } from '../application/ApplicationSelectors';
14-
import { createConnectionThunk, createConnectionFromDesktopIntegrationThunk, setDatabaseFromNeo4jDesktopIntegrationThunk } from '../application/ApplicationThunks';
15-
import { clearDesktopConnectionProperties, clearNotification, createNotification, setAboutModalOpen, setConnected, setConnectionModalOpen, setOldDashboard } from '../application/ApplicationActions';
13+
import { applicationGetConnection, applicationGetShareDetails, applicationGetOldDashboard, applicationHasNeo4jDesktopConnection, applicationHasAboutModalOpen, applicationHasCachedDashboard, applicationHasConnectionModalOpen, applicationIsConnected } from '../application/ApplicationSelectors';
14+
import { createConnectionThunk, createConnectionFromDesktopIntegrationThunk, setDatabaseFromNeo4jDesktopIntegrationThunk, handleSharedDashboardsThunk } from '../application/ApplicationThunks';
15+
import { clearDesktopConnectionProperties, clearNotification, resetShareDetails, setAboutModalOpen, setConnected, setConnectionModalOpen, setOldDashboard } from '../application/ApplicationActions';
1616
import { resetDashboardState } from '../dashboard/DashboardActions';
1717
import { NeoDashboardPlaceholder } from '../dashboard/DashboardPlaceholder';
1818
import NeoConnectionModal from '../modal/ConnectionModal';
@@ -21,13 +21,14 @@ import { CircularProgress, Typography } from '@material-ui/core';
2121
import NeoAboutModal from '../modal/AboutModal';
2222
import { NeoUpgradeOldDashboardModal } from '../modal/UpgradeOldDashboardModal';
2323
import { loadDashboardThunk } from '../dashboard/DashboardThunks';
24+
import { NeoLoadSharedDashboardModal } from '../modal/LoadSharedDashboardModal';
2425

2526
/**
2627
*
2728
*/
2829
const Application = ({ connection, connected, hasCachedDashboard, oldDashboard, clearOldDashboard,
29-
connectionModalOpen, aboutModalOpen, loadDashboard, hasNeo4jDesktopConnection,
30-
createConnection, createConnectionFromDesktopIntegration,
30+
connectionModalOpen, aboutModalOpen, loadDashboard, hasNeo4jDesktopConnection, shareDetails,
31+
createConnection, createConnectionFromDesktopIntegration, onResetShareDetails,
3132
initializeApplication, resetDashboard, onAboutModalOpen, onAboutModalClose,
3233
onConnectionModalOpen, onConnectionModalClose }) => {
3334

@@ -45,8 +46,6 @@ const Application = ({ connection, connected, hasCachedDashboard, oldDashboard,
4546
<CssBaseline />
4647
<NeoDashboardPlaceholder connected={false}></NeoDashboardPlaceholder>
4748
{(connected) ? <Dashboard></Dashboard> : <></>}
48-
49-
<NeoNotificationModal></NeoNotificationModal>
5049
<NeoAboutModal
5150
open={aboutModalOpen}
5251
handleClose={onAboutModalClose}>
@@ -69,13 +68,19 @@ const Application = ({ connection, connected, hasCachedDashboard, oldDashboard,
6968
loadDashboard={loadDashboard}
7069
clearOldDashboard={clearOldDashboard}>
7170
</NeoUpgradeOldDashboardModal>
71+
<NeoLoadSharedDashboardModal
72+
shareDetails={shareDetails}
73+
onResetShareDetails={onResetShareDetails}>
74+
</NeoLoadSharedDashboardModal>
75+
<NeoNotificationModal></NeoNotificationModal>
7276
</div>
7377
);
7478
}
7579

7680
const mapStateToProps = state => ({
7781
connected: applicationIsConnected(state),
7882
connection: applicationGetConnection(state),
83+
shareDetails: applicationGetShareDetails(state),
7984
oldDashboard: applicationGetOldDashboard(state),
8085
connectionModalOpen: applicationHasConnectionModalOpen(state),
8186
aboutModalOpen: applicationHasAboutModalOpen(state),
@@ -106,9 +111,10 @@ const mapDispatchToProps = dispatch => ({
106111
dispatch(setOldDashboard(old));
107112
dispatch(setConnected(false));
108113
dispatch(clearNotification());
114+
dispatch(handleSharedDashboardsThunk());
109115
dispatch(setConnectionModalOpen(false));
110-
111116
},
117+
onResetShareDetails: _ => dispatch(resetShareDetails()),
112118
onConnectionModalOpen: _ => dispatch(setConnectionModalOpen(true)),
113119
onConnectionModalClose: _ => dispatch(setConnectionModalOpen(false)),
114120
onAboutModalOpen: _ => dispatch(setAboutModalOpen(true)),

src/application/ApplicationActions.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,16 @@ export const setOldDashboard = (text: string) => ({
5454
type: SET_OLD_DASHBOARD,
5555
payload: { text },
5656
});
57+
58+
// Legacy pre1-v2 dashboard that can be optionally upgraded.
59+
export const RESET_SHARE_DETAILS = 'APPLICATION/RESET_SHARE_DETAILS';
60+
export const resetShareDetails = () => ({
61+
type: RESET_SHARE_DETAILS,
62+
payload: { },
63+
});
64+
65+
export const SET_SHARE_DETAILS_FROM_URL = 'APPLICATION/SET_SHARE_DETAILS_FROM_URL';
66+
export const setShareDetailsFromUrl = (type: string, id: string, standalone: boolean, protocol: string, url: string, port: string, database: string, username: string, password: string) => ({
67+
type: SET_SHARE_DETAILS_FROM_URL,
68+
payload: { type, id, standalone, protocol, url, port, database, username, password },
69+
});

src/application/ApplicationReducer.tsx

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
* Reducers define changes to the application state when a given action
33
*/
44

5-
import { CLEAR_DESKTOP_CONNECTION_PROPERTIES, CLEAR_NOTIFICATION, CREATE_NOTIFICATION, SET_ABOUT_MODAL_OPEN, SET_CONNECTED,
6-
SET_CONNECTION_MODAL_OPEN, SET_CONNECTION_PROPERTIES, SET_DESKTOP_CONNECTION_PROPERTIES, SET_OLD_DASHBOARD } from "./ApplicationActions";
5+
import {
6+
CLEAR_DESKTOP_CONNECTION_PROPERTIES, CLEAR_NOTIFICATION, CREATE_NOTIFICATION, RESET_SHARE_DETAILS, SET_ABOUT_MODAL_OPEN, SET_CONNECTED,
7+
SET_CONNECTION_MODAL_OPEN, SET_CONNECTION_PROPERTIES, SET_DESKTOP_CONNECTION_PROPERTIES, SET_OLD_DASHBOARD, SET_SHARE_DETAILS_FROM_URL
8+
} from "./ApplicationActions";
79

810
const update = (state, mutations) =>
911
Object.assign({}, state, mutations)
@@ -22,13 +24,14 @@ const initialState =
2224
username: "neo4j",
2325
password: ""
2426
},
27+
shareDetails: undefined,
2528
desktopConnection: null,
2629
connected: false
2730
}
2831
export const applicationReducer = (state = initialState, action: { type: any; payload: any; }) => {
2932
const { type, payload } = action;
3033

31-
if (!action.type.startsWith('APPLICATION/')){
34+
if (!action.type.startsWith('APPLICATION/')) {
3235
return state;
3336
}
3437

@@ -87,6 +90,27 @@ export const applicationReducer = (state = initialState, action: { type: any; pa
8790
})
8891
return state;
8992
}
93+
case RESET_SHARE_DETAILS: {
94+
state = update(state, { shareDetails: undefined });
95+
return state;
96+
}
97+
case SET_SHARE_DETAILS_FROM_URL: {
98+
const { type, id, standalone, protocol, url, port, database, username, password } = payload;
99+
state = update(state, {
100+
shareDetails: {
101+
type: type,
102+
id: id,
103+
standalone: standalone,
104+
protocol: protocol,
105+
url: url,
106+
port: port,
107+
database: database,
108+
username: username,
109+
password: password
110+
}
111+
})
112+
return state;
113+
}
90114
default: {
91115
return state;
92116
}

src/application/ApplicationSelectors.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export const applicationGetConnection = (state: any) => {
2121
return state.application.connection;
2222
}
2323

24+
export const applicationGetShareDetails = (state: any) => {
25+
return state.application.shareDetails;
26+
}
27+
2428
export const applicationHasNeo4jDesktopConnection = (state: any) => {
2529
return state.application.desktopConnection != null;
2630
}
@@ -40,7 +44,7 @@ export const applicationHasAboutModalOpen = (state: any) => {
4044

4145
export const applicationHasCachedDashboard = (state: any) => {
4246
// Avoid this expensive check when the application is connected, as it's only for the welcome screen.
43-
if (state.application.connected){
47+
if (state.application.connected) {
4448
return false;
4549
}
4650
return !_.isEqual(state.dashboard, initialState);

src/application/ApplicationThunks.tsx

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createDriver } from "use-neo4j";
22
import { createNotificationThunk } from "../page/PageThunks";
33
import { QueryStatus, runCypherQuery } from "../report/CypherQueryRunner";
4-
import { setConnected, setConnectionModalOpen, setConnectionProperties, setDesktopConnectionProperties } from "./ApplicationActions";
4+
import { setConnected, setConnectionModalOpen, setConnectionProperties, setDesktopConnectionProperties, resetShareDetails, setShareDetailsFromUrl } from "./ApplicationActions";
55

66

77
export const createConnectionThunk = (protocol, url, port, database, username, password) => (dispatch: any, getState: any) => {
@@ -28,7 +28,7 @@ export const createConnectionThunk = (protocol, url, port, database, username, p
2828
}
2929

3030
export const createConnectionFromDesktopIntegrationThunk = () => (dispatch: any, getState: any) => {
31-
try{
31+
try {
3232
const desktopConnectionDetails = getState().application.desktopConnection;
3333
const protocol = desktopConnectionDetails.protocol;
3434
const url = desktopConnectionDetails.url;
@@ -37,7 +37,7 @@ export const createConnectionFromDesktopIntegrationThunk = () => (dispatch: any,
3737
const username = desktopConnectionDetails.username;
3838
const password = desktopConnectionDetails.password;
3939
dispatch(createConnectionThunk(protocol, url, port, database, username, password));
40-
}catch (e) {
40+
} catch (e) {
4141
dispatch(createNotificationThunk("Unable to establish connection to Neo4j Desktop", e));
4242
}
4343
}
@@ -58,7 +58,7 @@ export const setDatabaseFromNeo4jDesktopIntegrationThunk = () => (dispatch: any,
5858
}
5959

6060
let promise = window.neo4jDesktopApi && window.neo4jDesktopApi.getContext();
61-
61+
6262
if (promise) {
6363
promise.then(function (context) {
6464
let neo4j = getActiveDatabase(context);
@@ -73,4 +73,33 @@ export const setDatabaseFromNeo4jDesktopIntegrationThunk = () => (dispatch: any,
7373
}
7474
});
7575
}
76+
}
77+
78+
export const handleSharedDashboardsThunk = () => (dispatch: any, getState: any) => {
79+
try {
80+
dispatch(resetShareDetails());
81+
const queryString = window.location.search;
82+
const urlParams = new URLSearchParams(queryString);
83+
if (urlParams.get("share") !== null) {
84+
const id = decodeURIComponent(urlParams.get("id"));
85+
const type = urlParams.get("type");
86+
const standalone = urlParams.get("standalone") == "yes";
87+
if(urlParams.get("credentials")){
88+
const connection = decodeURIComponent(urlParams.get("credentials"));
89+
const protocol = connection.split("://")[0];
90+
const username = connection.split("://")[1].split(":")[0];
91+
const password = connection.split("://")[1].split(":")[1].split("@")[0];
92+
const database = connection.split("@")[1].split(":")[0];
93+
const url = connection.split("@")[1].split(":")[1];
94+
const port = connection.split("@")[1].split(":")[2];
95+
dispatch(setShareDetailsFromUrl(type, id, standalone, protocol, url, port, database, username, password));
96+
}else{
97+
dispatch(setShareDetailsFromUrl(type, id, undefined, undefined, undefined, undefined, undefined, undefined, undefined));
98+
}
99+
100+
}
101+
102+
} catch (e) {
103+
dispatch(createNotificationThunk("Unable to load shared dashboard", "You have specified an invalid/incomplete share URL. Try regenerating the URL from the sharing window."));
104+
}
76105
}

src/modal/LoadModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export const NeoLoadModal = ({ loadDashboard, loadDashboardFromNeo4j, loadDashbo
7171
{ field: 'author', headerName: 'Author', width: 160 },
7272
{
7373
field: 'load', headerName: ' ', renderCell: (c) => {
74-
return <Button onClick={(e) => { loadDashboardFromNeo4j(driver, c.id, handleDashboardLoadedFromNeo4j) }} style={{ float: "right" }} variant="contained" size="medium" endIcon={<PlayArrow />}>Select</Button>
74+
return <Button onClick={(e) => { loadDashboardFromNeo4j(driver, c.id, handleDashboardLoadedFromNeo4j) }} style={{ float: "right", backgroundColor: "white"}} variant="contained" size="medium" endIcon={<PlayArrow />}>Select</Button>
7575
}, width: 120
7676
},
7777
]
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import React, { useContext } from 'react';
2+
import Button from '@material-ui/core/Button';
3+
import Dialog from '@material-ui/core/Dialog';
4+
import DialogActions from '@material-ui/core/DialogActions';
5+
import DialogContent from '@material-ui/core/DialogContent';
6+
import DialogContentText from '@material-ui/core/DialogContentText';
7+
import DialogTitle from '@material-ui/core/DialogTitle';
8+
import IconButton from '@material-ui/core/IconButton';
9+
import Badge from '@material-ui/core/Badge';
10+
import PlayArrow from '@material-ui/icons/PlayArrow';
11+
import SaveIcon from '@material-ui/icons/Save';
12+
import { ListItem, ListItemIcon, ListItemText, TextareaAutosize, Tooltip } from '@material-ui/core';
13+
import CloseIcon from '@material-ui/icons/Close';
14+
import GetAppIcon from '@material-ui/icons/GetApp';
15+
import useMediaQuery from '@material-ui/core/useMediaQuery';
16+
import { useTheme, withStyles } from '@material-ui/core/styles';
17+
import { connect } from "react-redux";
18+
import DashboardIcon from '@material-ui/icons/Dashboard';
19+
/**
20+
* A modal to save a dashboard as a JSON text string.
21+
* The button to open the modal is intended to use in a drawer at the side of the page.
22+
*/
23+
24+
const styles = {
25+
26+
};
27+
28+
export const NeoLoadSharedDashboardModal = ({ shareDetails, onResetShareDetails }) => {
29+
30+
const handleClose = () => {
31+
onResetShareDetails();
32+
};
33+
34+
return (
35+
<div>
36+
<Dialog maxWidth={"lg"} open={shareDetails !== undefined} aria-labelledby="form-dialog-title">
37+
<DialogTitle id="form-dialog-title">
38+
<DashboardIcon style={{
39+
height: "30px",
40+
paddingTop: "4px",
41+
marginBottom: "-8px",
42+
marginRight: "5px",
43+
paddingBottom: "5px"
44+
}} /> Loading Dashboard
45+
46+
<IconButton onClick={handleClose} style={{ padding: "3px", float: "right" }}>
47+
<Badge badgeContent={""} >
48+
<CloseIcon />
49+
</Badge>
50+
</IconButton>
51+
</DialogTitle>
52+
<DialogContent>
53+
<DialogContentText>
54+
{/* You are loading a shared Neo4j dashboard: <b>My First Dashboard</b>.<br/> */}
55+
You are loading a Neo4j dashboard.<br />
56+
{shareDetails && shareDetails.url ? <>You will be connected to <b>{shareDetails && shareDetails.url}</b>.</> : <>You will still need to specify a connection manually.</>}
57+
<br /> <br />
58+
This will override your current dashboard (if any). Continue?
59+
<br/>
60+
61+
<br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
62+
<Button
63+
component="label"
64+
onClick={e => {
65+
66+
}}
67+
style={{ backgroundColor: "white", marginTop: "20px", float: "right" }}
68+
color="default"
69+
variant="contained"
70+
endIcon={<PlayArrow />}
71+
size="medium">
72+
Continue
73+
</Button>
74+
<Button
75+
component="label"
76+
onClick={handleClose}
77+
style={{ float: "right", marginTop: "20px", marginRight: "10px", backgroundColor: "white" }}
78+
color="default"
79+
variant="contained"
80+
size="medium">
81+
Cancel
82+
</Button>
83+
</DialogContentText>
84+
</DialogContent>
85+
<DialogActions>
86+
87+
</DialogActions>
88+
</Dialog>
89+
</div>
90+
);
91+
}
92+
93+
const mapStateToProps = state => ({
94+
});
95+
96+
const mapDispatchToProps = dispatch => ({
97+
});
98+
99+
100+
export default withStyles(styles)(connect(mapStateToProps, mapDispatchToProps)(NeoLoadSharedDashboardModal));
101+
102+
103+

src/modal/SaveModal.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,6 @@ const mapDispatchToProps = dispatch => ({
214214
},
215215
});
216216

217-
//
218217
export default withStyles(styles)(connect(mapStateToProps, mapDispatchToProps)(NeoSaveModal));
219218

220219

src/modal/ShareModal.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import ReportSetting from '../component/ReportSetting';
2121
import { loadDashboardListFromNeo4jThunk } from '../dashboard/DashboardThunks';
2222
import { applicationGetConnection } from '../application/ApplicationSelectors';
2323

24-
const shareBaseURL = "http://neodash.graphapp.io";
24+
const shareBaseURL = "http://localhost:3000";
25+
// const shareBaseURL = "http://neodash.graphapp.io";
2526
const styles = {
2627

2728
};
@@ -65,7 +66,7 @@ export const NeoShareModal = ({ connection, loadDashboardListFromNeo4j }) => {
6566
setShareName(c.row.title);
6667
setShareType("database");
6768
setLoadFromNeo4jModalOpen(false)
68-
}} style={{ float: "right" }} variant="contained" size="medium" endIcon={<PlayArrow />}>Select</Button>
69+
}} style={{ float: "right", backgroundColor: "white" }} variant="contained" size="medium" endIcon={<PlayArrow />}>Select</Button>
6970
}, width: 120
7071
},
7172
]
@@ -174,7 +175,7 @@ export const NeoShareModal = ({ connection, loadDashboardListFromNeo4j }) => {
174175
onClick={(e) => {
175176
setShareLink((shareBaseURL + "/?share&type=" + shareType + "&id=" + encodeURIComponent(shareID) +
176177
(shareConnectionDetails == "Yes" ? "&credentials=" + encodeURIComponent(connection.protocol + "://"
177-
+ connection.username + ":" + connection.password + "@" + connection.url + ":" + connection.port) : "")
178+
+ connection.username + ":" + connection.password + "@" + connection.database +":" + connection.url + ":" + connection.port) : "")
178179
+ (shareStandalone == "Yes" ? "&standalone=" + shareStandalone : "")).toLowerCase());
179180
}}
180181
style={{ marginBottom: "10px", backgroundColor: "white" }}

src/modal/WelcomeScreenModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export const NeoWelcomeScreenModal = ({ hasCachedDashboard, hasNeo4jDesktopConne
120120
<DialogActions style={{ background: "#555" }}>
121121
<DialogContent>
122122
<DialogContentText style={{ color: "lightgrey" }}>
123-
NeoDash is a tool for building ad-hoc Neo4j dashboards. Need help building an integrated solution? <a style={{ color: "white" }} href="mailto:[email protected]">Get in touch</a>!
123+
NeoDash is a tool for building standalone Neo4j dashboards. Need advice on building an integrated solution? <a style={{ color: "white" }} href="mailto:[email protected]">Get in touch</a>!
124124
</DialogContentText>
125125
</DialogContent>
126126
</DialogActions>

0 commit comments

Comments
 (0)