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: