feat: Prevent users from starting levels that they did not reach yet

This commit is contained in:
Alexander Polynomdivision 2018-10-02 17:52:07 +02:00
parent cee5680a6a
commit 5361d1bab5
4 changed files with 109 additions and 62 deletions

View File

@ -192,3 +192,11 @@ export function setDashboardLoading(state: boolean) {
state, state,
}; };
}; };
export const LEVELLIST_SET_SNACKBAR = "LEVELLIST_SET_SNACKBAR";
export function setLevelListSnackbar(state: boolean) {
return {
type: LEVELLIST_SET_SNACKBAR,
state,
};
};

View File

@ -1,6 +1,8 @@
import { connect } from "react-redux"; import { connect } from "react-redux";
import { setLevelListLoading, setLevels } from "../actions"; import {
setLevelListLoading, setLevels, setLevelListSnackbar
} from "../actions";
import { ILevel } from "../models/level"; import { ILevel } from "../models/level";
@ -11,12 +13,14 @@ const mapStateToProps = state => {
levels: state.levels, levels: state.levels,
loading: state.levelList.loading, loading: state.levelList.loading,
user: state.user, user: state.user,
snackbar: state.levelList.snackbar,
}; };
}; };
const mapDispatchToProps = dispatch => { const mapDispatchToProps = dispatch => {
return { return {
setLoading: (state: boolean) => dispatch(setLevelListLoading(state)), setLoading: (state: boolean) => dispatch(setLevelListLoading(state)),
setLevels: (levels: ILevel[]) => dispatch(setLevels(levels)), setLevels: (levels: ILevel[]) => dispatch(setLevels(levels)),
setSnackbar: (state: boolean) => dispatch(setLevelListSnackbar(state)),
}; };
}; };

View File

@ -7,9 +7,11 @@ import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions'; import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent'; import CardContent from '@material-ui/core/CardContent';
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
import Snackbar from "@material-ui/core/Snackbar";
import CircularProgress from "@material-ui/core/CircularProgress"; import CircularProgress from "@material-ui/core/CircularProgress";
import { Link } from "react-router-dom"; import { withRouter } from "react-router-dom";
import { ILevel } from "../models/level"; import { ILevel } from "../models/level";
import { IUser } from "../models/user"; import { IUser } from "../models/user";
@ -17,77 +19,102 @@ import { IUser } from "../models/user";
interface IProps { interface IProps {
getLevels: () => Promise<ILevel[]>; getLevels: () => Promise<ILevel[]>;
history: any;
user: IUser; user: IUser;
setLevels: (levels: ILevel[]) => void; setLevels: (levels: ILevel[]) => void;
setLoading: (state: boolean) => void; setLoading: (state: boolean) => void;
loading: boolean; loading: boolean;
snackbar: boolean;
setSnackbar: (state: boolean) => void;
levels: ILevel[]; levels: ILevel[];
} }
export default class Dashboard extends React.Component<IProps> { const LevelListWithRouter = withRouter(
componentDidMount() { class Dashboard extends React.Component<IProps> {
this.props.setLoading(true); componentDidMount() {
this.props.setLoading(true);
// Fetch the levels // Fetch the levels
this.props.getLevels().then(res => { this.props.getLevels().then(res => {
this.props.setLevels(res); this.props.setLevels(res);
this.props.setLoading(false); this.props.setLoading(false);
}); });
}
render() {
if (this.props.loading) {
return <div>
<Grid
container
spacing={0}
direction="column"
alignItems="center"
justify="center"
style={{ minHeight: '100vh' }}>
<Grid item xs={12}>
<Paper className="paper">
<Grid container direction="column" spacing={8}>
<CircularProgress />
</Grid>
</Paper>
</Grid>
</Grid>
</div>;
} }
const small = window.matchMedia("(max-width: 700px)").matches; toLevel(id: number) {
const cName = small ? "lesson-card-xs" : "lesson-card-lg"; const maxLevel = Math.max(...this.props.user.levels);
if (maxLevel + 1 >= id) {
this.props.history.push(`/level/${id}`);
} else {
this.props.setSnackbar(true);
}
}
let key = 0; render() {
const levelToCard = (level: ILevel) => { if (this.props.loading) {
const suffix = level.level in this.props.user.levels ? " ✔" : ""; return <div>
return <Grid item key={key++}> <Grid
<Card style={{ container
width: small ? window.width - 32 : "300px" spacing={0}
}}> direction="column"
<CardContent className={cName}> alignItems="center"
<Typography variant="title">{`Level ${level.level}${suffix}`}</Typography> justify="center"
<Typography variant="title" component="p">{level.name}</Typography> style={{ minHeight: '100vh' }}>
<br /> <Grid item xs={12}>
<Typography component="p"> <Paper className="paper">
{level.description} <Grid container direction="column" spacing={8}>
</Typography> <CircularProgress />
</CardContent> </Grid>
<CardActions> </Paper>
<Button </Grid>
component={Link} </Grid>
to={`/level/${level.level}`} </div>;
className="lesson-card-btn"> }
Zum Level
const small = window.matchMedia("(max-width: 700px)").matches;
const cName = small ? "lesson-card-xs" : "lesson-card-lg";
let key = 0;
const levelToCard = (level: ILevel) => {
const suffix = level.level in this.props.user.levels ? " ✔" : "";
return <Grid item key={key++}>
<Card style={{
width: small ? window.width - 32 : "300px"
}}>
<CardContent className={cName}>
<Typography variant="title">{`Level ${level.level}${suffix}`}</Typography>
<Typography variant="title" component="p">{level.name}</Typography>
<br />
<Typography component="p">
{level.description}
</Typography>
</CardContent>
<CardActions>
<Button
className="lesson-card-btn"
onClick={() => this.toLevel(level.level)}>
Zum Level
</Button> </Button>
</CardActions> </CardActions>
</Card> </Card>
</Grid>; </Grid>;
}; };
return <Grid container spacing={16} direction="row"> return <div>
{this.props.levels.map(levelToCard)} <Grid container spacing={16} direction="row">
</Grid> {this.props.levels.map(levelToCard)}
</Grid>
<Snackbar
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
open={this.props.snackbar}
onClose={() => this.props.setSnackbar(false)}
message={<span>Du hast dieses Level noch nicht freigeschaltet!</span>} />
</div>;
}
} }
}; );
export default LevelListWithRouter;

View File

@ -34,6 +34,7 @@ interface IState {
levelList: { levelList: {
loading: boolean; loading: boolean;
snackbar: boolean;
}; };
dashboard: { dashboard: {
@ -91,6 +92,7 @@ const initialState: IState = {
levelList: { levelList: {
loading: true, loading: true,
snackbar: false,
}, },
dashboard: { dashboard: {
@ -248,6 +250,12 @@ export function LateinicusApp(state: IState = initialState, action: any) {
loading: action.state, loading: action.state,
}), }),
}); });
case Actions.LEVELLIST_SET_SNACKBAR:
return Object.assign({}, state, {
levelList: Object.assign({}, state.levelList, {
snackbar: action.state,
}),
});
default: default:
// Ignore the initialization call to the reducer. By that we can // Ignore the initialization call to the reducer. By that we can
// catch all actions that are not implemented // catch all actions that are not implemented