From 25d954f9907eda3940954f01d90973693121a563 Mon Sep 17 00:00:00 2001 From: Emma Date: Wed, 21 Sep 2022 03:00:21 -0400 Subject: [PATCH] Cleaning up calls to the Clipboard API (#2597) * Added a copyToClipboard function to utils This should make it easier to handle errors which result from the clipboard API (which are more likely in web builds). This should also make it easier to handle copying to clipboard in cordova builds by abstracting the platform specific code out of the views and moving it all into one function. * Moved the $t function out of utils * Removing locale snippets I was using and am not now * Added function comment to copyToClipboard * Adding some missing references * Adding an additional check * Re-reviewing my changes, I found a mistake * Update src/renderer/store/modules/utils.js Co-authored-by: absidue <48293849+absidue@users.noreply.github.com> * Update static/locales/en-US.yaml Co-authored-by: ChunkyProgrammer <78101139+ChunkyProgrammer@users.noreply.github.com> * Reverting the language back to what it was previously * Switching to using i18n.t() instead of handling the translations myself. Also, it looks like eslint removed a tab. Co-authored-by: absidue <48293849+absidue@users.noreply.github.com> Co-authored-by: ChunkyProgrammer <78101139+ChunkyProgrammer@users.noreply.github.com> --- .../components/data-settings/data-settings.js | 7 ++-- .../components/ft-list-video/ft-list-video.js | 28 ++++--------- .../ft-share-button/ft-share-button.js | 26 +++---------- .../components/playlist-info/playlist-info.js | 13 ++----- .../watch-video-comments.js | 11 +++--- .../watch-video-playlist.js | 7 ++-- src/renderer/store/modules/utils.js | 39 +++++++++++++++++++ src/renderer/views/Channel/Channel.js | 27 ++++++------- src/renderer/views/Search/Search.js | 7 ++-- .../views/Subscriptions/Subscriptions.js | 11 +++--- src/renderer/views/Trending/Trending.js | 7 ++-- src/renderer/views/Watch/Watch.js | 9 +++-- static/locales/en-US.yaml | 3 ++ 13 files changed, 106 insertions(+), 89 deletions(-) diff --git a/src/renderer/components/data-settings/data-settings.js b/src/renderer/components/data-settings/data-settings.js index 60531c5d..a8596af3 100644 --- a/src/renderer/components/data-settings/data-settings.js +++ b/src/renderer/components/data-settings/data-settings.js @@ -1291,7 +1291,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err.responseJSON.error}`, time: 10000, action: () => { - navigator.clipboard.writeText(err.responseJSON.error) + this.copyToClipboard({ content: err.responseJSON.error }) } }) @@ -1318,7 +1318,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) @@ -1348,7 +1348,8 @@ export default Vue.extend({ 'showSaveDialog', 'getUserDataPath', 'addPlaylist', - 'addVideo' + 'addVideo', + 'copyToClipboard' ]), ...mapMutations([ diff --git a/src/renderer/components/ft-list-video/ft-list-video.js b/src/renderer/components/ft-list-video/ft-list-video.js index 6b43128a..9eae1d85 100644 --- a/src/renderer/components/ft-list-video/ft-list-video.js +++ b/src/renderer/components/ft-list-video/ft-list-video.js @@ -303,46 +303,31 @@ export default Vue.extend({ } break case 'copyYoutube': - navigator.clipboard.writeText(this.youtubeShareUrl) - this.showToast({ - message: this.$t('Share.YouTube URL copied to clipboard') - }) + this.copyToClipboard({ content: this.youtubeShareUrl, messageOnSuccess: this.$t('Share.YouTube URL copied to clipboard') }) break case 'openYoutube': this.openExternalLink(this.youtubeUrl) break case 'copyYoutubeEmbed': - navigator.clipboard.writeText(this.youtubeEmbedUrl) - this.showToast({ - message: this.$t('Share.YouTube Embed URL copied to clipboard') - }) + this.copyToClipboard({ content: this.youtubeEmbedUrl, messageOnSuccess: this.$t('Share.YouTube Embed URL copied to clipboard') }) break case 'openYoutubeEmbed': this.openExternalLink(this.youtubeEmbedUrl) break case 'copyInvidious': - navigator.clipboard.writeText(this.invidiousUrl) - this.showToast({ - message: this.$t('Share.Invidious URL copied to clipboard') - }) + this.copyToClipboard({ content: this.invidiousUrl, messageOnSuccess: this.$t('Share.Invidious URL copied to clipboard') }) break case 'openInvidious': this.openExternalLink(this.invidiousUrl) break case 'copyYoutubeChannel': - navigator.clipboard.writeText(this.youtubeChannelUrl) - this.showToast({ - message: this.$t('Share.YouTube Channel URL copied to clipboard') - }) + this.copyToClipboard({ content: this.youtubeChannelUrl, messageOnSuccess: this.$t('Share.YouTube Channel URL copied to clipboard') }) break case 'openYoutubeChannel': this.openExternalLink(this.youtubeChannelUrl) break case 'copyInvidiousChannel': - navigator.clipboard.writeText(this.invidiousChannelUrl) - this.showToast({ - message: this.$t('Share.Invidious Channel URL copied to clipboard') - }) + this.copyToClipboard({ content: this.invidiousChannelUrl, messageOnSuccess: this.$t('Share.Invidious Channel URL copied to clipboard') }) break case 'openInvidiousChannel': this.openExternalLink(this.invidiousChannelUrl) @@ -540,7 +525,8 @@ export default Vue.extend({ 'removeFromHistory', 'addVideo', 'removeVideo', - 'openExternalLink' + 'openExternalLink', + 'copyToClipboard' ]) } }) diff --git a/src/renderer/components/ft-share-button/ft-share-button.js b/src/renderer/components/ft-share-button/ft-share-button.js index 0efc7f15..2b4303e9 100644 --- a/src/renderer/components/ft-share-button/ft-share-button.js +++ b/src/renderer/components/ft-share-button/ft-share-button.js @@ -76,9 +76,6 @@ export default Vue.extend({ } }, methods: { - copy(text) { - navigator.clipboard.writeText(text) - }, openInvidious() { this.openExternalLink(this.getFinalUrl(this.invidiousURL)) @@ -86,10 +83,7 @@ export default Vue.extend({ }, copyInvidious() { - this.showToast({ - message: this.$t('Share.Invidious URL copied to clipboard') - }) - this.copy(this.getFinalUrl(this.invidiousURL)) + this.copyToClipboard({ content: this.getFinalUrl(this.invidiousURL), messageOnSuccess: this.$t('Share.Invidious URL copied to clipboard') }) this.$refs.iconButton.focusOut() }, @@ -99,10 +93,7 @@ export default Vue.extend({ }, copyYoutube() { - this.showToast({ - message: this.$t('Share.YouTube URL copied to clipboard') - }) - this.copy(this.getFinalUrl(this.youtubeShareURL)) + this.copyToClipboard({ content: this.getFinalUrl(this.youtubeShareURL), messageOnSuccess: this.$t('Share.YouTube URL copied to clipboard') }) this.$refs.iconButton.focusOut() }, @@ -112,10 +103,7 @@ export default Vue.extend({ }, copyYoutubeEmbed() { - this.showToast({ - message: this.$t('Share.YouTube Embed URL copied to clipboard') - }) - this.copy(this.getFinalUrl(this.youtubeEmbedURL)) + this.copyToClipboard({ content: this.getFinalUrl(this.youtubeEmbedURL), messageOnSuccess: this.$t('Share.YouTube Embed URL copied to clipboard') }) this.$refs.iconButton.focusOut() }, @@ -125,10 +113,7 @@ export default Vue.extend({ }, copyInvidiousEmbed() { - this.showToast({ - message: this.$t('Share.Invidious Embed URL copied to clipboard') - }) - this.copy(this.getFinalUrl(this.invidiousEmbedURL)) + this.copyToClipboard({ content: this.getFinalUrl(this.invidiousEmbedURL), messageOnSuccess: this.$t('Share.Invidious Embed URL copied to clipboard') }) this.$refs.iconButton.focusOut() }, @@ -145,7 +130,8 @@ export default Vue.extend({ ...mapActions([ 'showToast', - 'openExternalLink' + 'openExternalLink', + 'copyToClipboard' ]) } }) diff --git a/src/renderer/components/playlist-info/playlist-info.js b/src/renderer/components/playlist-info/playlist-info.js index 2243b15b..df905d51 100644 --- a/src/renderer/components/playlist-info/playlist-info.js +++ b/src/renderer/components/playlist-info/playlist-info.js @@ -111,19 +111,13 @@ export default Vue.extend({ switch (method) { case 'copyYoutube': - navigator.clipboard.writeText(youtubeUrl) - this.showToast({ - message: this.$t('Share.YouTube URL copied to clipboard') - }) + this.copyToClipboard({ content: youtubeUrl, messageOnSuccess: this.$t('Share.YouTube URL copied to clipboard') }) break case 'openYoutube': this.openExternalLink(youtubeUrl) break case 'copyInvidious': - navigator.clipboard.writeText(invidiousUrl) - this.showToast({ - message: this.$t('Share.Invidious URL copied to clipboard') - }) + this.copyToClipboard({ content: invidiousUrl, messageOnSuccess: this.$t('Share.Invidious URL copied to clipboard') }) break case 'openInvidious': this.openExternalLink(invidiousUrl) @@ -150,7 +144,8 @@ export default Vue.extend({ ...mapActions([ 'showToast', - 'openExternalLink' + 'openExternalLink', + 'copyToClipboard' ]) } }) diff --git a/src/renderer/components/watch-video-comments/watch-video-comments.js b/src/renderer/components/watch-video-comments/watch-video-comments.js index 29cc2b1e..cf06c3e3 100644 --- a/src/renderer/components/watch-video-comments/watch-video-comments.js +++ b/src/renderer/components/watch-video-comments/watch-video-comments.js @@ -188,7 +188,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) if (this.backendFallback && this.backendPreference === 'local') { @@ -223,7 +223,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) if (this.backendFallback && this.backendPreference === 'local') { @@ -339,7 +339,7 @@ export default Vue.extend({ message: `${errorMessage}: ${xhr.responseText}`, time: 10000, action: () => { - navigator.clipboard.writeText(xhr.responseText) + this.copyToClipboard({ content: xhr.responseText }) } }) if (this.backendFallback && this.backendPreference === 'invidious') { @@ -396,7 +396,7 @@ export default Vue.extend({ message: `${errorMessage}: ${xhr.responseText}`, time: 10000, action: () => { - navigator.clipboard.writeText(xhr.responseText) + this.copyToClipboard({ content: xhr.responseText }) } }) this.isLoading = false @@ -410,7 +410,8 @@ export default Vue.extend({ ...mapActions([ 'showToast', 'toLocalePublicationString', - 'invidiousAPICall' + 'invidiousAPICall', + 'copyToClipboard' ]) } }) diff --git a/src/renderer/components/watch-video-playlist/watch-video-playlist.js b/src/renderer/components/watch-video-playlist/watch-video-playlist.js index 0aaf7d5e..91f3a6af 100644 --- a/src/renderer/components/watch-video-playlist/watch-video-playlist.js +++ b/src/renderer/components/watch-video-playlist/watch-video-playlist.js @@ -313,7 +313,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) if (this.backendPreference === 'local' && this.backendFallback) { @@ -354,7 +354,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) if (this.backendPreference === 'invidious' && this.backendFallback) { @@ -392,7 +392,8 @@ export default Vue.extend({ ...mapActions([ 'showToast', 'ytGetPlaylistInfo', - 'invidiousGetPlaylistInfo' + 'invidiousGetPlaylistInfo', + 'copyToClipboard' ]) } }) diff --git a/src/renderer/store/modules/utils.js b/src/renderer/store/modules/utils.js index 7c1b8620..caffd10a 100644 --- a/src/renderer/store/modules/utils.js +++ b/src/renderer/store/modules/utils.js @@ -246,6 +246,45 @@ const actions = { return filenameNew }, + /** + * This writes to the clipboard. If an error occurs during the copy, + * a toast with the error is shown. If the copy is successful and + * there is a success message, a toast with that message is shown. + * @param {string} content the content to be copied to the clipboard + * @param {string} messageOnSuccess the message to be displayed as a toast when the copy succeeds (optional) + * @param {string} messageOnError the message to be displayed as a toast when the copy fails (optional) + */ + async copyToClipboard ({ dispatch }, { content, messageOnSuccess, messageOnError }) { + if (navigator.clipboard !== undefined && window.isSecureContext) { + try { + await navigator.clipboard.writeText(content) + if (messageOnSuccess !== undefined) { + dispatch('showToast', { + message: messageOnSuccess + }) + } + } catch (error) { + console.error(`Failed to copy ${content} to clipboard`, error) + if (messageOnError !== undefined) { + dispatch('showToast', { + message: `${messageOnError}: ${error}`, + time: 5000 + }) + } else { + dispatch('showToast', { + message: `${i18n.t('Clipboard.Copy failed')}: ${error}`, + time: 5000 + }) + } + } + } else { + dispatch('showToast', { + message: i18n.t('Clipboard.Cannot access clipboard without a secure connection'), + time: 5000 + }) + } + }, + async downloadMedia({ rootState, dispatch }, { url, title, extension, fallingBackPath }) { const fileName = `${await dispatch('replaceFilenameForbiddenChars', title)}.${extension}` const locale = i18n._vm.locale diff --git a/src/renderer/views/Channel/Channel.js b/src/renderer/views/Channel/Channel.js index 9bc14d82..07df2aca 100644 --- a/src/renderer/views/Channel/Channel.js +++ b/src/renderer/views/Channel/Channel.js @@ -310,7 +310,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) if (this.backendPreference === 'local' && this.backendFallback) { @@ -342,7 +342,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) if (this.backendPreference === 'local' && this.backendFallback) { @@ -367,7 +367,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) }) @@ -422,7 +422,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err.responseJSON.error}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err.responseJSON.error }) } }) this.isLoading = false @@ -450,7 +450,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) }) @@ -477,7 +477,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) if (this.backendPreference === 'local' && this.backendFallback) { @@ -503,7 +503,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) }) @@ -529,7 +529,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err.responseJSON.error}`, time: 10000, action: () => { - navigator.clipboard.writeText(err.responseJSON.error) + this.copyToClipboard({ content: err.responseJSON.error }) } }) if (this.backendPreference === 'invidious' && this.backendFallback) { @@ -572,7 +572,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err.responseJSON.error}`, time: 10000, action: () => { - navigator.clipboard.writeText(err.responseJSON.error) + this.copyToClipboard({ content: err.responseJSON.error }) } }) if (this.backendPreference === 'invidious' && this.backendFallback) { @@ -738,7 +738,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) if (this.backendPreference === 'local' && this.backendFallback) { @@ -763,7 +763,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) }) @@ -791,7 +791,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) if (this.backendPreference === 'invidious' && this.backendFallback) { @@ -810,7 +810,8 @@ export default Vue.extend({ 'updateProfile', 'invidiousGetChannelInfo', 'invidiousAPICall', - 'updateSubscriptionDetails' + 'updateSubscriptionDetails', + 'copyToClipboard' ]) } }) diff --git a/src/renderer/views/Search/Search.js b/src/renderer/views/Search/Search.js index 777e8de1..a7196351 100644 --- a/src/renderer/views/Search/Search.js +++ b/src/renderer/views/Search/Search.js @@ -207,7 +207,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) if (this.backendPreference === 'local' && this.backendFallback) { @@ -279,7 +279,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) if (this.backendPreference === 'invidious' && this.backendFallback) { @@ -345,7 +345,8 @@ export default Vue.extend({ ...mapActions([ 'showToast', 'ytSearch', - 'invidiousAPICall' + 'invidiousAPICall', + 'copyToClipboard' ]) } }) diff --git a/src/renderer/views/Subscriptions/Subscriptions.js b/src/renderer/views/Subscriptions/Subscriptions.js index c00fc97d..62f54f5f 100644 --- a/src/renderer/views/Subscriptions/Subscriptions.js +++ b/src/renderer/views/Subscriptions/Subscriptions.js @@ -260,7 +260,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) switch (failedAttempts) { @@ -323,7 +323,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) switch (failedAttempts) { @@ -371,7 +371,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err.responseText}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err.responseText }) } }) switch (failedAttempts) { @@ -422,7 +422,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) if (err.toString().match(/500/)) { @@ -465,7 +465,8 @@ export default Vue.extend({ 'updateShowProgressBar', 'updateProfileSubscriptions', 'updateAllSubscriptionsList', - 'calculatePublishedDate' + 'calculatePublishedDate', + 'copyToClipboard' ]), ...mapMutations([ diff --git a/src/renderer/views/Trending/Trending.js b/src/renderer/views/Trending/Trending.js index 7e89bc33..d3c13c70 100644 --- a/src/renderer/views/Trending/Trending.js +++ b/src/renderer/views/Trending/Trending.js @@ -132,7 +132,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) if (this.backendPreference === 'local' && this.backendFallback) { @@ -191,7 +191,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err.responseText}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err.responseText }) } }) @@ -208,7 +208,8 @@ export default Vue.extend({ ...mapActions([ 'showToast', - 'invidiousAPICall' + 'invidiousAPICall', + 'copyToClipboard' ]) } }) diff --git a/src/renderer/views/Watch/Watch.js b/src/renderer/views/Watch/Watch.js index 9256906a..38b007c4 100644 --- a/src/renderer/views/Watch/Watch.js +++ b/src/renderer/views/Watch/Watch.js @@ -593,7 +593,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) console.log(err) @@ -778,7 +778,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err.responseText}`, time: 10000, action: () => { - navigator.clipboard.writeText(err.responseText) + this.copyToClipboard({ content: err.responseText }) } }) console.log(err) @@ -949,7 +949,7 @@ export default Vue.extend({ message: `${errorMessage}: ${err}`, time: 10000, action: () => { - navigator.clipboard.writeText(err) + this.copyToClipboard({ content: err }) } }) console.log(err) @@ -1409,7 +1409,8 @@ export default Vue.extend({ 'getUserDataPath', 'ytGetVideoInformation', 'invidiousGetVideoInformation', - 'updateSubscriptionDetails' + 'updateSubscriptionDetails', + 'copyToClipboard' ]) } }) diff --git a/static/locales/en-US.yaml b/static/locales/en-US.yaml index f3dd927e..f106a311 100644 --- a/static/locales/en-US.yaml +++ b/static/locales/en-US.yaml @@ -688,6 +688,9 @@ Share: YouTube URL copied to clipboard: YouTube URL copied to clipboard YouTube Embed URL copied to clipboard: YouTube Embed URL copied to clipboard YouTube Channel URL copied to clipboard: YouTube Channel URL copied to clipboard +Clipboard: + Copy failed: Copy to clipboard failed + Cannot access clipboard without a secure connection: Cannot access clipboard without a secure connection Mini Player: Mini Player Comments: