Implement a summary screen
This commit is contained in:
		
							parent
							
								
									2c9a2e88c7
								
							
						
					
					
						commit
						4e0506f607
					
				
							
								
								
									
										40
									
								
								src/components/SummaryTable.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/components/SummaryTable.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
 | 
			
		||||
import Table from "@material-ui/core/Table";
 | 
			
		||||
import TableHead from "@material-ui/core/TableHead";
 | 
			
		||||
import TableBody from "@material-ui/core/TableBody";
 | 
			
		||||
import TableRow from "@material-ui/core/TableRow";
 | 
			
		||||
import TableCell from "@material-ui/core/TableCell";
 | 
			
		||||
 | 
			
		||||
import { IReviewMetadata } from "../models/review";
 | 
			
		||||
 | 
			
		||||
interface IProps {
 | 
			
		||||
    reviewMeta: IReviewMetadata;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default class SummaryTable extends React.Component<IProps> {
 | 
			
		||||
    render() {
 | 
			
		||||
        return <Table>
 | 
			
		||||
            <TableHead>
 | 
			
		||||
                <TableRow>
 | 
			
		||||
                    <TableCell>Antworten</TableCell>
 | 
			
		||||
                    <TableCell>Anzahl</TableCell>
 | 
			
		||||
                </TableRow>
 | 
			
		||||
            </TableHead>
 | 
			
		||||
            <TableBody>
 | 
			
		||||
                <TableRow>
 | 
			
		||||
                    <TableCell>Korrekt</TableCell>
 | 
			
		||||
                    <TableCell numeric>{this.props.reviewMeta.correct}</TableCell>
 | 
			
		||||
                </TableRow>
 | 
			
		||||
                <TableRow>
 | 
			
		||||
                    <TableCell>Fast richtig</TableCell>
 | 
			
		||||
                    <TableCell numeric>{this.props.reviewMeta.nearly}</TableCell>
 | 
			
		||||
                </TableRow>
 | 
			
		||||
                <TableRow>
 | 
			
		||||
                    <TableCell>Falsch</TableCell>
 | 
			
		||||
                    <TableCell numeric>{this.props.reviewMeta.wrong}</TableCell>
 | 
			
		||||
                </TableRow>
 | 
			
		||||
            </TableBody>
 | 
			
		||||
        </Table>;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -25,9 +25,11 @@ import LoginPage from "../pages/login";
 | 
			
		||||
import LevelListPage from "../pages/levelList";
 | 
			
		||||
import LevelPage from "../pages/level";
 | 
			
		||||
import ReviewPage from "../pages/review";
 | 
			
		||||
import SummaryPage from "../pages/summary";
 | 
			
		||||
 | 
			
		||||
import { ILevel } from "../models/level";
 | 
			
		||||
import { IVocab } from "../models/vocab";
 | 
			
		||||
import { IVocab, VocabType } from "../models/vocab";
 | 
			
		||||
import { IReviewMetadata } from "../models/review";
 | 
			
		||||
 | 
			
		||||
interface IState {
 | 
			
		||||
    loggedIn: boolean;
 | 
			
		||||
@ -71,6 +73,15 @@ export default class Application extends React.Component<{}, IState> {
 | 
			
		||||
        return levels;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getLastReview(): IReviewMetadata {
 | 
			
		||||
        // TODO: Actually fetch this
 | 
			
		||||
        return {
 | 
			
		||||
            correct: 5,
 | 
			
		||||
            nearly: 5,
 | 
			
		||||
            wrong: 5,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getLearners(): ILearner[] {
 | 
			
		||||
        return [{
 | 
			
		||||
            username: "Polynomdivision",
 | 
			
		||||
@ -104,23 +115,27 @@ export default class Application extends React.Component<{}, IState> {
 | 
			
		||||
            latin: "Vinum",
 | 
			
		||||
            german: "Wein",
 | 
			
		||||
            hint: "Worte auf '-um' sind meistens NeutrUM"
 | 
			
		||||
            type: VocabType.NOMEN,
 | 
			
		||||
            id: 0
 | 
			
		||||
        }, {
 | 
			
		||||
            latin: "Vinum (Genitiv)",
 | 
			
		||||
            german: "<Wortbedeutung>",
 | 
			
		||||
            german: "Vini",
 | 
			
		||||
            type: VocabType.NOMEN,
 | 
			
		||||
            id: 1
 | 
			
		||||
        }/* , {
 | 
			
		||||
          *    latin: "Vici",
 | 
			
		||||
          *    german: "<Wortbedeutung>",
 | 
			
		||||
          *    hint: "Wird \"Viki\" und nicht \"Vichi\" ausgesprochen",
 | 
			
		||||
          *    mnemonic: "Merk dir das Wort mit Caesars berühmten Worten: \"Veni Vidi Vici\"; Er kam, sah und siegte",
 | 
			
		||||
          *    type: VocabType.NOMEN,
 | 
			
		||||
          *    id: 2
 | 
			
		||||
            }, {
 | 
			
		||||
            latin: "Vici",
 | 
			
		||||
            german: "<Wortbedeutung>",
 | 
			
		||||
            hint: "Wird \"Viki\" und nicht \"Vichi\" ausgesprochen",
 | 
			
		||||
            mnemonic: "Merk dir das Wort mit Caesars berühmten Worten: \"Veni Vidi Vici\"; Er kam, sah und siegte",
 | 
			
		||||
            id: 2
 | 
			
		||||
        }, {
 | 
			
		||||
            latin: "fuga",
 | 
			
		||||
            german: "Flucht",
 | 
			
		||||
            hint: "Worte auf \"-a\" sind FeminA",
 | 
			
		||||
            id: 3
 | 
			
		||||
        }] as IVocab[];
 | 
			
		||||
          *    latin: "fuga",
 | 
			
		||||
          *    german: "Flucht",
 | 
			
		||||
          *    hint: "Worte auf \"-a\" sind FeminA",
 | 
			
		||||
          *    type: VocabType.NOMEN,
 | 
			
		||||
          *    id: 3
 | 
			
		||||
            } */] as IVocab[];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    login(username: string, password: string): Promise<boolean> {
 | 
			
		||||
@ -233,7 +248,12 @@ export default class Application extends React.Component<{}, IState> {
 | 
			
		||||
                    <AuthRoute
 | 
			
		||||
                        isAuth={this.isAuthenticated}
 | 
			
		||||
                        path="/dashboard"
 | 
			
		||||
                        component={() => <Dashboard nextLevel={this.getNextLevel} learners={this.getLearners()} />} />
 | 
			
		||||
                        component={() => {
 | 
			
		||||
                            return <Dashboard
 | 
			
		||||
                                nextLevel={this.getNextLevel}
 | 
			
		||||
                                lastReview={this.getLastReview}
 | 
			
		||||
                                learners={this.getLearners()} />
 | 
			
		||||
                        }} />
 | 
			
		||||
                    <AuthRoute
 | 
			
		||||
                        isAuth={this.isAuthenticated}
 | 
			
		||||
                        path="/levelList"
 | 
			
		||||
@ -257,6 +277,12 @@ export default class Application extends React.Component<{}, IState> {
 | 
			
		||||
                                return <Redirect to="/login" />;
 | 
			
		||||
                            }
 | 
			
		||||
                        }} />
 | 
			
		||||
                    <AuthRoute
 | 
			
		||||
                        isAuth={this.isAuthenticated}
 | 
			
		||||
                        path="/review/summary"
 | 
			
		||||
                        component={() => {
 | 
			
		||||
                            return <SummaryPage reviewMeta={this.getLastReview} />
 | 
			
		||||
                        }} />
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </BrowserRouter>;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										8
									
								
								src/models/review.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/models/review.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
export interface IReviewMetadata {
 | 
			
		||||
    // Number of correct answers
 | 
			
		||||
    correct: number;
 | 
			
		||||
    // Number of wrong answers
 | 
			
		||||
    wrong: number;
 | 
			
		||||
    // Number of nearly correct answers
 | 
			
		||||
    nearly: number;
 | 
			
		||||
};
 | 
			
		||||
@ -3,12 +3,28 @@ export enum ReviewMode {
 | 
			
		||||
    LAT_TO_GER,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum VocabType {
 | 
			
		||||
    NOMEN,
 | 
			
		||||
    VERB,
 | 
			
		||||
    ADJEKTIV,
 | 
			
		||||
    ADVERB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IVocab {
 | 
			
		||||
    german: string;
 | 
			
		||||
    latin: string;
 | 
			
		||||
    hint?: string;
 | 
			
		||||
    mnemonic?: string;
 | 
			
		||||
 | 
			
		||||
    type: VocabType;
 | 
			
		||||
 | 
			
		||||
    // This number is lesson specific
 | 
			
		||||
    id: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const typeToPoints = (VocabType type) => {
 | 
			
		||||
    switch (type) {
 | 
			
		||||
        // Nomen: 2P + 1 (Wenn richtig)
 | 
			
		||||
        // 
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,12 +8,15 @@ import Button from "@material-ui/core/Button";
 | 
			
		||||
import Paper from "@material-ui/core/Paper";
 | 
			
		||||
 | 
			
		||||
import Scoreboard from "../components/scoreboard";
 | 
			
		||||
import SummaryTable from "../components/SummaryTable";
 | 
			
		||||
 | 
			
		||||
import { ILevel } from "../models/level";
 | 
			
		||||
import { ILearner } from "../models/learner";
 | 
			
		||||
import { IReviewMetadata } from "../models/review";
 | 
			
		||||
 | 
			
		||||
interface IProps {
 | 
			
		||||
    nextLevel: () => ILevel;
 | 
			
		||||
    lastReview: () => IReviewMetadata;
 | 
			
		||||
    learners: ILearner[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -72,7 +75,10 @@ export default class Dashboard extends React.Component<IProps, IState> {
 | 
			
		||||
                </Grid>
 | 
			
		||||
                <Grid item lg={4}>
 | 
			
		||||
                    <Paper className="paper">
 | 
			
		||||
                        Some stuff
 | 
			
		||||
                        <Typography variant="title">
 | 
			
		||||
                            Letzte Wiederholung
 | 
			
		||||
                        </Typography>
 | 
			
		||||
                        <SummaryTable reviewMeta={this.props.lastReview()} />
 | 
			
		||||
                    </Paper>
 | 
			
		||||
                </Grid>
 | 
			
		||||
            </Grid>
 | 
			
		||||
 | 
			
		||||
@ -60,6 +60,15 @@ export default class LevelPage extends React.Component<IProps, IState> {
 | 
			
		||||
        </ListItem>;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toReview = () => {
 | 
			
		||||
        // Only go to the review if all vocabulary item have been looked at
 | 
			
		||||
        if (this.props.levelVocab().length === this.state.lookedAt.length) {
 | 
			
		||||
            this.setState({
 | 
			
		||||
                toReview: true,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render() {
 | 
			
		||||
        const { currentVocab } = this.state;
 | 
			
		||||
 | 
			
		||||
@ -69,9 +78,7 @@ export default class LevelPage extends React.Component<IProps, IState> {
 | 
			
		||||
                    <List>
 | 
			
		||||
                        {this.props.levelVocab(this.props.id).map(this.renderVocabListItem)}
 | 
			
		||||
                        {/* TODO*/}
 | 
			
		||||
                        <ListItem button onClick={() => this.setState({
 | 
			
		||||
                            toReview: true,
 | 
			
		||||
                        })}>
 | 
			
		||||
                        <ListItem button onClick={this.toReview}>
 | 
			
		||||
                            <ListItemText>
 | 
			
		||||
                                Zur Übung
 | 
			
		||||
                            </ListItemText>
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,17 @@
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
 | 
			
		||||
import Card from "@material-ui/core/Card";
 | 
			
		||||
import CardContent from "@material-ui/core/CardContent";
 | 
			
		||||
import TextField from "@material-ui/core/TextField";
 | 
			
		||||
import Grid from "@material-ui/core/Grid";
 | 
			
		||||
import Button from "@material-ui/core/Button";
 | 
			
		||||
import Typography from "@material-ui/core/Button";
 | 
			
		||||
import Typography from "@material-ui/core/Typography";
 | 
			
		||||
import Popover from "@material-ui/core/Popover";
 | 
			
		||||
import Paper from "@material-ui/core/Paper";
 | 
			
		||||
 | 
			
		||||
import { IVocab, ReviewMode } from "../models/vocab";
 | 
			
		||||
import { Redirect } from "react-router-dom";
 | 
			
		||||
 | 
			
		||||
import { IVocab, ReviewMode, VocabType } from "../models/vocab";
 | 
			
		||||
 | 
			
		||||
interface IProps {
 | 
			
		||||
    levelId: number;
 | 
			
		||||
@ -16,25 +21,38 @@ interface IProps {
 | 
			
		||||
interface IState {
 | 
			
		||||
    input: string;
 | 
			
		||||
    current: number;
 | 
			
		||||
 | 
			
		||||
    toSummary: boolean;
 | 
			
		||||
 | 
			
		||||
    popoverOpen: boolean;
 | 
			
		||||
    popoverText: string;
 | 
			
		||||
    popoverColor: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default class ReviewPage extends React.Component<IProps> {
 | 
			
		||||
export default class ReviewPage extends React.Component<IProps, IState> {
 | 
			
		||||
    private vocab: IVocab[] = [];
 | 
			
		||||
    // Used for positioning the popover
 | 
			
		||||
    private buttonRef: HTMLButtonElement;
 | 
			
		||||
 | 
			
		||||
    constructor(props: any) {
 | 
			
		||||
        super(props);
 | 
			
		||||
 | 
			
		||||
        this.state = {
 | 
			
		||||
            input: "",
 | 
			
		||||
            current: 0,
 | 
			
		||||
 | 
			
		||||
            toSummary: false,
 | 
			
		||||
 | 
			
		||||
            popoverOpen: false,
 | 
			
		||||
            popoverText: "",
 | 
			
		||||
            popoverColor: "red",
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const { vocabByLevel, levelId } = this.props;
 | 
			
		||||
        this.vocab = vocabByLevel(levelId);
 | 
			
		||||
 | 
			
		||||
        this.currentVocab = this.currentVocab.bind(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    currentVocab() {
 | 
			
		||||
    currentVocab = () => {
 | 
			
		||||
        return this.vocab[this.state.current];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -43,45 +61,91 @@ export default class ReviewPage extends React.Component<IProps> {
 | 
			
		||||
 | 
			
		||||
        // Check if the user's answer was correct
 | 
			
		||||
        // TODO: Levensthein-Distance?
 | 
			
		||||
        if (this.state.input === current.german) {
 | 
			
		||||
        if (this.state.input.toLowerCase() === current.german.toLowerCase()) {
 | 
			
		||||
            // TODO: Show it's correct
 | 
			
		||||
            console.log("Hell yeah");
 | 
			
		||||
 | 
			
		||||
            // Show the next vocab word
 | 
			
		||||
            if (this.state.current + 1 >= this.vocab.length) {
 | 
			
		||||
                // TODO: Go to a summary screen
 | 
			
		||||
                // TODO: Set some data that the summary screen will show
 | 
			
		||||
                this.setState({
 | 
			
		||||
                    toSummary: true,
 | 
			
		||||
                });
 | 
			
		||||
            } else {
 | 
			
		||||
                // Increase the vocab
 | 
			
		||||
                this.setState({
 | 
			
		||||
                    current: this.state.current + 1,
 | 
			
		||||
                    input: "",
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // TODO: Show it's wrong
 | 
			
		||||
            console.log("Hell no");
 | 
			
		||||
            this.setState({
 | 
			
		||||
                popoverOpen: true,
 | 
			
		||||
                popoverText: "Das war nicht richtig",
 | 
			
		||||
                popoverColor: "red",
 | 
			
		||||
                // TODO: Or maybe don't reset the text
 | 
			
		||||
                input: "",
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // TODO: Show a snackbar for showing the updated score
 | 
			
		||||
        // TODO(?): Show a snackbar for showing the updated score
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render() {
 | 
			
		||||
        return <div style={{ margin: 12 }}>
 | 
			
		||||
        return <div>
 | 
			
		||||
            {
 | 
			
		||||
                this.state.toSummary ? (
 | 
			
		||||
                    <Redirect to="/review/summary" />
 | 
			
		||||
                ) : undefined
 | 
			
		||||
            }
 | 
			
		||||
            <Grid container justify="center">
 | 
			
		||||
                <Grid item>
 | 
			
		||||
                    <Card>
 | 
			
		||||
                        <CardContent>
 | 
			
		||||
                            <Grid container direction="column">
 | 
			
		||||
                            <h1>
 | 
			
		||||
                                {this.vocab[this.state.current].latin}
 | 
			
		||||
                            </h1>
 | 
			
		||||
                                <Typography variant="display2">
 | 
			
		||||
                                    {this.currentVocab().latin}
 | 
			
		||||
                                </Typography>
 | 
			
		||||
                                <TextField
 | 
			
		||||
                                    margin="normal"
 | 
			
		||||
                                    fullWidth={true}
 | 
			
		||||
                                    value={this.state.input}
 | 
			
		||||
                                    onChange={(ev) => this.setState({
 | 
			
		||||
                                        input: ev.target.value,
 | 
			
		||||
                                })} />
 | 
			
		||||
                            <Button onClick={this.checkInput}>
 | 
			
		||||
                                    })}
 | 
			
		||||
                                    onKeyPress={(ev) => {
 | 
			
		||||
                                        // Allow checking of the answer by pressing Enter
 | 
			
		||||
                                        if (ev.key === "Enter")
 | 
			
		||||
                                            this.checkInput();
 | 
			
		||||
                                    }} />
 | 
			
		||||
                                <Popover
 | 
			
		||||
                                    open={this.state.popoverOpen}
 | 
			
		||||
                                    anchorOrigin={{
 | 
			
		||||
                                        vertical: "center",
 | 
			
		||||
                                        horizontal: "left"
 | 
			
		||||
                                    }}
 | 
			
		||||
                                    transformOrigin={{
 | 
			
		||||
                                        vertical: "bottom",
 | 
			
		||||
                                        horizontal: "left"
 | 
			
		||||
                                    }}
 | 
			
		||||
                                    anchorEl={this.buttonRef}
 | 
			
		||||
                                    onClose={() => this.setState({
 | 
			
		||||
                                        popoverOpen: false,
 | 
			
		||||
                                    })}
 | 
			
		||||
                                    PaperProps={{
 | 
			
		||||
                                        style: {
 | 
			
		||||
                                            backgroundColor: this.state.popoverColor,
 | 
			
		||||
                                            padding: 10,
 | 
			
		||||
                                            color: "white"
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }}>
 | 
			
		||||
                                    <Typography variant="button" color="inherit">
 | 
			
		||||
                                        {this.state.popoverText}
 | 
			
		||||
                                    </Typography>
 | 
			
		||||
                                </Popover>
 | 
			
		||||
                                <Button onClick={this.checkInput} buttonRef={node => this.buttonRef = node}>
 | 
			
		||||
                                    Prüfen
 | 
			
		||||
                            </Button>
 | 
			
		||||
                            </Grid>
 | 
			
		||||
                        </CardContent>
 | 
			
		||||
                    </Card>
 | 
			
		||||
                </Grid>
 | 
			
		||||
            </Grid>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										60
									
								
								src/pages/summary.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/pages/summary.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
 | 
			
		||||
import Typography from "@material-ui/core/Typography";
 | 
			
		||||
import Paper from "@material-ui/core/Paper";
 | 
			
		||||
import Grid from "@material-ui/core/Grid";
 | 
			
		||||
import Button from "@material-ui/core/Button";
 | 
			
		||||
import SummaryTable from "../components/SummaryTable";
 | 
			
		||||
 | 
			
		||||
import { Redirect } from "react-router-dom";
 | 
			
		||||
 | 
			
		||||
import { IReviewMetadata } from "../models/review";
 | 
			
		||||
 | 
			
		||||
interface IProps {
 | 
			
		||||
    reviewMeta: () => IReviewMetadata;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IState {
 | 
			
		||||
    toDashboard: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default class SummaryPage extends React.Component<IProps, IState> {
 | 
			
		||||
    constructor(props: any) {
 | 
			
		||||
        super(props);
 | 
			
		||||
 | 
			
		||||
        this.state = {
 | 
			
		||||
            toDashboard: false,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render() {
 | 
			
		||||
        return <div>
 | 
			
		||||
            {
 | 
			
		||||
                this.state.toDashboard ? (
 | 
			
		||||
                    <Redirect to="/dashboard" />
 | 
			
		||||
                ) : undefined
 | 
			
		||||
            }
 | 
			
		||||
            <Grid
 | 
			
		||||
                container
 | 
			
		||||
                spacing={0}
 | 
			
		||||
                direction="column"
 | 
			
		||||
                alignItems="center"
 | 
			
		||||
                justify="center"
 | 
			
		||||
                style={{ minHeight: '100vh' }}>
 | 
			
		||||
                <Grid item xs={12}>
 | 
			
		||||
                    <Paper className="paper">
 | 
			
		||||
                        <Typography variant="title">Zusammenfassung</Typography>
 | 
			
		||||
                        <Grid container direction="column">
 | 
			
		||||
                            <SummaryTable reviewMeta={this.props.reviewMeta} />
 | 
			
		||||
                            <Button onClick={() => this.setState({
 | 
			
		||||
                                toDashboard: true,
 | 
			
		||||
                            })}>
 | 
			
		||||
                                Zum Dashboard
 | 
			
		||||
                            </Button>
 | 
			
		||||
                        </Grid>
 | 
			
		||||
                    </Paper>
 | 
			
		||||
                </Grid>
 | 
			
		||||
            </Grid>
 | 
			
		||||
        </div>;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user