diff --git a/package.json b/package.json index 75ec5b9..3097ac3 100644 --- a/package.json +++ b/package.json @@ -8,12 +8,13 @@ "@material-ui/core": "^3.7.0", "@material-ui/icons": "^3.0.1", "dayjs": "^1.8.8", + "js-cookie": "^2.2.0", "react": "^16.6.3", "react-ace": "^6.4.0", "react-dom": "^16.6.3", + "react-l10n": "^0.3.0", "react-router-dom": "^4.3.1", - "react-scripts": "^2.1.5", - "typeface-roboto": "0.0.54" + "react-scripts": "^2.1.5" }, "scripts": { "start": "react-scripts start", diff --git a/src/app/globalStatusBar/clock/clock.js b/src/app/globalStatusBar/clock/clock.js index 3d398a4..81751b0 100644 --- a/src/app/globalStatusBar/clock/clock.js +++ b/src/app/globalStatusBar/clock/clock.js @@ -7,6 +7,8 @@ import AccessTime from '@material-ui/icons/AccessTime' import timeAgo from '../../../external/timeAgo.js'; import './clock.css' +import LocalizedMessage from 'react-l10n'; + /** * @name CountdownClock * @description A simple clock that shows time left and the duration of the contest @@ -57,9 +59,16 @@ class CountdownClock extends Component { diff --git a/src/app/globalStatusBar/globalStatusBar.js b/src/app/globalStatusBar/globalStatusBar.js index 633791e..e89d792 100644 --- a/src/app/globalStatusBar/globalStatusBar.js +++ b/src/app/globalStatusBar/globalStatusBar.js @@ -2,6 +2,8 @@ import React, { Component } from 'react' import { Toolbar, AppBar, IconButton } from '@material-ui/core' import Menu from '@material-ui/icons/Menu' +import LocalizedMessage from 'react-l10n'; + import ContestSignature from './contestSignature/contestSignature.js' import CountdownClock from './clock/clock.js' import LoginButton from './login/loginButton.js' @@ -135,7 +137,7 @@ class GlobalStatusBar extends Component { }) } > - {'Log in here'} + ) : ( - Log in + + + - Log in to submit solutions and view problems. + {/* TODO : configurable language */} @@ -107,7 +111,7 @@ class LoginDialog extends Component { } value={this.state.id} onChange={this.handleUserIDChange} fullWidth={true} @@ -128,7 +132,7 @@ class LoginDialog extends Component { } ref={this.state.passkeyRef} type="password" onChange={this.handleKeyChange} @@ -144,9 +148,8 @@ class LoginDialog extends Component { diff --git a/src/app/globalStatusBar/login/stub/login.js b/src/app/globalStatusBar/login/stub/login.js index c6df1ae..7b976e6 100644 --- a/src/app/globalStatusBar/login/stub/login.js +++ b/src/app/globalStatusBar/login/stub/login.js @@ -1,5 +1,3 @@ -import { pushNotification } from '../../../notifier/notify.js' - /** * @name constructRequestBody * @desc Create an URLSearchParam object suitable to be sent as log in request @@ -40,8 +38,6 @@ async function login(username, password) { ) .then(res => res.ok) .catch(err => { - if (typeof pushNotification === 'function') - pushNotification('Failed to log in.') if (process.env.NODE_ENV === 'development') console.log(err); return false; diff --git a/src/app/globalStatusBar/userSetting/localeChange/localeChange.js b/src/app/globalStatusBar/userSetting/localeChange/localeChange.js new file mode 100644 index 0000000..840c672 --- /dev/null +++ b/src/app/globalStatusBar/userSetting/localeChange/localeChange.js @@ -0,0 +1,21 @@ +import React from 'react'; +import { RadioGroup, FormControlLabel, Radio, FormControl } from '@material-ui/core'; + +export default class LocaleChange extends React.PureComponent { + render() { + let out = []; + for (let lang_key in this.props.languages) { + out.push(}/>) + } + return ( +
+ + + {out} + + +
+ ) + } +} \ No newline at end of file diff --git a/src/app/globalStatusBar/userSetting/userSettingDialog.js b/src/app/globalStatusBar/userSetting/userSettingDialog.js index a9561d0..5a01f68 100644 --- a/src/app/globalStatusBar/userSetting/userSettingDialog.js +++ b/src/app/globalStatusBar/userSetting/userSettingDialog.js @@ -10,9 +10,17 @@ import { Tab, } from '@material-ui/core' import { Button } from '@material-ui/core' + +import * as Cookies from 'js-cookie'; + import PasswordChangeDialog from './passwordChange/passwordChangeDialog.js' +import LocaleChange from './localeChange/localeChange.js'; import { fade } from '../lib/libTransition.js' +import { supportedLanguages } from '../../../l10n-loader.js'; + +import LocalizedMessage from 'react-l10n'; + /** * @name UserSettingDialog * @description Dialog to change user settings. @@ -26,20 +34,35 @@ import { fade } from '../lib/libTransition.js' class UserSettingDialog extends Component { constructor(props) { - super(props) + super(props); + + // default to en_US + if (!Cookies.get('language')) + Cookies.set('language', 'en_US') + this.state = { currentTab: 0, pwdChangeDialogOpen: false, + + language: Cookies.get('language') } - this.handleChange = this.handleChange.bind(this) + + this.requireReload = false; + // some settings require reloading + + this.submitOptions = this.submitOptions.bind(this); } - handleChange(event, value) { - this.setState({ - currentTab: value, - }) + submitOptions() { + Cookies.set('language', this.state.language); + + if (this.requireReload) + window.location.reload(); + else + this.props.onClose() } + render() { return ( <> @@ -52,17 +75,26 @@ class UserSettingDialog extends Component { } > - User settings{' '} - {this.props.user ? `for ${this.props.user}` : ''} + {this.props.user + ? ( + <> + + {this.props.user} + + ) + : ( + + )} - + onChange={(e, v) => this.setState({ currentTab: v })}> + {/* we only care about the target value, ignore the event passed */} + } /> + } /> {this.state.currentTab === 0 && ( @@ -78,14 +110,33 @@ class UserSettingDialog extends Component { }) } > - Change your password + )} + {this.state.currentTab === 1 && ( + <> + { + this.requireReload = arg !== Cookies.get('language'); + this.setState({ language: arg }) + }}/> + {this.requireReload + &&
+ +
} + + )}
- - + + - Welcome back, {this.props.user} + + + {this.props.user} + - Change user settings + + + + - Log out ) } diff --git a/src/app/home/homepageLauncher.js b/src/app/home/homepageLauncher.js index f5318db..5f80e40 100644 --- a/src/app/home/homepageLauncher.js +++ b/src/app/home/homepageLauncher.js @@ -2,6 +2,8 @@ import React from 'react' import { ListItemIcon, ListItemText } from '@material-ui/core' import Home from '@material-ui/icons/Home' +import LocalizedMessage from 'react-l10n'; + /** * @name HomepageLauncher * @description Element to be rendered in the sidenav, responsible for launching home page @@ -14,7 +16,9 @@ class HomepageLauncher extends React.Component { - Home + + + ) } diff --git a/src/app/problemList/codeEditor/codeBox.js b/src/app/problemList/codeEditor/codeBox.js index 04a6c30..bff3255 100644 --- a/src/app/problemList/codeEditor/codeBox.js +++ b/src/app/problemList/codeEditor/codeBox.js @@ -1,5 +1,6 @@ -import React from "react"; -import { AppBar, Grid, Divider } from "@material-ui/core"; +import React from 'react' +import { AppBar, Grid, Divider } from '@material-ui/core' +import LocalizedMessage from 'react-l10n'; import CodeEditor from "./codeEditor.js"; import SubmitButton from "./submitButton.js"; @@ -22,6 +23,7 @@ class CodeBox extends React.PureComponent { fileLoading: false, editorHeight: window.innerHeight - 180 }; + this.catcherRef = React.createRef(); this.updateEditorHeight = this.updateEditorHeight.bind(this); this.processFile = this.processFile.bind(this); @@ -119,7 +121,7 @@ class CodeBox extends React.PureComponent { }) } > - Submit +
@@ -138,6 +140,7 @@ class CodeBox extends React.PureComponent { // enforce source limit even for editor }} code={this.state.code} + editorHeight={this.state.editorHeight} /> this.processFile(event.target.files[0])} diff --git a/src/app/problemList/codeEditor/langSelection.js b/src/app/problemList/codeEditor/langSelection.js index 11effbf..ad25192 100644 --- a/src/app/problemList/codeEditor/langSelection.js +++ b/src/app/problemList/codeEditor/langSelection.js @@ -4,6 +4,7 @@ import Menu from "@material-ui/core/Menu"; import { MenuItem, Tooltip } from "@material-ui/core"; import PropTypes from "prop-types"; +import LocalizedMessage from 'react-l10n'; import friendlyLang from '../../../strings/lang.json'; @@ -52,14 +53,16 @@ class LangSelection extends React.Component { return ( <> - + } + placement="bottom"> : + ) ) return res.ok }) .catch(() => { if (typeof pushNotification === 'function') pushNotification( - 'Failed to submit. It seems like a transmission error...' + ) }) } diff --git a/src/app/problemList/codeEditor/uploadButton.js b/src/app/problemList/codeEditor/uploadButton.js index 1f9c7be..0eca25e 100644 --- a/src/app/problemList/codeEditor/uploadButton.js +++ b/src/app/problemList/codeEditor/uploadButton.js @@ -2,10 +2,12 @@ import React from 'react'; import { Button, Tooltip, CircularProgress } from '@material-ui/core'; import CloudUpload from '@material-ui/icons/CloudUpload'; +import LocalizedMessage from 'react-l10n' + export default class UploadButton extends React.PureComponent { render() { return ( - + }>