Use github releases for update notifications
This commit is contained in:
6
app/package-lock.json
generated
6
app/package-lock.json
generated
@@ -1429,6 +1429,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
|
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
|
||||||
},
|
},
|
||||||
|
"compare-versions": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"component-bind": {
|
"component-bind": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"@types/react": "^16.7.18",
|
"@types/react": "^16.7.18",
|
||||||
"@types/react-dom": "^16.0.11",
|
"@types/react-dom": "^16.0.11",
|
||||||
"@types/react-redux": "^6.0.12",
|
"@types/react-redux": "^6.0.12",
|
||||||
|
"compare-versions": "^3.4.0",
|
||||||
"css-loader": "^2.1.0",
|
"css-loader": "^2.1.0",
|
||||||
"electron-nucleus": "^1.11.0",
|
"electron-nucleus": "^1.11.0",
|
||||||
"electron-telemetry": "git+https://github.com/thomasnordquist/electron-telemetry.git",
|
"electron-telemetry": "git+https://github.com/thomasnordquist/electron-telemetry.git",
|
||||||
|
|||||||
@@ -10,16 +10,18 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
} from '@material-ui/core'
|
} from '@material-ui/core'
|
||||||
import { Theme, withStyles } from '@material-ui/core/styles'
|
import { Theme, withStyles } from '@material-ui/core/styles'
|
||||||
import { UpdateInfo, checkForUpdates, rendererEvents, updateAvailable } from '../../events'
|
import { green } from '@material-ui/core/colors'
|
||||||
import { green, red } from '@material-ui/core/colors'
|
|
||||||
|
|
||||||
import { AppState } from './reducers'
|
import { AppState } from './reducers'
|
||||||
import Close from '@material-ui/icons/Close'
|
import Close from '@material-ui/icons/Close'
|
||||||
import CloudDownload from '@material-ui/icons/CloudDownload'
|
import CloudDownload from '@material-ui/icons/CloudDownload'
|
||||||
import { UpdateFileInfo } from 'builder-util-runtime'
|
|
||||||
import { bindActionCreators } from 'redux'
|
import { bindActionCreators } from 'redux'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { updateNotifierActions } from './actions'
|
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 {
|
interface Props {
|
||||||
showUpdateNotification: boolean
|
showUpdateNotification: boolean
|
||||||
@@ -28,35 +30,55 @@ interface Props {
|
|||||||
actions: any
|
actions: any
|
||||||
}
|
}
|
||||||
|
|
||||||
class UpdateNotifier extends React.Component<Props, {}> {
|
interface GithubRelease {
|
||||||
private updateInfo?: UpdateInfo
|
url: string,
|
||||||
|
assets?: GithubAsset[]
|
||||||
|
published_at: string // "2019-01-25T20:14:39Z"
|
||||||
|
body_html: string
|
||||||
|
body: string
|
||||||
|
body_text: string
|
||||||
|
tag_name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GithubAsset {
|
||||||
|
id: number
|
||||||
|
node_id: string
|
||||||
|
url: string
|
||||||
|
name: string
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
newerVersions: GithubRelease[]
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdateNotifier extends React.Component<Props, State> {
|
||||||
constructor(props: any) {
|
constructor(props: any) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {
|
this.state = { newerVersions: [] }
|
||||||
selectedNode: undefined,
|
|
||||||
}
|
// window.compare = compareVersions
|
||||||
|
const ownVersion = '0.0.9' || electron.remote.app.getVersion()
|
||||||
|
this.fetchReleases().then((releases) => {
|
||||||
|
const newerVersions = releases
|
||||||
|
.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)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
private fetchReleases(): Promise<GithubRelease[]> {
|
||||||
rendererEvents.emit(checkForUpdates, undefined)
|
return axios.get('https://api.github.com/repos/thomasnordquist/mqtt-explorer/releases', {
|
||||||
rendererEvents.subscribe(updateAvailable, this.handleUpdate)
|
headers: {
|
||||||
}
|
accept: 'application/vnd.github.v3.full+json',
|
||||||
|
},
|
||||||
public componentWillUnmount() {
|
}).then((res) => {
|
||||||
rendererEvents.unsubscribeAll(updateAvailable)
|
return res.data
|
||||||
}
|
})
|
||||||
|
|
||||||
private fixUrl(url: string, version: string) {
|
|
||||||
if (!/^http/.test(url)) {
|
|
||||||
return `https://github.com/thomasnordquist/MQTT-Explorer/releases/download/v${version}/${url}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleUpdate = (updateInfo: UpdateInfo) => {
|
|
||||||
this.updateInfo = updateInfo
|
|
||||||
this.props.actions.showUpdateNotification(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onCloseNotification = (event: React.SyntheticEvent<any>, reason: string) => {
|
private onCloseNotification = (event: React.SyntheticEvent<any>, reason: string) => {
|
||||||
@@ -93,17 +115,18 @@ class UpdateNotifier extends React.Component<Props, {}> {
|
|||||||
vertical: 'top',
|
vertical: 'top',
|
||||||
horizontal: 'right',
|
horizontal: 'right',
|
||||||
}
|
}
|
||||||
|
console.log(this.state.newerVersions)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Snackbar
|
<Snackbar
|
||||||
anchorOrigin={snackbarAnchor}
|
anchorOrigin={snackbarAnchor}
|
||||||
open={this.props.showUpdateNotification}
|
open={this.props.showUpdateNotification}
|
||||||
autoHideDuration={7000}
|
autoHideDuration={12000}
|
||||||
onClose={this.onCloseNotification}
|
onClose={this.onCloseNotification}
|
||||||
>
|
>
|
||||||
<SnackbarContent
|
<SnackbarContent
|
||||||
className={this.props.classes.success}
|
className={this.props.classes.success}
|
||||||
message="Update available"
|
message={`${this.state.newerVersions.length} Update(s) available`}
|
||||||
action={this.notificationActions()}
|
action={this.notificationActions()}
|
||||||
/>
|
/>
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
@@ -113,7 +136,7 @@ class UpdateNotifier extends React.Component<Props, {}> {
|
|||||||
private notificationActions() {
|
private notificationActions() {
|
||||||
return [(
|
return [(
|
||||||
<Button key="undo" size="small" onClick={this.showDetails}>
|
<Button key="undo" size="small" onClick={this.showDetails}>
|
||||||
Download
|
Details
|
||||||
</Button>
|
</Button>
|
||||||
), (
|
), (
|
||||||
<IconButton
|
<IconButton
|
||||||
@@ -130,10 +153,14 @@ class UpdateNotifier extends React.Component<Props, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private renderUpdateDetails() {
|
private renderUpdateDetails() {
|
||||||
if (!this.updateInfo) {
|
const latestUpdate = this.state.newerVersions[0]
|
||||||
|
if (!latestUpdate) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const releaseNotes = (this.updateInfo.releaseNotes as string) || ''
|
const releaseNotes = this.state.newerVersions
|
||||||
|
.map(release => `<p><h3>${release.tag_name}</h3><p/><p>${release.body_html}</p>`)
|
||||||
|
.join('<hr />')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={this.props.showUpdateDetails}
|
open={this.props.showUpdateDetails}
|
||||||
@@ -141,31 +168,58 @@ class UpdateNotifier extends React.Component<Props, {}> {
|
|||||||
onClose={this.hideDetails}
|
onClose={this.hideDetails}
|
||||||
>
|
>
|
||||||
<Paper className={this.props.classes.root}>
|
<Paper className={this.props.classes.root}>
|
||||||
<Typography variant="h6" className={this.props.classes.title}>Version {this.updateInfo.version}</Typography>
|
<Typography variant="h6" className={this.props.classes.title}>Version {latestUpdate.tag_name}</Typography>
|
||||||
<Typography className={this.props.classes.title}>Changelog</Typography>
|
<Typography className={this.props.classes.title}>Changelog</Typography>
|
||||||
<div className={this.props.classes.releaseNotes} dangerouslySetInnerHTML={{ __html: releaseNotes }} />
|
<div className={this.props.classes.releaseNotes} dangerouslySetInnerHTML={{ __html: releaseNotes }} />
|
||||||
{this.renderDownloads(this.updateInfo)}
|
{this.renderDownloads()}
|
||||||
|
<Button
|
||||||
|
className={this.props.classes.download}
|
||||||
|
onClick={this.openGithub}
|
||||||
|
>
|
||||||
|
Github Page
|
||||||
|
</Button>
|
||||||
<Button className={this.props.classes.closeButton} color="secondary" onClick={this.hideDetails}>Close</Button>
|
<Button className={this.props.classes.closeButton} color="secondary" onClick={this.hideDetails}>Close</Button>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private urlToFilename(url: string) {
|
private openGithub = () => {
|
||||||
const parts = url.split('/')
|
this.openUrl('https://github.com/thomasnordquist/MQTT-Explorer')
|
||||||
|
|
||||||
return parts[parts.length - 1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderDownloads(updateInfo: UpdateInfo) {
|
private openUrl = (url: string) => {
|
||||||
return updateInfo.files
|
electron.shell.openExternal(url)
|
||||||
.map((file: UpdateFileInfo, index: number) => (
|
}
|
||||||
<div key={index}>
|
|
||||||
|
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 => (
|
||||||
|
<div>
|
||||||
<Button
|
<Button
|
||||||
className={this.props.classes.download}
|
className={this.props.classes.download}
|
||||||
href={this.fixUrl(file.url, updateInfo.version)}
|
onClick={() => this.openUrl(asset.url)}
|
||||||
>
|
>
|
||||||
<IconButton><CloudDownload /></IconButton>{this.urlToFilename(file.url)}
|
<CloudDownload /> {asset.name}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|||||||
Reference in New Issue
Block a user