import * as React from 'react' import { Button, IconButton, Modal, Paper, Snackbar, SnackbarContent, Typography, } from '@material-ui/core' import { Theme, withStyles } from '@material-ui/core/styles' import { green } from '@material-ui/core/colors' import { AppState } from './reducers' import Close from '@material-ui/icons/Close' import CloudDownload from '@material-ui/icons/CloudDownload' import { bindActionCreators } from 'redux' import { connect } from 'react-redux' import { updateNotifierActions } from './actions' import axios from 'axios' import * as compareVersions from 'compare-versions' import * as electron from 'electron' import * as os from 'os' interface Props { showUpdateNotification: boolean showUpdateDetails: boolean classes: any actions: any } interface GithubRelease { url: string, assets?: GithubAsset[] published_at: string // "2019-01-25T20:14:39Z" body_html: string body: string body_text: string tag_name: string prerelease: boolean } interface GithubAsset { id: number node_id: string url: string name: string label: string } interface State { newerVersions: GithubRelease[] } class UpdateNotifier extends React.Component { constructor(props: any) { super(props) this.state = { newerVersions: [] } const ownVersion = electron.remote.app.getVersion() this.fetchReleases().then((releases) => { const newerVersions = releases .filter(release => this.allowPrereleaseIfOwnVersionIsBeta(release, ownVersion)) .filter(release => compareVersions(release.tag_name, ownVersion) > 0) .sort((a, b) => compareVersions(b.tag_name, a.tag_name)) if (newerVersions.length > 0) { this.setState({ newerVersions }) this.props.actions.showUpdateNotification(true) } }) } private allowPrereleaseIfOwnVersionIsBeta(release: GithubRelease, ownVersion: string) { const ownVersionIsBeta = !/alpha|beta/.test(ownVersion) return ownVersionIsBeta || !release.prerelease } private async fetchReleases(): Promise { const res = await axios.get('https://api.github.com/repos/thomasnordquist/mqtt-explorer/releases', { headers: { accept: 'application/vnd.github.v3.full+json', }, }) return res.data as GithubRelease[] } private onCloseNotification = (event: React.SyntheticEvent, reason: string) => { if (reason === 'clickaway') { return } this.props.actions.showUpdateNotification(false) } private closeNotification = () => { this.props.actions.showUpdateNotification(false) } private showDetails = () => { this.props.actions.showUpdateNotification(false) this.props.actions.showUpdateDetails(true) } private hideDetails = () => { this.props.actions.showUpdateDetails(false) } private renderUpdateNotification() { const snackbarAnchor: any = { vertical: 'top', horizontal: 'right', } return ( ) } private notificationActions() { return [( ), ( ), ] } private renderUpdateDetails() { const latestUpdate = this.state.newerVersions[0] if (!latestUpdate) { return null } const releaseNotes = this.state.newerVersions .map(release => `

${release.tag_name}

${release.body_html}

`) .join('
') return ( Version {latestUpdate.tag_name} Changelog
{this.renderDownloads()} ) } private openGithub = () => { this.openUrl('https://github.com/thomasnordquist/MQTT-Explorer') } private openUrl = (url: string) => { electron.shell.openExternal(url) } private assetForCurrentPlatform(asset: GithubAsset) { let regex: RegExp if (os.platform() === 'darwin') { regex = /\.dmg$/ } else if (os.platform() === 'darwin') { regex = /\.exe$/ } else { regex = /\.AppImage$/ } return regex.test(asset.name) } private renderDownloads() { const latestUpdate = this.state.newerVersions[0] if (!latestUpdate || !latestUpdate.assets) { return null } return latestUpdate.assets .filter(this.assetForCurrentPlatform) .map(asset => (
)) } public render() { return (
{this.renderUpdateNotification()} {this.renderUpdateDetails()}
) } } const styles = (theme: Theme) => ({ success: { backgroundColor: green[600], color: theme.typography.button.color, }, close: { padding: '4px', }, root: { minWidth: '350px', maxWidth: '500px', backgroundColor: theme.palette.background.default, margin: '20vh auto auto auto', padding: theme.spacing(2), outline: 'none', }, title: { color: theme.palette.text.primary, }, releaseNotes: { overflow: 'auto scroll', color: theme.palette.text.secondary, backgroundColor: 'rgba(60, 60, 60, 0.6)', maxHeight: '28vh', }, paper: { padding: theme.spacing(2), color: theme.palette.text.secondary, }, download: { width: '100%', }, closeButton: { display: 'block', margin: '0 0 0 auto', }, }) const mapStateToProps = (state: AppState) => { return { showUpdateNotification: state.globalState.get('showUpdateNotification'), showUpdateDetails: state.globalState.get('showUpdateDetails'), } } const mapDispatchToProps = (dispatch: any) => { return { actions: bindActionCreators(updateNotifierActions, dispatch), } } export default withStyles(styles)(connect(mapStateToProps, mapDispatchToProps)(UpdateNotifier))