From b0d1ddf1ac63ae6ad95d43790102c5a510e5c0da Mon Sep 17 00:00:00 2001 From: Preston Date: Sun, 20 Sep 2020 14:22:39 -0400 Subject: [PATCH] Add notifications for new blog posts and app updates --- package-lock.json | 21 +++- package.json | 1 + src/renderer/App.css | 18 ++- src/renderer/App.js | 105 +++++++++++++++++- src/renderer/App.vue | 33 ++++++ .../ft-notification-banner.css | 27 +++++ .../ft-notification-banner.js | 26 +++++ .../ft-notification-banner.vue | 24 ++++ .../components/ft-prompt/ft-prompt.css | 6 +- .../general-settings/general-settings.js | 4 + .../general-settings/general-settings.vue | 48 +++++--- .../components/playlist-info/playlist-info.js | 6 +- src/renderer/components/top-nav/top-nav.js | 2 +- src/renderer/store/modules/settings.js | 19 ++++ src/renderer/store/modules/utils.js | 9 ++ static/locales/en-US.yaml | 6 + 16 files changed, 324 insertions(+), 31 deletions(-) create mode 100644 src/renderer/components/ft-notification-banner/ft-notification-banner.css create mode 100644 src/renderer/components/ft-notification-banner/ft-notification-banner.js create mode 100644 src/renderer/components/ft-notification-banner/ft-notification-banner.vue diff --git a/package-lock.json b/package-lock.json index 56e0bb74..2bb4649b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3186,8 +3186,7 @@ "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "accepts": { "version": "1.3.7", @@ -13583,6 +13582,24 @@ "object-visit": "^1.0.0" } }, + "markdown": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/markdown/-/markdown-0.5.0.tgz", + "integrity": "sha1-KCBbVlqK51kt4gdGPWY33BgnIrI=", + "requires": { + "nopt": "~2.1.1" + }, + "dependencies": { + "nopt": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz", + "integrity": "sha1-bMzZd7gBMqB3MdbozljCyDA8+a8=", + "requires": { + "abbrev": "1" + } + } + } + }, "matcher": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", diff --git a/package.json b/package.json index 9ca316c5..eba82f1d 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "lodash.debounce": "^4.0.8", "lodash.isequal": "^4.5.0", "lodash.uniqwith": "^4.5.0", + "markdown": "^0.5.0", "material-design-icons": "^3.0.1", "mediaelement": "^4.2.16", "nedb": "^1.8.0", diff --git a/src/renderer/App.css b/src/renderer/App.css index d80c9ec8..6adf7ac0 100644 --- a/src/renderer/App.css +++ b/src/renderer/App.css @@ -24,12 +24,16 @@ body { } .banner { - margin-top: 70px; - margin-bottom: -65px; + width: 85%; +} + +.flexBox { + margin-top: 60px; + margin-bottom: -75px; } .fade-enter-active, .fade-leave-active { - transition: opacity .2s; + transition: opacity .15s; } .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ { opacity: 0; @@ -42,7 +46,11 @@ body { } .banner { - margin-top: 70px; - margin-bottom: -65px; + width: 90%; + } + + .flexBox { + margin-top: 60px; + margin-bottom: -75px; } } diff --git a/src/renderer/App.js b/src/renderer/App.js index daafe54b..fbfbe4d7 100644 --- a/src/renderer/App.js +++ b/src/renderer/App.js @@ -1,10 +1,16 @@ import Vue from 'vue' import { ObserveVisibility } from 'vue-observe-visibility' +import FtFlexBox from './components/ft-flex-box/ft-flex-box.vue' import TopNav from './components/top-nav/top-nav.vue' import SideNav from './components/side-nav/side-nav.vue' +import FtNotificationBanner from './components/ft-notification-banner/ft-notification-banner.vue' +import FtPrompt from './components/ft-prompt/ft-prompt.vue' +import FtButton from './components/ft-button/ft-button.vue' import FtToast from './components/ft-toast/ft-toast.vue' import FtProgressBar from './components/ft-progress-bar/ft-progress-bar.vue' import $ from 'jquery' +import { markdown } from 'markdown' +import Parser from 'rss-parser' let useElectron let shell @@ -22,14 +28,26 @@ if (window && window.process && window.process.type === 'renderer') { export default Vue.extend({ name: 'App', components: { + FtFlexBox, TopNav, SideNav, + FtNotificationBanner, + FtPrompt, + FtButton, FtToast, FtProgressBar }, data: function () { return { - hideOutlines: true + hideOutlines: true, + showUpdatesBanner: false, + showBlogBanner: false, + showReleaseNotes: false, + updateBannerMessage: '', + blogBannerMessage: '', + latestBlogUrl: '', + updateChangelog: '', + changeLogTitle: '' } }, computed: { @@ -41,6 +59,12 @@ export default Vue.extend({ }, isRightAligned: function () { return this.$i18n.locale === 'ar' + }, + checkForUpdates: function () { + return this.$store.getters.getCheckForUpdates + }, + checkForBlogPosts: function () { + return this.$store.getters.getCheckForBlogPosts } }, mounted: function () { @@ -56,6 +80,11 @@ export default Vue.extend({ this.activateKeyboardShortcuts() this.openAllLinksExternally() } + + setTimeout(() => { + this.checkForNewUpdates() + this.checkForNewBlogPosts() + }, 500) }, methods: { checkLocale: function () { @@ -105,6 +134,80 @@ export default Vue.extend({ localStorage.setItem('secColor', theme.secColor) }, + checkForNewUpdates: function () { + if (this.checkForUpdates) { + const { version } = require('../../package.json') + const requestUrl = 'https://api.github.com/repos/freetubeapp/freetube-vue/releases' + + $.getJSON(requestUrl, (response) => { + const tagName = response[0].tag_name + const versionNumber = tagName.replace('v', '').replace('-beta', '') + this.updateChangelog = markdown.toHTML(response[0].body) + this.changeLogTitle = response[0].name + + const message = this.$t('Version $ is now available! Click for more details') + this.updateBannerMessage = message.replace('$', versionNumber) + if (version < versionNumber) { + this.showUpdatesBanner = true + } + }).fail((xhr, textStatus, error) => { + console.log(xhr) + console.log(textStatus) + console.log(requestUrl) + console.log(error) + }) + } + }, + + checkForNewBlogPosts: function () { + if (this.checkForBlogPosts) { + const parser = new Parser() + const feedUrl = 'https://write.as/freetube/feed/' + let lastAppWasRunning = localStorage.getItem('lastAppWasRunning') + + if (lastAppWasRunning !== null) { + lastAppWasRunning = new Date(lastAppWasRunning) + } + + parser.parseURL(feedUrl).then((response) => { + const latestBlog = response.items[0] + const latestPubDate = new Date(latestBlog.pubDate) + + if (lastAppWasRunning === null || latestPubDate > lastAppWasRunning) { + const message = this.$t('A new blog is now available, $. Click to view more') + this.blogBannerMessage = message.replace('$', latestBlog.title) + this.latestBlogUrl = latestBlog.link + this.showBlogBanner = true + } + + localStorage.setItem('lastAppWasRunning', new Date()) + }) + } + }, + + handleUpdateBannerClick: function (response) { + if (response !== false) { + this.showReleaseNotes = true + } else { + this.showUpdatesBanner = false + } + }, + + handleNewBlogBannerClick: function (response) { + if (response) { + shell.openExternal(this.latestBlogUrl) + } + + this.showBlogBanner = false + }, + + openDownloadsPage: function () { + const url = 'https://freetubeapp.io#download' + shell.openExternal(url) + this.showReleaseNotes = false + this.showUpdatesBanner = false + }, + activateKeyboardShortcuts: function () { $(document).on('keydown', this.handleKeyboardShortcuts) $(document).on('mousedown', () => { diff --git a/src/renderer/App.vue b/src/renderer/App.vue index 3e0e6f78..82bb9c0b 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -8,6 +8,24 @@ > + + + +

+ {{ changeLogTitle }} +

+ + + + +
+
+
+ +

+ {{ message }} +

+
+
+ +
+ + +