diff --git a/ui/src/component/ConfirmDialog.js b/ui/src/component/ConfirmDialog.tsx similarity index 74% rename from ui/src/component/ConfirmDialog.js rename to ui/src/component/ConfirmDialog.tsx index 67a0fa6..55611c7 100644 --- a/ui/src/component/ConfirmDialog.js +++ b/ui/src/component/ConfirmDialog.tsx @@ -1,17 +1,16 @@ -import React, {Component} from 'react'; import Button from 'material-ui/Button'; import Dialog, {DialogActions, DialogContent, DialogContentText, DialogTitle} from 'material-ui/Dialog'; -import PropTypes from 'prop-types'; +import React, {Component} from 'react'; -export default class ConfirmDialog extends Component { - static propTypes = { - title: PropTypes.string.isRequired, - text: PropTypes.string.isRequired, - fClose: PropTypes.func.isRequired, - fOnSubmit: PropTypes.func.isRequired, - }; +interface IProps { + title: string + text: string + fClose: VoidFunction + fOnSubmit: VoidFunction +} - render() { +export default class ConfirmDialog extends Component { + public render() { const {title, text, fClose, fOnSubmit} = this.props; const submitAndClose = () => { fOnSubmit(); diff --git a/ui/src/component/Container.js b/ui/src/component/Container.tsx similarity index 53% rename from ui/src/component/Container.js rename to ui/src/component/Container.tsx index e699882..6ad4cd5 100644 --- a/ui/src/component/Container.js +++ b/ui/src/component/Container.tsx @@ -1,7 +1,7 @@ -import React, {Component} from 'react'; -import {withStyles} from 'material-ui/styles'; +import {WithStyles} from "material-ui"; import Paper from 'material-ui/Paper'; -import PropTypes from 'prop-types'; +import {withStyles} from 'material-ui/styles'; +import * as React from 'react'; const styles = () => ({ paper: { @@ -9,14 +9,12 @@ const styles = () => ({ }, }); -class Container extends Component { - static propTypes = { - classes: PropTypes.object.isRequired, - children: PropTypes.node, - style: PropTypes.object, - }; +interface IProps { + style?: object, +} - render() { +class Container extends React.Component, {}> { + public render() { const {classes, children, style} = this.props; return ( @@ -26,4 +24,4 @@ class Container extends Component { } } -export default withStyles(styles)(Container); +export default withStyles(styles)(Container); diff --git a/ui/src/component/DefaultPage.js b/ui/src/component/DefaultPage.js deleted file mode 100644 index bfa2ab2..0000000 --- a/ui/src/component/DefaultPage.js +++ /dev/null @@ -1,44 +0,0 @@ -import React, {Component} from 'react'; -import Button from 'material-ui/Button'; -import Grid from 'material-ui/Grid'; -import Typography from 'material-ui/Typography'; -import PropTypes from 'prop-types'; - -export default class DefaultPage extends Component { - static defaultProps = { - buttonDisabled: false, - hideButton: false, - maxWidth: 700, - }; - - static propTypes = { - title: PropTypes.string.isRequired, - buttonTitle: PropTypes.string, - fButton: PropTypes.func, - buttonDisabled: PropTypes.bool.isRequired, - maxWidth: PropTypes.number.isRequired, - hideButton: PropTypes.bool.isRequired, - children: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node, - ]).isRequired, - }; - - render() { - const {title, buttonTitle, fButton, buttonDisabled, maxWidth, hideButton, children} = this.props; - return ( -
- - - - {title} - - {hideButton ? null : } - - {children} - -
- ); - } -} diff --git a/ui/src/component/DefaultPage.tsx b/ui/src/component/DefaultPage.tsx new file mode 100644 index 0000000..fdbf927 --- /dev/null +++ b/ui/src/component/DefaultPage.tsx @@ -0,0 +1,29 @@ +import Button from 'material-ui/Button'; +import Grid from 'material-ui/Grid'; +import Typography from 'material-ui/Typography'; +import React, {SFC} from 'react'; + +interface IProps { + title: string + buttonTitle?: string + fButton?: VoidFunction + buttonDisabled?: boolean + maxWidth?: number + hideButton?: boolean +} + +const DefaultPage: SFC = ({title, buttonTitle, fButton, buttonDisabled = false, maxWidth = 700, hideButton, children}) => ( +
+ + + + {title} + + {hideButton ? null : } + + {children} + +
+); +export default DefaultPage; diff --git a/ui/src/component/FixedReactList.js b/ui/src/component/FixedReactList.tsx similarity index 73% rename from ui/src/component/FixedReactList.js rename to ui/src/component/FixedReactList.tsx index 303cf23..7925f7b 100644 --- a/ui/src/component/FixedReactList.js +++ b/ui/src/component/FixedReactList.tsx @@ -1,33 +1,41 @@ +// @ts-ignore import ReactList from 'react-list'; // See also https://github.com/coderiety/react-list/blob/master/react-list.es6 class FixedReactList extends ReactList { // deleting a messages or adding a message (per shift) requires invalidating the cache, react-list sucks as it does // not provide such functionality, therefore we need to hack it inside there :( - ignoreNextCacheUpdate = false; + public ignoreNextCacheUpdate = false; - cacheSizes() { + public cacheSizes(): void { if (this.ignoreNextCacheUpdate) { this.ignoreNextCacheUpdate = false; return; } + // @ts-ignore accessing private member super.cacheSizes(); } - clearCacheFromIndex(startIndex) { + + public clearCacheFromIndex(startIndex: number): void { this.ignoreNextCacheUpdate = true; if (startIndex === 0) { + // @ts-ignore accessing private member this.cache = {}; } else { + // @ts-ignore accessing private member Object.keys(this.cache).filter((index) => index >= startIndex).forEach((index) => { + // @ts-ignore accessing private member delete this.cache[index]; }); } }; - componentDidUpdate() { + public componentDidUpdate() { + // @ts-ignore accessing private member const hasCacheForLastRenderedItem = Object.keys(this.cache).length && this.cache[this.getVisibleRange()[1]]; + // @ts-ignore accessing private member super.componentDidUpdate(); if (!hasCacheForLastRenderedItem) { // when there is no cache for the last rendered item, then its a new item, react-list doesn't know it size diff --git a/ui/src/component/Header.js b/ui/src/component/Header.tsx similarity index 84% rename from ui/src/component/Header.js rename to ui/src/component/Header.tsx index c20ae2d..b007b10 100644 --- a/ui/src/component/Header.js +++ b/ui/src/component/Header.tsx @@ -1,21 +1,21 @@ -import React, {Component} from 'react'; -import AppBar from 'material-ui/AppBar'; -import Button from 'material-ui/Button'; -import Toolbar from 'material-ui/Toolbar'; -import Typography from 'material-ui/Typography'; -import {withStyles} from 'material-ui/styles'; -import PropTypes from 'prop-types'; -import IconButton from 'material-ui/IconButton'; -import * as UserAction from '../actions/UserAction'; -import {Link} from 'react-router-dom'; +import {Theme, WithStyles} from "material-ui"; +import AccountCircle from 'material-ui-icons/AccountCircle'; import Chat from 'material-ui-icons/Chat'; -import SupervisorAccount from 'material-ui-icons/SupervisorAccount'; import DevicesOther from 'material-ui-icons/DevicesOther'; import ExitToApp from 'material-ui-icons/ExitToApp'; -import AccountCircle from 'material-ui-icons/AccountCircle'; import LightbulbOutline from 'material-ui-icons/LightbulbOutline'; +import SupervisorAccount from 'material-ui-icons/SupervisorAccount'; +import AppBar from 'material-ui/AppBar'; +import Button from 'material-ui/Button'; +import IconButton from 'material-ui/IconButton'; +import {withStyles} from 'material-ui/styles'; +import Toolbar from 'material-ui/Toolbar'; +import Typography from 'material-ui/Typography'; +import React, {Component} from 'react'; +import {Link} from 'react-router-dom'; +import * as UserAction from '../actions/UserAction'; -const styles = (theme) => ({ +const styles = (theme: Theme) => ({ appBar: { zIndex: theme.zIndex.drawer + 1, }, @@ -33,38 +33,19 @@ const styles = (theme) => ({ }, }); -class Header extends Component { - static propTypes = { - classes: PropTypes.object.isRequired, - loggedIn: PropTypes.bool.isRequired, - name: PropTypes.string.isRequired, - admin: PropTypes.bool.isRequired, - version: PropTypes.string.isRequired, - toggleTheme: PropTypes.func.isRequired, - showSettings: PropTypes.func.isRequired, - }; +type Styles = WithStyles<'link' | 'titleName' | 'title' | 'appBar'> - renderButtons(name, admin) { - const {classes, showSettings} = this.props; - return ( -
- {admin - ? - - : ''} - - - - - - - -
- ); - } +interface IProps { + loggedIn: boolean + name: string + admin: boolean + version: string + toggleTheme: VoidFunction + showSettings: VoidFunction +} - render() { +class Header extends Component { + public render() { const {classes, version, name, loggedIn, admin, toggleTheme} = this.props; return ( @@ -88,6 +69,26 @@ class Header extends Component { ); } + + private renderButtons(name: string, admin: boolean) { + const {classes, showSettings} = this.props; + return ( +
+ {admin + ? + + : ''} + + + + + + + +
+ ); + } } -export default withStyles(styles, {withTheme: true})(Header); +export default withStyles(styles, {withTheme: true})(Header); diff --git a/ui/src/component/LoadingSpinner.js b/ui/src/component/LoadingSpinner.tsx similarity index 95% rename from ui/src/component/LoadingSpinner.js rename to ui/src/component/LoadingSpinner.tsx index 30e4848..060b13e 100644 --- a/ui/src/component/LoadingSpinner.js +++ b/ui/src/component/LoadingSpinner.tsx @@ -1,10 +1,10 @@ -import React, {Component} from 'react'; -import {CircularProgress} from 'material-ui/Progress'; -import DefaultPage from './DefaultPage'; import Grid from 'material-ui/Grid'; +import {CircularProgress} from 'material-ui/Progress'; +import React, {Component} from 'react'; +import DefaultPage from './DefaultPage'; class LoadingSpinner extends Component { - render() { + public render() { return ( diff --git a/ui/src/component/Message.js b/ui/src/component/Message.tsx similarity index 81% rename from ui/src/component/Message.js rename to ui/src/component/Message.tsx index 9d1192a..0f6a156 100644 --- a/ui/src/component/Message.js +++ b/ui/src/component/Message.tsx @@ -1,11 +1,11 @@ -import React, {Component} from 'react'; +import {WithStyles} from "material-ui"; +import Delete from 'material-ui-icons/Delete'; +import IconButton from 'material-ui/IconButton'; import {withStyles} from 'material-ui/styles'; import Typography from 'material-ui/Typography'; -import IconButton from 'material-ui/IconButton'; -import PropTypes from 'prop-types'; -import Container from './Container'; +import React, {Component} from 'react'; import TimeAgo from 'react-timeago'; -import Delete from 'material-ui-icons/Delete'; +import Container from './Container'; const styles = () => ({ header: { @@ -32,16 +32,18 @@ const styles = () => ({ }, }); -class Message extends Component { - static propTypes = { - classes: PropTypes.object.isRequired, - title: PropTypes.string.isRequired, - image: PropTypes.string, - date: PropTypes.string.isRequired, - content: PropTypes.string.isRequired, - fDelete: PropTypes.func.isRequired, - }; - render() { +type Style = WithStyles<'header' | 'headerTitle' | 'trash' | 'wrapperPadding' | 'messageContentWrapper' | 'image' | 'imageWrapper'>; + +interface IProps { + title: string + image?: string + date: string + content: string + fDelete: VoidFunction +} + +class Message extends Component { + public render() { const {fDelete, classes, title, date, content, image} = this.props; return ( @@ -68,4 +70,4 @@ class Message extends Component { } } -export default withStyles(styles)(Message); +export default withStyles(styles)(Message); diff --git a/ui/src/component/Navigation.js b/ui/src/component/Navigation.tsx similarity index 72% rename from ui/src/component/Navigation.js rename to ui/src/component/Navigation.tsx index 6344909..f471204 100644 --- a/ui/src/component/Navigation.js +++ b/ui/src/component/Navigation.tsx @@ -1,47 +1,47 @@ -import React, {Component} from 'react'; +import {Theme, WithStyles} from "material-ui"; import Divider from 'material-ui/Divider'; import Drawer from 'material-ui/Drawer'; import {ListItem, ListItemText} from 'material-ui/List'; import {withStyles} from 'material-ui/styles'; -import PropTypes from 'prop-types'; -import AppStore from '../stores/AppStore'; +import React, {Component} from 'react'; import {Link} from 'react-router-dom'; +import AppStore from '../stores/AppStore'; -const styles = (theme) => ({ +const styles = (theme: Theme) => ({ drawerPaper: { - position: 'relative', + position: 'relative' as 'relative', width: 250, minHeight: '100%', }, - toolbar: theme.mixins.toolbar, + toolbar: theme.mixins.toolbar as any, link: { color: 'inherit', textDecoration: 'none', }, }); -class Navigation extends Component { - static propTypes = { - classes: PropTypes.object.isRequired, - loggedIn: PropTypes.bool.isRequired, - }; +type Styles = WithStyles<'drawerPaper' | 'toolbar' | 'link'> - constructor() { - super(); - this.state = {apps: []}; - } +interface IProps { + loggedIn: boolean +} - componentWillMount() { +interface IState { + apps: IApplication[] +} + +class Navigation extends Component { + public state: IState = {apps: []}; + + public componentWillMount() { AppStore.on('change', this.updateApps); } - componentWillUnmount() { + public componentWillUnmount() { AppStore.removeListener('change', this.updateApps); } - updateApps = () => this.setState({apps: AppStore.get()}); - - render() { + public render() { const {classes, loggedIn} = this.props; const {apps} = this.state; @@ -49,7 +49,7 @@ class Navigation extends Component { ); - const userApps = apps.length === 0 ? empty : apps.map(function(app) { + const userApps = apps.length === 0 ? empty : apps.map((app) => { return ( @@ -82,6 +82,8 @@ class Navigation extends Component { ); } + + private updateApps = () => this.setState({apps: AppStore.get()}); } -export default withStyles(styles, {withTheme: true})(Navigation); +export default withStyles(styles,{withTheme: true})(Navigation); diff --git a/ui/src/component/ScrollUpButton.js b/ui/src/component/ScrollUpButton.tsx similarity index 79% rename from ui/src/component/ScrollUpButton.js rename to ui/src/component/ScrollUpButton.tsx index 0d47874..38bd9ea 100644 --- a/ui/src/component/ScrollUpButton.js +++ b/ui/src/component/ScrollUpButton.tsx @@ -1,17 +1,19 @@ -import React, {Component} from 'react'; -import Button from 'material-ui/Button'; import KeyboardArrowUp from 'material-ui-icons/KeyboardArrowUp'; +import Button from 'material-ui/Button'; +import React, {Component} from 'react'; class ScrollUpButton extends Component { - render() { + public render() { return ( ); } + + private scrollUp = () => window.scrollTo(0, 0); } export default ScrollUpButton; diff --git a/ui/src/component/SettingsDialog.js b/ui/src/component/SettingsDialog.tsx similarity index 82% rename from ui/src/component/SettingsDialog.js rename to ui/src/component/SettingsDialog.tsx index 5151132..d82d045 100644 --- a/ui/src/component/SettingsDialog.js +++ b/ui/src/component/SettingsDialog.tsx @@ -1,28 +1,22 @@ -import React, {Component} from 'react'; import Button from 'material-ui/Button'; +import Dialog, {DialogActions, DialogContent, DialogTitle} from 'material-ui/Dialog'; import TextField from 'material-ui/TextField'; import Tooltip from 'material-ui/Tooltip'; -import Dialog, {DialogActions, DialogContent, DialogTitle} from 'material-ui/Dialog'; -import PropTypes from 'prop-types'; +import React, {ChangeEvent, Component} from 'react'; import * as UserAction from '../actions/UserAction'; -export default class SettingsDialog extends Component { - static propTypes = { - fClose: PropTypes.func.isRequired, - }; +interface IState { + pass: string +} - constructor() { - super(); - this.state = {pass: ''}; - } +interface IProps { + fClose: VoidFunction +} - handleChange(propertyName, event) { - const state = this.state; - state[propertyName] = event.target.value; - this.setState(state); - } +export default class SettingsDialog extends Component { + public state = {pass: ''}; - render() { + public render() { const {pass} = this.state; const {fClose} = this.props; const submitAndClose = () => { @@ -50,4 +44,10 @@ export default class SettingsDialog extends Component { ); } + + private handleChange(propertyName: string, event: ChangeEvent) { + const state = this.state; + state[propertyName] = event.target.value; + this.setState(state); + } } diff --git a/ui/src/component/SnackBarHandler.js b/ui/src/component/SnackBarHandler.tsx similarity index 73% rename from ui/src/component/SnackBarHandler.js rename to ui/src/component/SnackBarHandler.tsx index c6648cc..4530308 100644 --- a/ui/src/component/SnackBarHandler.js +++ b/ui/src/component/SnackBarHandler.tsx @@ -1,54 +1,37 @@ +import Close from 'material-ui-icons/Close'; +import IconButton from 'material-ui/IconButton'; +import Snackbar from 'material-ui/Snackbar'; import React, {Component} from 'react'; import SnackBarStore from '../stores/SnackBarStore'; -import Snackbar from 'material-ui/Snackbar'; -import IconButton from 'material-ui/IconButton'; -import Close from 'material-ui-icons/Close'; -class SnackBarHandler extends Component { - static MAX_VISIBLE_SNACK_TIME_IN_MS = 6000; - static MIN_VISIBLE_SNACK_TIME_IN_MS = 1000; - state = { +interface IState { + current: string + hasNext: boolean + open: boolean + openWhen: number +} + +class SnackBarHandler extends Component<{}, IState> { + private static MAX_VISIBLE_SNACK_TIME_IN_MS = 6000; + private static MIN_VISIBLE_SNACK_TIME_IN_MS = 1000; + + public state = { current: '', hasNext: false, open: false, openWhen: 0, }; - componentWillMount = () => SnackBarStore.on('change', this.onNewSnack); - componentWillUnmount = () => SnackBarStore.removeListener('change', this.onNewSnack); + public componentWillMount() { + SnackBarStore.on('change', this.onNewSnack); + } - onNewSnack = () => { - const {open, openWhen} = this.state; + public componentWillUnmount() { + SnackBarStore.removeListener('change', this.onNewSnack); + } - if (!open) { - this.openNextSnack(); - return; - } - - const snackOpenSince = Date.now() - openWhen; - if (snackOpenSince > SnackBarHandler.MIN_VISIBLE_SNACK_TIME_IN_MS) { - this.closeCurrentSnack(); - } else { - setTimeout(this.closeCurrentSnack, SnackBarHandler.MIN_VISIBLE_SNACK_TIME_IN_MS - snackOpenSince); - } - }; - - openNextSnack = () => { - if (SnackBarStore.hasNext()) { - this.setState({ - ...this.state, - open: true, - openWhen: Date.now(), - current: SnackBarStore.next(), - hasNext: SnackBarStore.hasNext(), - }); - } - }; - - closeCurrentSnack = () => this.setState({...this.state, open: false}); - - render() { + public render() { const {open, current, hasNext} = this.state; const duration = hasNext ? SnackBarHandler.MIN_VISIBLE_SNACK_TIME_IN_MS @@ -68,6 +51,36 @@ class SnackBarHandler extends Component { /> ); } + + private onNewSnack = () => { + const {open, openWhen} = this.state; + + if (!open) { + this.openNextSnack(); + return; + } + + const snackOpenSince = Date.now() - openWhen; + if (snackOpenSince > SnackBarHandler.MIN_VISIBLE_SNACK_TIME_IN_MS) { + this.closeCurrentSnack(); + } else { + setTimeout(this.closeCurrentSnack, SnackBarHandler.MIN_VISIBLE_SNACK_TIME_IN_MS - snackOpenSince); + } + }; + + private openNextSnack = () => { + if (SnackBarStore.hasNext()) { + this.setState({ + ...this.state, + open: true, + openWhen: Date.now(), + current: SnackBarStore.next(), + hasNext: SnackBarStore.hasNext(), + }); + } + }; + + private closeCurrentSnack = () => this.setState({...this.state, open: false}); } export default SnackBarHandler; diff --git a/ui/src/component/ToggleVisibility.js b/ui/src/component/ToggleVisibility.tsx similarity index 69% rename from ui/src/component/ToggleVisibility.js rename to ui/src/component/ToggleVisibility.tsx index a75d52f..96a0e06 100644 --- a/ui/src/component/ToggleVisibility.js +++ b/ui/src/component/ToggleVisibility.tsx @@ -1,24 +1,22 @@ -import React, {Component} from 'react'; -import Typography from 'material-ui/Typography'; -import IconButton from 'material-ui/IconButton'; -import VisibilityOff from 'material-ui-icons/VisibilityOff'; import Visibility from 'material-ui-icons/Visibility'; -import PropTypes from 'prop-types'; +import VisibilityOff from 'material-ui-icons/VisibilityOff'; +import IconButton from 'material-ui/IconButton'; +import Typography from 'material-ui/Typography'; +import React, {Component} from 'react'; -class ToggleVisibility extends Component { - static propTypes = { - value: PropTypes.string.isRequired, - style: PropTypes.object, - }; +interface IProps { + value: string + style?: object +} - constructor() { - super(); - this.state = {visible: false}; - } +interface IState { + visible: boolean +} - toggleVisibility = () => this.setState({visible: !this.state.visible}); +class ToggleVisibility extends Component { + public state = {visible: false}; - render() { + public render() { const {value, style} = this.props; const text = this.state.visible ? value : '•••••••••••••••'; return ( @@ -32,6 +30,8 @@ class ToggleVisibility extends Component { ); } + + private toggleVisibility = () => this.setState({visible: !this.state.visible}); }