...
 
Commits (2)
......@@ -2,4 +2,3 @@ Things to do
============
- Add a Snackbar when a button is clicked (https://material-ui.com/demos/snackbars/)
- Store the state in the URL fragment or in the localStorage (and add a button to reset)
......@@ -9596,6 +9596,11 @@
}
}
},
"redux-persist": {
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-5.10.0.tgz",
"integrity": "sha512-sSJAzNq7zka3qVHKce1hbvqf0Vf5DuTVm7dr4GtsqQVOexnrvbV47RWFiPxQ8fscnyiuWyD2O92DOxPl0tGCRg=="
},
"redux-thunk": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
......
......@@ -11,6 +11,7 @@
"react-redux": "^5.0.7",
"react-scripts": "1.1.4",
"redux": "^4.0.0",
"redux-persist": "^5.10.0",
"redux-thunk": "^2.3.0"
},
"homepage": "http://www.mentalktoomuch.info/",
......
......@@ -4,6 +4,7 @@ export const ALREADY_TALKED = "ALREADY_TALKED";
export const IS_ORGANIZER = "IS_ORGANIZER";
export const TICK = "TICK";
export const CHANGE_TIMER_LIMIT = "CHANGE_TIMER_LIMIT";
export const RESET = "RESET";
function currentSpeech(state) {
const current = state.current;
......@@ -85,3 +86,9 @@ export function changeTimerLimit(value) {
warnTime: value - Math.round(value / 5)
};
}
export function reset() {
return {
type: RESET
};
}
import React, { Component } from "react";
import { withStyles } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";
import Icon from "@material-ui/core/Icon";
import grey from "@material-ui/core/colors/grey";
import { defineMessages, FormattedMessage } from "react-intl";
......@@ -29,6 +30,9 @@ class AlreadyTalkedButton extends Component {
className={classes.button}
disabled={this.props.disabled}
>
<Icon fontSize="inherit" style={{ marginRight: "0.3em" }}>
cached
</Icon>
<FormattedMessage {...messages.text} />
</Button>
);
......
import React, { Component } from "react";
import IconButton from "@material-ui/core/IconButton";
import Button from "@material-ui/core/Button";
import Icon from "@material-ui/core/Icon";
import Tooltip from "@material-ui/core/Tooltip";
import { injectIntl, defineMessages } from "react-intl";
import { defineMessages, FormattedMessage } from "react-intl";
const messages = defineMessages({
save: {
id: "exportbutton.save",
defaultMessage: "Save"
export: {
id: "exportbutton.export",
defaultMessage: "Export"
},
timestamp: {
id: "exportbutton.timestamp",
......@@ -37,7 +36,7 @@ const messages = defineMessages({
class ExportButton extends Component {
makeData = () => {
const toCSVLine = a => a.map(v => '"' + v + '"').join(",") + ";\n";
const toCSVLine = a => a.map(v => '"' + v + '"').join(",") + "\n";
let data = [
toCSVLine([
this.props.intl.formatMessage(messages.timestamp),
......@@ -78,16 +77,14 @@ class ExportButton extends Component {
render() {
return (
<Tooltip
title={this.props.intl.formatMessage(messages.save)}
placement="left"
>
<IconButton color="default" onClick={this.onClick}>
<Icon>save_alt</Icon>
</IconButton>
</Tooltip>
<Button color="default" size="small" onClick={this.onClick}>
<Icon fontSize="inherit" style={{ marginRight: "0.3em" }}>
open_in_new
</Icon>
<FormattedMessage {...messages.export} />
</Button>
);
}
}
export default injectIntl(ExportButton);
export default ExportButton;
import React, { Component } from "react";
import { withStyles } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";
import Icon from "@material-ui/core/Icon";
import grey from "@material-ui/core/colors/grey";
import { defineMessages, FormattedMessage } from "react-intl";
......@@ -29,6 +30,9 @@ class IsOrganizerButton extends Component {
className={classes.button}
disabled={this.props.disabled}
>
<Icon fontSize="inherit" style={{ marginRight: "0.3em" }}>
verified_user
</Icon>
<FormattedMessage {...messages.text} />
</Button>
);
......
import React, { Component } from "react";
import Button from "@material-ui/core/Button";
import Icon from "@material-ui/core/Icon";
import { defineMessages, FormattedMessage } from "react-intl";
const messages = defineMessages({
text: {
id: "resetbutton.text",
defaultMessage: "Reset"
}
});
class ResetButton extends Component {
render() {
return (
<Button color="secondary" size="small" onClick={this.props.onClick}>
<Icon fontSize="inherit" style={{ marginRight: "0.3em" }}>
cancel
</Icon>
<FormattedMessage {...messages.text} />
</Button>
);
}
}
export default ResetButton;
import React, { Component } from "react";
import { withStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import Grid from "@material-ui/core/Grid";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
......@@ -8,11 +9,15 @@ import ListItemText from "@material-ui/core/ListItemText";
import Icon from "@material-ui/core/Icon";
import { injectIntl, defineMessages, FormattedMessage } from "react-intl";
import Duration from "./Duration";
import ResetButton from "../containers/ConnectedResetButton";
import ExportButton from "../containers/ConnectedExportButton";
const styles = {
root: {
marginTop: "2em"
},
buttons: {
marginTop: "0.5em"
}
};
......@@ -50,9 +55,31 @@ const messages = defineMessages({
class Stats extends Component {
render() {
const { classes } = this.props;
const totalSpeakers =
this.props.stats.man.speakers + this.props.stats.other.speakers;
return (
<div className={classes.root}>
<StatsValues {...this.props} />
{totalSpeakers > 0 && (
<Grid
container
justify="center"
spacing={16}
className={classes.buttons}
>
<Grid item xs={6}>
<Typography align="left">
<ExportButton />
</Typography>
</Grid>
<Grid item xs={6}>
<Typography align="right">
<ResetButton />
</Typography>
</Grid>
</Grid>
)}
</div>
);
}
......@@ -168,10 +195,6 @@ class StatsValues extends Component {
/>
</ListItem>
</List>
<Typography align="right">
<ExportButton />
</Typography>
</div>
);
}
......
......@@ -20,7 +20,7 @@ const styles = theme => ({
border: "1px solid black"
},
buttonField: {
marginTop: "1em"
marginTop: "1.5em"
},
title: {
marginBottom: "1em"
......
import { connect } from "react-redux";
import { reset } from "../actions";
import ResetButton from "../components/ResetButton";
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: () => dispatch(reset())
};
};
const ConnectedResetButton = connect(
null,
mapDispatchToProps
)(ResetButton);
export default ConnectedResetButton;
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import configureStore from "./store";
import App from "./components/App";
//import registerServiceWorker from './registerServiceWorker';
......@@ -34,13 +35,15 @@ const messages =
localeData[language] ||
localeData.en;
const store = configureStore();
const { store, persistor } = configureStore();
ReactDOM.render(
<Provider store={store}>
<IntlProvider locale={navigator.language} messages={messages}>
<App />
</IntlProvider>
<PersistGate loading={null} persistor={persistor}>
<IntlProvider locale={navigator.language} messages={messages}>
<App />
</IntlProvider>
</PersistGate>
</Provider>,
document.getElementById("root")
);
......
......@@ -6,15 +6,16 @@
"exportbutton.alreadyTalked": "A déjà parlé",
"exportbutton.category": "Categorie",
"exportbutton.duration": "Durée",
"exportbutton.export": "Exporter",
"exportbutton.isOrganizer": "Organisateur",
"exportbutton.overtime": "A dépassé",
"exportbutton.save": "Sauvegarder",
"exportbutton.timestamp": "Horodatage",
"footer.text1": "Très largement inspiré de",
"footer.text2": "En fait, c'est pareil mais avec quelques stats en plus.",
"footer.viewCode": "Voir le code",
"header.title": "Les hommes parlent trop, prouvez-le.",
"isorganizerbutton.text": "Organisateur/trice",
"resetbutton.text": "Réinitialiser",
"stats.forOthers": "pour les autres :",
"stats.header": "Les mecs ont parlé :",
"stats.nobody": "Personne n'a encore parlé.",
......
......@@ -5,7 +5,8 @@ import {
ALREADY_TALKED,
IS_ORGANIZER,
TICK,
CHANGE_TIMER_LIMIT
CHANGE_TIMER_LIMIT,
RESET
} from "./actions";
const MAX_TIME = 2 * 60 + 30; // 2:30
......@@ -32,6 +33,8 @@ function limits(state = initialState.limits, action) {
maxTime: action.maxTime,
warnTime: action.warnTime
};
case RESET:
return initialState.limits;
default:
return state;
}
......@@ -68,6 +71,8 @@ function current(state = initialState.current, action) {
...state,
isOrganizer: true
};
case RESET:
return initialState.current;
default:
return state;
}
......@@ -81,6 +86,8 @@ function speeches(state = initialState.speeches, action) {
return state;
}
return [...state, action.speech];
case RESET:
return initialState.speeches;
default:
return state;
}
......
import thunkMiddleware from "redux-thunk";
import { createStore, applyMiddleware } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import rootReducer from "./reducers";
const persistConfig = {
key: "root",
storage,
blacklist: ["current"]
};
export default function configureStore() {
return createStore(
rootReducer,
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(
persistedReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__(),
applyMiddleware(thunkMiddleware)
);
let persistor = persistStore(store);
return { store, persistor };
}