frontend-session & message-for-author
parent
423f79f04b
commit
432efcffe3
|
@ -20,7 +20,8 @@
|
||||||
"About": "About"
|
"About": "About"
|
||||||
},
|
},
|
||||||
"General": {
|
"General": {
|
||||||
"Close": "Close"
|
"Close": "Close",
|
||||||
|
"Send": "Send"
|
||||||
},
|
},
|
||||||
"Session": {
|
"Session": {
|
||||||
"Title": "Sessions",
|
"Title": "Sessions",
|
||||||
|
@ -59,6 +60,10 @@
|
||||||
"Domain": "Domain",
|
"Domain": "Domain",
|
||||||
"ActiveSession": "Active session",
|
"ActiveSession": "Active session",
|
||||||
"ActiveSessionSubtitle": "Expand to see forwards",
|
"ActiveSessionSubtitle": "Expand to see forwards",
|
||||||
"DDNSProvider": "Dynamic DNS Provider"
|
"DDNSProvider": "Dynamic DNS Provider",
|
||||||
|
"Actions": {
|
||||||
|
"SendMessageToAuthor": "Send message to author"
|
||||||
|
},
|
||||||
|
"MessageForAuthor": "Message for author"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
"About": "Despre"
|
"About": "Despre"
|
||||||
},
|
},
|
||||||
"General": {
|
"General": {
|
||||||
"Close": "Închide"
|
"Close": "Închide",
|
||||||
|
"Send": "Trimite"
|
||||||
},
|
},
|
||||||
"Session": {
|
"Session": {
|
||||||
"Title": "Sesiuni",
|
"Title": "Sesiuni",
|
||||||
|
@ -50,6 +51,10 @@
|
||||||
"Domain": "Domeniu",
|
"Domain": "Domeniu",
|
||||||
"ActiveSession": "Sesiune activă",
|
"ActiveSession": "Sesiune activă",
|
||||||
"ActiveSessionSubtitle": "Extindeţi pentru a vedea redirectările",
|
"ActiveSessionSubtitle": "Extindeţi pentru a vedea redirectările",
|
||||||
"DDNSProvider": "Furnizor DNS dinamic"
|
"DDNSProvider": "Furnizor DNS dinamic",
|
||||||
|
"Actions": {
|
||||||
|
"SendMessageToAuthor": "Trimite mesaj către autor"
|
||||||
|
},
|
||||||
|
"MessageForAuthor": "Mesaj pentru autor"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { Suspense } from "react";
|
import React, { Suspense, useEffect } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
import { Route, Switch } from "react-router-dom";
|
import { Route, Switch } from "react-router-dom";
|
||||||
import HomePage from "./home/HomePage";
|
import HomePage from "./home/HomePage";
|
||||||
import Header from "./layout/Header";
|
import Header from "./layout/Header";
|
||||||
|
@ -8,8 +9,15 @@ import "react-toastify/dist/ReactToastify.css";
|
||||||
import SessionContainer from "../features/session/components/SessionContainer";
|
import SessionContainer from "../features/session/components/SessionContainer";
|
||||||
import ReleaseNotesContainer from "../features/releaseNotes/components/ReleaseNotesContainer";
|
import ReleaseNotesContainer from "../features/releaseNotes/components/ReleaseNotesContainer";
|
||||||
import AboutContainer from "../features/about/components/AboutContainer";
|
import AboutContainer from "../features/about/components/AboutContainer";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { bindActionCreators } from "redux";
|
||||||
|
import { loadFrontendSession } from "../features/frontendSession/actionCreators";
|
||||||
|
|
||||||
|
function App({ actions }) {
|
||||||
|
useEffect(() => {
|
||||||
|
actions.loadFrontendSession();
|
||||||
|
}, []);
|
||||||
|
|
||||||
function App() {
|
|
||||||
const contentStyle = {
|
const contentStyle = {
|
||||||
paddingLeft: "30px",
|
paddingLeft: "30px",
|
||||||
paddingRight: "30px"
|
paddingRight: "30px"
|
||||||
|
@ -33,4 +41,18 @@ function App() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
App.propTypes = {
|
||||||
|
actions: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
function mapStateToProps() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch) {
|
||||||
|
return {
|
||||||
|
actions: bindActionCreators({ loadFrontendSession }, dispatch)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(App);
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import * as types from "./actionTypes";
|
||||||
|
import api from "./api";
|
||||||
|
import { sendHttpRequest } from "../../redux/actions/httpActions";
|
||||||
|
|
||||||
|
export function loadFrontendSession() {
|
||||||
|
return async function (dispatch) {
|
||||||
|
try {
|
||||||
|
dispatch({ type: types.INITIALIZE_FRONTEND_SESSION_STARTED });
|
||||||
|
const data = await dispatch(sendHttpRequest(api.getFrontendSession()));
|
||||||
|
dispatch({
|
||||||
|
type: types.INITIALIZE_FRONTEND_SESSION_SUCCESS,
|
||||||
|
payload: data
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
export const INITIALIZE_FRONTEND_SESSION_STARTED =
|
||||||
|
"INITIALIZE_FRONTEND_SESSION_STARTED";
|
||||||
|
|
||||||
|
export const INITIALIZE_FRONTEND_SESSION_SUCCESS =
|
||||||
|
"INITIALIZE_FRONTEND_SESSION_SUCCESS";
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { get } from "../../api/axiosApi";
|
||||||
|
const baseUrl = process.env.REVERSE_PROXY_API_URL;
|
||||||
|
|
||||||
|
const getFrontendSession = () => get(`${baseUrl}/system/frontend-session`);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getFrontendSession
|
||||||
|
};
|
|
@ -0,0 +1,26 @@
|
||||||
|
import * as types from "./actionTypes";
|
||||||
|
import initialState from "../../redux/reducers/initialState";
|
||||||
|
|
||||||
|
export default function frontendSessionReducer(
|
||||||
|
state = initialState.frontendSession,
|
||||||
|
action
|
||||||
|
) {
|
||||||
|
switch (action.type) {
|
||||||
|
case types.INITIALIZE_FRONTEND_SESSION_STARTED:
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
loaded: false
|
||||||
|
};
|
||||||
|
|
||||||
|
case types.INITIALIZE_FRONTEND_SESSION_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...action.payload,
|
||||||
|
loading: false,
|
||||||
|
loaded: true
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import * as types from "./actionTypes";
|
||||||
|
import api from "./api";
|
||||||
|
import { sendHttpRequest } from "../../redux/actions/httpActions";
|
||||||
|
|
||||||
|
export function saveMessageForAuthor(messageContent) {
|
||||||
|
return async function (dispatch, getState) {
|
||||||
|
try {
|
||||||
|
const sessionId = getState().frontendSession.sessionId;
|
||||||
|
const data = await dispatch(
|
||||||
|
sendHttpRequest(api.saveMessageForAuthor(sessionId, messageContent))
|
||||||
|
);
|
||||||
|
dispatch({ type: types.SAVE_MESSAGE_FOR_AUTHOR_SUCCESS, payload: data });
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const SAVE_MESSAGE_FOR_AUTHOR_SUCCESS =
|
||||||
|
"SAVE_MESSAGE_FOR_AUTHOR_SUCCESS";
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { post } from "../../api/axiosApi";
|
||||||
|
const baseUrl = process.env.REVERSE_PROXY_API_URL;
|
||||||
|
|
||||||
|
const saveMessageForAuthor = (sessionId, messageContent) =>
|
||||||
|
post(`${baseUrl}/system/message-for-author`, { sessionId, messageContent });
|
||||||
|
|
||||||
|
export default {
|
||||||
|
saveMessageForAuthor
|
||||||
|
};
|
|
@ -0,0 +1,52 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { bindActionCreators } from "redux";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import MessageForAuthorDialog from "./MessageForAuthorDialog";
|
||||||
|
import { saveMessageForAuthor } from "../actionCreators";
|
||||||
|
|
||||||
|
const MessageForAuthorContainer = ({ actions, open, handleClose }) => {
|
||||||
|
const [messageForAuthor, setMessageForAuthor] = useState("");
|
||||||
|
|
||||||
|
const onMessageForAuthorChanged = (event) => {
|
||||||
|
const value = event.target.value;
|
||||||
|
setMessageForAuthor(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveMessage = () => {
|
||||||
|
actions.saveMessageForAuthor(messageForAuthor);
|
||||||
|
setMessageForAuthor("");
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MessageForAuthorDialog
|
||||||
|
open={open}
|
||||||
|
handleClose={handleClose}
|
||||||
|
messageForAuthor={messageForAuthor}
|
||||||
|
onMessageForAuthorChanged={onMessageForAuthorChanged}
|
||||||
|
saveMessage={saveMessage}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
MessageForAuthorContainer.propTypes = {
|
||||||
|
actions: PropTypes.object.isRequired,
|
||||||
|
open: PropTypes.bool.isRequired,
|
||||||
|
handleClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
function mapStateToProps() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch) {
|
||||||
|
return {
|
||||||
|
actions: bindActionCreators({ saveMessageForAuthor }, dispatch)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(MessageForAuthorContainer);
|
|
@ -0,0 +1,69 @@
|
||||||
|
import React from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import TextField from "@material-ui/core/TextField";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
IconButton,
|
||||||
|
Grid,
|
||||||
|
Tooltip
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import SendRoundedIcon from "@material-ui/icons/SendRounded";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
const MessageForAuthorDialog = ({
|
||||||
|
open,
|
||||||
|
handleClose,
|
||||||
|
messageForAuthor,
|
||||||
|
onMessageForAuthorChanged,
|
||||||
|
saveMessage
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Dialog
|
||||||
|
fullWidth
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
aria-labelledby="form-dialog-title"
|
||||||
|
>
|
||||||
|
<DialogContent>
|
||||||
|
<Grid container>
|
||||||
|
<Grid item xs={11} sm={11} md={11}>
|
||||||
|
<TextField
|
||||||
|
autoFocus
|
||||||
|
id="message-for-author"
|
||||||
|
label={t("Server.MessageForAuthor")}
|
||||||
|
fullWidth
|
||||||
|
value={messageForAuthor}
|
||||||
|
onChange={onMessageForAuthorChanged}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={1} sm={1} md={1}>
|
||||||
|
<Tooltip title={t("General.Send")}>
|
||||||
|
<IconButton
|
||||||
|
aria-label="send message"
|
||||||
|
onClick={saveMessage}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<SendRoundedIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
MessageForAuthorDialog.propTypes = {
|
||||||
|
open: PropTypes.bool.isRequired,
|
||||||
|
handleClose: PropTypes.func.isRequired,
|
||||||
|
messageForAuthor: PropTypes.string.isRequired,
|
||||||
|
onMessageForAuthorChanged: PropTypes.func.isRequired,
|
||||||
|
saveMessage: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MessageForAuthorDialog;
|
|
@ -10,13 +10,14 @@ import {
|
||||||
Collapse,
|
Collapse,
|
||||||
Avatar,
|
Avatar,
|
||||||
IconButton,
|
IconButton,
|
||||||
Typography
|
Typography,
|
||||||
|
Tooltip
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import FavoriteIcon from "@material-ui/icons/Favorite";
|
|
||||||
import ShareIcon from "@material-ui/icons/Share";
|
import ShareIcon from "@material-ui/icons/Share";
|
||||||
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
|
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
|
||||||
import MoreVertIcon from "@material-ui/icons/MoreVert";
|
import MoreVertIcon from "@material-ui/icons/MoreVert";
|
||||||
import DnsRoundedIcon from "@material-ui/icons/DnsRounded";
|
import DnsRoundedIcon from "@material-ui/icons/DnsRounded";
|
||||||
|
import MessageRoundedIcon from "@material-ui/icons/MessageRounded";
|
||||||
import styles from "../../../components/common/styles/expandableCardStyles";
|
import styles from "../../../components/common/styles/expandableCardStyles";
|
||||||
import ServerSummary from "./ServerSummary";
|
import ServerSummary from "./ServerSummary";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
@ -27,7 +28,8 @@ const ServerComponent = ({
|
||||||
data,
|
data,
|
||||||
serverHost,
|
serverHost,
|
||||||
openAbout,
|
openAbout,
|
||||||
handleOpenInNewTab
|
handleOpenInNewTab,
|
||||||
|
showMessageForAuthor
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -65,9 +67,11 @@ const ServerComponent = ({
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardActions disableSpacing>
|
<CardActions disableSpacing>
|
||||||
<IconButton aria-label="add to favorites">
|
<Tooltip title={t("Server.Actions.SendMessageToAuthor")}>
|
||||||
<FavoriteIcon />
|
<IconButton aria-label="sent message" onClick={showMessageForAuthor}>
|
||||||
|
<MessageRoundedIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
<IconButton aria-label="share">
|
<IconButton aria-label="share">
|
||||||
<ShareIcon />
|
<ShareIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
@ -121,7 +125,8 @@ ServerComponent.propTypes = {
|
||||||
data: PropTypes.object.isRequired,
|
data: PropTypes.object.isRequired,
|
||||||
serverHost: PropTypes.string,
|
serverHost: PropTypes.string,
|
||||||
openAbout: PropTypes.func.isRequired,
|
openAbout: PropTypes.func.isRequired,
|
||||||
handleOpenInNewTab: PropTypes.func.isRequired
|
handleOpenInNewTab: PropTypes.func.isRequired,
|
||||||
|
showMessageForAuthor: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ServerComponent;
|
export default ServerComponent;
|
||||||
|
|
|
@ -1,12 +1,23 @@
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { bindActionCreators } from "redux";
|
import { bindActionCreators } from "redux";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { loadServerData, loadSystemVersion } from "../actionCreators";
|
import { loadServerData, loadSystemVersion } from "../actionCreators";
|
||||||
import ServerComponent from "./ServerComponent";
|
import ServerComponent from "./ServerComponent";
|
||||||
import { withRouter } from "react-router-dom";
|
import { withRouter } from "react-router-dom";
|
||||||
|
import MessageForAuthorContainer from "../../messageForAuthor/components/MessageForAuthorContainer";
|
||||||
|
|
||||||
const ServerContainer = ({ actions, data, serverHost, history }) => {
|
const ServerContainer = ({ actions, data, serverHost, history }) => {
|
||||||
|
const [openMessageForAuthor, setOpenMessageForAuthor] = useState(false);
|
||||||
|
|
||||||
|
const closeMessageForAuthor = () => {
|
||||||
|
setOpenMessageForAuthor(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const showMessageForAuthor = () => {
|
||||||
|
setOpenMessageForAuthor(true);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
actions.loadServerData();
|
actions.loadServerData();
|
||||||
actions.loadSystemVersion();
|
actions.loadSystemVersion();
|
||||||
|
@ -23,12 +34,19 @@ const ServerContainer = ({ actions, data, serverHost, history }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<ServerComponent
|
<ServerComponent
|
||||||
data={data}
|
data={data}
|
||||||
serverHost={serverHost}
|
serverHost={serverHost}
|
||||||
openAbout={openAbout}
|
openAbout={openAbout}
|
||||||
handleOpenInNewTab={handleOpenInNewTab}
|
handleOpenInNewTab={handleOpenInNewTab}
|
||||||
|
showMessageForAuthor={showMessageForAuthor}
|
||||||
/>
|
/>
|
||||||
|
<MessageForAuthorContainer
|
||||||
|
open={openMessageForAuthor}
|
||||||
|
handleClose={closeMessageForAuthor}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,10 @@ import {
|
||||||
forwardsReducer
|
forwardsReducer
|
||||||
} from "../../features/session/reducers";
|
} from "../../features/session/reducers";
|
||||||
import releaseNotesReducer from "../../features/releaseNotes/reducer";
|
import releaseNotesReducer from "../../features/releaseNotes/reducer";
|
||||||
|
import frontendSessionReducer from "../../features/frontendSession/reducer";
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
|
frontendSession: frontendSessionReducer,
|
||||||
server: serverReducer,
|
server: serverReducer,
|
||||||
sessions: sessionsReducer,
|
sessions: sessionsReducer,
|
||||||
forwards: forwardsReducer,
|
forwards: forwardsReducer,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
|
frontendSession: { loading: false, loaded: false },
|
||||||
server: {
|
server: {
|
||||||
data: { loading: false, loaded: false },
|
data: { loading: false, loaded: false },
|
||||||
activeSession: { loading: false, loaded: false }
|
activeSession: { loading: false, loaded: false }
|
||||||
|
|
Loading…
Reference in New Issue