Commit 8f668e77 authored by Florent Chehab's avatar Florent Chehab

feat(frontend): general improvements

* Final touch to truncated markdown
* A bit of reorganization and hookification
* Fixed tabbar position on university page
* Change mobile phone notification bar color
* WIP responsiveness in course feedback
* Shared parameters and HOC for pages / styles
parent 2e1e841b
Pipeline #40922 passed with stages
in 3 minutes and 34 seconds
...@@ -9,6 +9,9 @@ ...@@ -9,6 +9,9 @@
name="viewport" name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no" content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
/> />
<!-- Custom colors for phone status bar -->
<meta name="theme-color" content="#9c27b0">
<meta name="apple-mobile-web-app-status-bar-style" content="#9c27b0">
<!-- Favicon hell --> <!-- Favicon hell -->
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="{% static '/base_app/favicon/apple-touch-icon-57x57.png' %}" /> <link rel="apple-touch-icon-precomposed" sizes="57x57" href="{% static '/base_app/favicon/apple-touch-icon-57x57.png' %}" />
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="{% static '/base_app/favicon/apple-touch-icon-114x114.png' %}" /> <link rel="apple-touch-icon-precomposed" sizes="114x114" href="{% static '/base_app/favicon/apple-touch-icon-114x114.png' %}" />
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
*/ */
import React from "react"; import React from "react";
import PropTypes from "prop-types";
import {withStyles} from "@material-ui/styles";
import FullScreenDialog from "./FullScreenDialog"; import FullScreenDialog from "./FullScreenDialog";
import {connect} from "react-redux"; import {connect} from "react-redux";
...@@ -33,12 +31,11 @@ import PageAboutConditions from "../pages/PageAboutConditions"; ...@@ -33,12 +31,11 @@ import PageAboutConditions from "../pages/PageAboutConditions";
*/ */
class App extends CustomComponentForAPI { class App extends CustomComponentForAPI {
customRender() { customRender() {
const {classes} = this.props;
return ( return (
<> <>
<AppFrame> <AppFrame>
<FullScreenDialog/> <FullScreenDialog/>
<main className={classes.content}> <main>
<Route exact path={APP_ROUTES.base} component={PageHome}/> <Route exact path={APP_ROUTES.base} component={PageHome}/>
<Route path={APP_ROUTES.search} component={PageSearch}/> <Route path={APP_ROUTES.search} component={PageSearch}/>
<Route path={APP_ROUTES.map} component={PageMap}/> <Route path={APP_ROUTES.map} component={PageMap}/>
...@@ -55,9 +52,7 @@ class App extends CustomComponentForAPI { ...@@ -55,9 +52,7 @@ class App extends CustomComponentForAPI {
} }
} }
App.propTypes = { App.propTypes = {};
classes: PropTypes.object.isRequired,
};
// Already load some of the data even if it's not use here. // Already load some of the data even if it's not use here.
// /!\ Don't delete it // /!\ Don't delete it
...@@ -81,20 +76,7 @@ const mapDispatchToProps = (dispatch) => { ...@@ -81,20 +76,7 @@ const mapDispatchToProps = (dispatch) => {
}; };
}; };
const styles = theme => ({
content: {
[theme.breakpoints.up("md")]: {
padding: theme.spacing(3),
},
[theme.breakpoints.down("sm")]: {
padding: 0,
},
},
});
export default compose( export default compose(
connect(mapStateToProps, mapDispatchToProps), connect(mapStateToProps, mapDispatchToProps),
withStyles(styles, {withTheme: true}),
withErrorBoundary(), withErrorBoundary(),
)(App); )(App);
...@@ -18,6 +18,7 @@ import DrawerMenu from "./DrawerMenu"; ...@@ -18,6 +18,7 @@ import DrawerMenu from "./DrawerMenu";
import {APP_ROUTES} from "../../config/appRoutes"; import {APP_ROUTES} from "../../config/appRoutes";
import CustomNavLink from "../common/CustomNavLink"; import CustomNavLink from "../common/CustomNavLink";
import {classNames} from "../../utils/classNames"; import {classNames} from "../../utils/classNames";
import {appBarHeight, siteMaxWidth} from "../../config/sharedStyles";
const styles = theme => ({ const styles = theme => ({
root: { root: {
...@@ -25,14 +26,15 @@ const styles = theme => ({ ...@@ -25,14 +26,15 @@ const styles = theme => ({
}, },
appBar: { appBar: {
width: "100%", width: "100%",
height: appBarHeight(theme),
}, },
toolBar: { toolBar: {
width: "100%", width: "100%",
maxWidth: 1920, maxWidth: siteMaxWidth(),
display: "flex", display: "flex",
}, },
content: { content: {
maxWidth: 1920, maxWidth: siteMaxWidth(),
}, },
siteName: { siteName: {
fontWeight: 900, fontWeight: 900,
......
...@@ -222,12 +222,16 @@ class Markdown extends Component { ...@@ -222,12 +222,16 @@ class Markdown extends Component {
render() { render() {
const compiledSource = this.compileSource(this.props.source); const compiledSource = this.compileSource(this.props.source);
return <ReactMarkdown return (
<div style={{wordBreak: "break-word"}}>
<ReactMarkdown
renderers={renderers} renderers={renderers}
allowedTypes={[...Object.keys(renderers), "text", "emphasis", "root", "strong"]} // Only allow custom nodes and basic ones allowedTypes={[...Object.keys(renderers), "text", "emphasis", "root", "strong"]} // Only allow custom nodes and basic ones
mode={"escape"} mode={"escape"}
source={compiledSource} source={compiledSource}
/>; />
</div>
);
} }
} }
......
...@@ -2,7 +2,6 @@ import React from "react"; ...@@ -2,7 +2,6 @@ import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import MuiThemeProvider from "@material-ui/core/styles/MuiThemeProvider"; import MuiThemeProvider from "@material-ui/core/styles/MuiThemeProvider";
import createMuiTheme from "@material-ui/core/styles/createMuiTheme";
import {connect} from "react-redux"; import {connect} from "react-redux";
import {BrowserRouter as Router} from "react-router-dom"; import {BrowserRouter as Router} from "react-router-dom";
import CustomComponentForAPI from "./CustomComponentForAPI"; import CustomComponentForAPI from "./CustomComponentForAPI";
...@@ -10,7 +9,8 @@ import "typeface-roboto"; ...@@ -10,7 +9,8 @@ import "typeface-roboto";
import getActions from "../../redux/api/getActions"; import getActions from "../../redux/api/getActions";
import {RequestParams} from "../../redux/api/RequestParams"; import {RequestParams} from "../../redux/api/RequestParams";
import {responsiveFontSizes} from "@material-ui/core/styles"; import {responsiveFontSizes, rgbToHex} from "@material-ui/core/styles";
import {createMuiTheme} from "@material-ui/core";
const siteSettings = { const siteSettings = {
typography: { typography: {
...@@ -22,6 +22,26 @@ const siteSettings = { ...@@ -22,6 +22,26 @@ const siteSettings = {
} }
}; };
/**
* method to set the correct meta tags on the HTML page so that
* the status bar on phone is of a matching color as the theme.
*
* @param mainColor
*/
function updatePhoneStatusBarColor(mainColor) {
let color = rgbToHex(mainColor);
try {
for (let el of document.getElementsByTagName("meta")) {
if (el.name === "theme-color" || el.name === "apple-mobile-web-app-status-bar-style") {
el.content = color;
}
}
} catch (e) {
// nothing, yes.
}
}
/** /**
* Method to generate a full site theme based on an object themeData * Method to generate a full site theme based on an object themeData
* @param themeData Should be an object like: src/config/defaultTheme.json * @param themeData Should be an object like: src/config/defaultTheme.json
...@@ -51,10 +71,13 @@ export function getTheme(themeData) { ...@@ -51,10 +71,13 @@ export function getTheme(themeData) {
class ThemeProvider extends CustomComponentForAPI { class ThemeProvider extends CustomComponentForAPI {
customRender() { customRender() {
const {userData} = this.getAllLatestReadData("userData"), const {userData} = this.getAllLatestReadData("userData"),
themeData = userData.theme; themeData = userData.theme,
theme = getTheme(themeData);
updatePhoneStatusBarColor(theme.palette.primary.main);
return ( return (
<MuiThemeProvider theme={getTheme(themeData)}> <MuiThemeProvider theme={theme}>
<Router> <Router>
{this.props.children} {this.props.children}
</Router> </Router>
......
...@@ -4,20 +4,23 @@ import PropTypes from "prop-types"; ...@@ -4,20 +4,23 @@ import PropTypes from "prop-types";
import {compose} from "recompose"; import {compose} from "recompose";
import withStyles from "@material-ui/core/styles/withStyles"; import withStyles from "@material-ui/core/styles/withStyles";
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import Collapse from "@material-ui/core/Collapse";
function TruncatedMarkdown(props) { function TruncatedMarkdown(props) {
const [truncated, setTruncated] = useState(false), const [truncated, setTruncated] = useState(false),
{comment, truncateFromLength, classes} = props, {comment, truncateFromLength, classes} = props,
truncatable = comment.length > truncateFromLength; truncatable = comment.length > truncateFromLength;
if (!truncatable) {
return <Markdown source={comment}/>;
} else {
return ( return (
<div>
<div className={truncatable && !truncated ? classes.truncated : ""}>
<Markdown source={comment}/>
</div>
{
truncatable ?
<> <>
<Collapse in={truncated} collapsedHeight={"4rem"}>
<Markdown source={comment}/>
</Collapse>
{!truncated ? <div className={classes.gradientBorder}/> : <></>} {!truncated ? <div className={classes.gradientBorder}/> : <></>}
<Button variant={"contained"} <Button variant={"contained"}
className={classes.moreButton} className={classes.moreButton}
...@@ -25,11 +28,8 @@ function TruncatedMarkdown(props) { ...@@ -25,11 +28,8 @@ function TruncatedMarkdown(props) {
{!truncated ? "En lire plus..." : "En lire moins..."} {!truncated ? "En lire plus..." : "En lire moins..."}
</Button> </Button>
</> </>
:
<></>
}
</div>
); );
}
} }
TruncatedMarkdown.propTypes = { TruncatedMarkdown.propTypes = {
...@@ -49,7 +49,7 @@ const styles = theme => ({ ...@@ -49,7 +49,7 @@ const styles = theme => ({
}, },
gradientBorder: { gradientBorder: {
position: "relative", position: "relative",
marginTop: "-3rem", marginTop: "-2rem",
bottom: 0, bottom: 0,
width: "100%", width: "100%",
height: "3rem", height: "3rem",
......
import {useTheme} from "@material-ui/styles";
import useMediaQuery from "@material-ui/core/useMediaQuery";
// Small hack to keep track of the last width there seem to be bug sometimes
let lastWidth = "xs";
export function useWindowWidth() {
const theme = useTheme();
lastWidth = [...theme.breakpoints.keys].reverse().reduce((output, key) => {
const matches = useMediaQuery(theme.breakpoints.only(key));
return !output && matches ? key : output;
}, null) || lastWidth;
return lastWidth;
}
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import Paper from "@material-ui/core/Paper";
import Markdown from "../common/Markdown"; import Markdown from "../common/Markdown";
import {compose} from "recompose"; import {compose} from "recompose";
import {withErrorBoundary} from "../common/ErrorBoundary"; import {withErrorBoundary} from "../common/ErrorBoundary";
import {withPaddedPaper} from "./shared";
const source = ` const source = `
...@@ -19,33 +18,23 @@ const source = ` ...@@ -19,33 +18,23 @@ const source = `
/** /**
* Component corresponding to page about use conditions. * Component corresponding to page about use conditions.
*
* @class PageAboutConditions
* @extends {React.Component}
*/ */
class PageAboutConditions extends React.Component { function PageAboutConditions() {
render() {
const {theme} = this.props;
return ( return (
<Paper style={theme.myPaper}> <>
<Typography variant="h3"> <Typography variant="h3">
Le projet <em><b>REX-DRI</b></em> Le projet <em><b>REX-DRI</b></em>
</Typography> </Typography>
<Markdown source={source}/> <Markdown source={source}/>
</Paper> </>
); );
}
} }
PageAboutConditions.propTypes = { PageAboutConditions.propTypes = {
theme: PropTypes.object.isRequired, theme: PropTypes.object.isRequired,
}; };
const styles = {};
export default compose( export default compose(
withStyles(styles, {withTheme: true}), withPaddedPaper(),
withErrorBoundary() withErrorBoundary()
)(PageAboutConditions); )(PageAboutConditions);
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import Paper from "@material-ui/core/Paper";
import Markdown from "../common/Markdown"; import Markdown from "../common/Markdown";
import {compose} from "recompose"; import {compose} from "recompose";
import {withErrorBoundary} from "../common/ErrorBoundary"; import {withErrorBoundary} from "../common/ErrorBoundary";
import {withPaddedPaper} from "./shared";
const source = ` const source = `
...@@ -17,28 +16,18 @@ Rendez vous sur [le GitLab de l'UTC](https://gitlab.utc.fr/rex-dri/rex-dri) ! ...@@ -17,28 +16,18 @@ Rendez vous sur [le GitLab de l'UTC](https://gitlab.utc.fr/rex-dri/rex-dri) !
`; `;
/** /**
* Component corresponding to page about the project. * Component corresponding to page about the project.
*
* @class PageAboutProject
* @extends {React.Component}
*/ */
class PageAboutProject extends React.Component { function PageAboutProject() {
render() {
const { theme } = this.props;
return ( return (
<Paper style={theme.myPaper}> <>
<Typography variant="h3"> <Typography variant="h3">
Le projet <em><b>REX-DRI</b></em> Le projet <em><b>REX-DRI</b></em>
</Typography> </Typography>
<Markdown source={source} /> <Markdown source={source}/>
</Paper> </>
); );
}
} }
PageAboutProject.propTypes = { PageAboutProject.propTypes = {
...@@ -46,9 +35,7 @@ PageAboutProject.propTypes = { ...@@ -46,9 +35,7 @@ PageAboutProject.propTypes = {
}; };
const styles = {};
export default compose( export default compose(
withStyles(styles, { withTheme: true }), withPaddedPaper(),
withErrorBoundary() withErrorBoundary()
)(PageAboutProject); )(PageAboutProject);
import React from "react"; import React from "react";
import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import Paper from "@material-ui/core/Paper";
import {compose} from "recompose"; import {compose} from "recompose";
import {withErrorBoundary} from "../common/ErrorBoundary"; import {withErrorBoundary} from "../common/ErrorBoundary";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import Markdown from "../common/Markdown"; import Markdown from "../common/Markdown";
import {withPaddedPaper} from "./shared";
const source = ` const source = `
...@@ -62,34 +60,21 @@ You can format money: \`:100LSD:\` => :100LSD: ...@@ -62,34 +60,21 @@ You can format money: \`:100LSD:\` => :100LSD:
/** /**
* Component corresponding to the landing page of the site * Component corresponding to the landing page of the site
*
* @class PageHome
* @extends {React.Component}
*/ */
class PageHome extends React.Component { function PageHome() {
render() {
const {theme} = this.props;
return ( return (
<Paper style={theme.myPaper}> <>
<Typography variant="h3"> <Typography variant="h3">
Bienvenue sur <em><b>REX-DRI</b></em> ! Bienvenue sur <em><b>REX-DRI</b></em> !
</Typography> </Typography>
<Markdown source={source}/> <Markdown source={source}/>
</Paper> </>
); );
}
} }
PageHome.propTypes = { PageHome.propTypes = {};
theme: PropTypes.object.isRequired,
};
const styles = {};
export default compose( export default compose(
withStyles(styles, {withTheme: true}), withPaddedPaper(),
withErrorBoundary() withErrorBoundary()
)(PageHome); )(PageHome);
...@@ -7,6 +7,8 @@ import ViewList from "../recommendation/ViewListSubPage"; ...@@ -7,6 +7,8 @@ import ViewList from "../recommendation/ViewListSubPage";
import {makeStyles} from "@material-ui/styles"; import {makeStyles} from "@material-ui/styles";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import {withPaddedPaper} from "./shared";
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
paper: theme.myPaper, paper: theme.myPaper,
})); }));
...@@ -37,5 +39,6 @@ PageLists.propTypes = { ...@@ -37,5 +39,6 @@ PageLists.propTypes = {
}; };
export default compose( export default compose(
withPaddedPaper(),
withErrorBoundary(), withErrorBoundary(),
)(PageLists); )(PageLists);
...@@ -5,24 +5,23 @@ import {compose} from "recompose"; ...@@ -5,24 +5,23 @@ import {compose} from "recompose";
import {withErrorBoundary} from "../common/ErrorBoundary"; import {withErrorBoundary} from "../common/ErrorBoundary";
import Filter from "../filter/Filter"; import Filter from "../filter/Filter";
import MainMap from "../map/MainMap"; import MainMap from "../map/MainMap";
import Paper from "@material-ui/core/Paper"; import {withPaddedPaper} from "./shared";
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
filter: { filter: {
marginBottom: theme.spacing(2), marginBottom: theme.spacing(2),
}, },
paper: theme.myPaper,
})); }));
/** /**
* Component corresponding to the page with the map of the universities * Component corresponding to the page with the map of the universities
*/ */
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
function PageMap(props) { function PageMap() {
const classes = useStyles(); const classes = useStyles();
return ( return (
<Paper className={classes.paper}> <>
<Typography variant="h4" gutterBottom> <Typography variant="h4" gutterBottom>
Exploration Cartographique Exploration Cartographique
</Typography> </Typography>
...@@ -30,10 +29,11 @@ function PageMap(props) { ...@@ -30,10 +29,11 @@ function PageMap(props) {
<Filter/> <Filter/>
</div> </div>
<MainMap/> <MainMap/>
</Paper> </>
); );
} }
export default compose( export default compose(
withPaddedPaper(),
withErrorBoundary() withErrorBoundary()
)(PageMap); )(PageMap);
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import Search from "../search/Search"; import Search from "../search/Search";
import Paper from "@material-ui/core/Paper";
import {withErrorBoundary} from "../common/ErrorBoundary"; import {withErrorBoundary} from "../common/ErrorBoundary";
import {compose} from "recompose"; import {compose} from "recompose";
import Filter from "../filter/Filter"; import Filter from "../filter/Filter";
import {withPaddedPaper} from "./shared";
/** /**
* Component corresponding to the page with the search university capabilities * Component corresponding to the page with the search university capabilities
*
* @class PageSearch
* @extends {React.Component}
*/ */
class PageSearch extends React.Component { function PageSearch() {
state = {
selectedOption: null,
};
render() {
const {theme} = this.props;
return ( return (
<Paper style={theme.myPaper}> <>
<Typography variant="h4" gutterBottom> <Typography variant="h4" gutterBottom>
Recherche d'une université Recherche d'une université
</Typography> </Typography>
<Filter/> <Filter/>
<Search/> <Search/>
</Paper> </>
); );
}
} }
PageSearch.propTypes = { PageSearch.propTypes = {
theme: PropTypes.object.isRequired, theme: PropTypes.object.isRequired,
}; };