From bd0e8518bea69f5cc9f482bd19f4e879a485bbc1 Mon Sep 17 00:00:00 2001 From: advplyr Date: Wed, 15 Sep 2021 17:59:38 -0500 Subject: [PATCH] Add version checker --- client/components/app/Appbar.vue | 5 +++ client/layouts/default.vue | 38 +++++++++++++++++++- client/package.json | 2 +- client/plugins/toast.js | 2 +- client/plugins/version.js | 59 ++++++++++++++++++++++++++++++++ client/store/index.js | 17 ++++++++- package.json | 2 +- 7 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 client/plugins/version.js diff --git a/client/components/app/Appbar.vue b/client/components/app/Appbar.vue index e8b441a6..5a12c88c 100644 --- a/client/components/app/Appbar.vue +++ b/client/components/app/Appbar.vue @@ -11,6 +11,11 @@
+ + upload diff --git a/client/layouts/default.vue b/client/layouts/default.vue index 1d3db799..8d1631d2 100644 --- a/client/layouts/default.vue +++ b/client/layouts/default.vue @@ -232,10 +232,40 @@ export default { this.socket.on('download_failed', this.downloadFailed) this.socket.on('download_killed', this.downloadKilled) this.socket.on('download_expired', this.downloadExpired) + }, + showUpdateToast(versionData) { + var ignoreVersion = localStorage.getItem('ignoreVersion') + var latestVersion = versionData.latestVersion + + if (!ignoreVersion || ignoreVersion !== latestVersion) { + this.$toast.info(`Update is available!\nCheck release notes for v${versionData.latestVersion}`, { + position: 'top-center', + toastClassName: 'cursor-pointer', + bodyClassName: 'custom-class-1', + timeout: 20000, + closeOnClick: false, + draggable: false, + hideProgressBar: false, + onClick: () => { + window.open(versionData.githubTagUrl, '_blank') + }, + onClose: () => { + localStorage.setItem('ignoreVersion', versionData.latestVersion) + } + }) + } else { + console.warn(`Update is available but user chose to dismiss it! v${versionData.latestVersion}`) + } } }, mounted() { this.initializeSocket() + this.$store + .dispatch('checkForUpdate') + .then((res) => { + if (res && res.hasUpdate) this.showUpdateToast(res) + }) + .catch((err) => console.error(err)) if (this.$route.query.error) { this.$toast.error(this.$route.query.error) @@ -243,4 +273,10 @@ export default { } } } - \ No newline at end of file + + + \ No newline at end of file diff --git a/client/package.json b/client/package.json index dc1154cb..008af1d8 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf-client", - "version": "1.1.8", + "version": "1.1.9", "description": "Audiobook manager and player", "main": "index.js", "scripts": { diff --git a/client/plugins/toast.js b/client/plugins/toast.js index 24192c9d..498b00e7 100644 --- a/client/plugins/toast.js +++ b/client/plugins/toast.js @@ -7,4 +7,4 @@ const options = { draggable: false } -Vue.use(Toast, options) \ No newline at end of file +Vue.use(Toast, options) diff --git a/client/plugins/version.js b/client/plugins/version.js new file mode 100644 index 00000000..c8518d26 --- /dev/null +++ b/client/plugins/version.js @@ -0,0 +1,59 @@ +import packagejson from '../package.json' +import axios from 'axios' + +function parseSemver(ver) { + if (!ver) return null + var groups = ver.match(/^v((([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)$/) + if (groups && groups.length > 6) { + var total = Number(groups[3]) * 100 + Number(groups[4]) * 10 + Number(groups[5]) + if (isNaN(total)) { + console.warn('Invalid version total', groups[3], groups[4], groups[5]) + return null + } + return { + total, + version: groups[2], + major: Number(groups[3]), + minor: Number(groups[4]), + patch: Number(groups[5]), + preRelease: groups[6] || null + } + } else { + console.warn('Invalid semver string', ver) + } + return null +} +export async function checkForUpdate() { + if (!packagejson.version) { + return + } + var currVerObj = parseSemver('v' + packagejson.version) + if (!currVerObj) { + console.error('Invalid version', packagejson.version) + return + } + var largestVer = null + await axios.get(`https://api.github.com/repos/advplyr/audiobookshelf/tags`).then((res) => { + var tags = res.data + if (tags && tags.length) { + tags.forEach((tag) => { + var verObj = parseSemver(tag.name) + if (verObj) { + if (!largestVer || largestVer.total < verObj.total) { + largestVer = verObj + } + } + }) + } + }) + if (!largestVer) { + console.error('No valid version tags to compare with') + return + } + return { + hasUpdate: largestVer.total > currVerObj.total, + latestVersion: largestVer.version, + githubTagUrl: `https://github.com/advplyr/audiobookshelf/releases/tag/v${largestVer.version}`, + currentVersion: currVerObj.version + } +} \ No newline at end of file diff --git a/client/store/index.js b/client/store/index.js index 688ad59f..5b193f8b 100644 --- a/client/store/index.js +++ b/client/store/index.js @@ -1,6 +1,7 @@ -import Vue from 'vue' +import { checkForUpdate } from '@/plugins/version' export const state = () => ({ + versionData: null, serverSettings: null, streamAudiobook: null, editModalTab: 'details', @@ -39,10 +40,24 @@ export const actions = { console.error('Failed to update server settings', error) return false }) + }, + checkForUpdate({ commit }) { + return checkForUpdate() + .then((res) => { + commit('setVersionData', res) + return res + }) + .catch((error) => { + console.error('Update check failed', error) + return false + }) } } export const mutations = { + setVersionData(state, versionData) { + state.versionData = versionData + }, setServerSettings(state, settings) { state.serverSettings = settings }, diff --git a/package.json b/package.json index 5580c6dc..80a763f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf", - "version": "1.1.8", + "version": "1.1.9", "description": "Self-hosted audiobook server for managing and playing audiobooks.", "main": "index.js", "scripts": {