Skip to content

Commit

Permalink
Feature/8134 - copy to clipboard
Browse files Browse the repository at this point in the history
  • Loading branch information
pascalwengerter committed Dec 26, 2022
1 parent c2ec30b commit 1ef49a1
Show file tree
Hide file tree
Showing 13 changed files with 57 additions and 36 deletions.
6 changes: 6 additions & 0 deletions changelog/unreleased/enhancement-clipboard-copy
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enhancement: Make clipboard copy available to more browsers

We have added more functionality for copying (e.g. links) to the user's clipboard. Standard browser API (if available) now takes precedence over the already used 3rd-party library.

https://github.com/owncloud/web/pull/8136
https://github.com/owncloud/web/issues/8134
1 change: 0 additions & 1 deletion packages/web-app-files/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"description": "ownCloud web files",
"license": "AGPL-3.0",
"dependencies": {
"copy-to-clipboard": "^3.3.1",
"mark.js": "^8.11.1"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ import {
useUserContext
} from 'web-pkg/src/composables'
import { getIndicators } from '../../../helpers/statusIndicators'
import copyToClipboard from 'copy-to-clipboard'
import { clipboardCopy } from 'web-pkg/src/helpers'
import { encodePath } from 'web-pkg/src/utils'
import { formatDateFromHTTP, formatFileSize } from 'web-pkg/src/helpers'
import { eventBus } from 'web-pkg/src/services/eventBus'
Expand Down Expand Up @@ -469,7 +469,7 @@ export default defineComponent({
this.loading = false
},
copyEosPathToClipboard() {
copyToClipboard(this.file.path)
clipboardCopy(this.file.path)
this.copiedEos = true
this.clipboardSuccessHandler()
this.showMessage({
Expand All @@ -478,7 +478,7 @@ export default defineComponent({
})
},
copyDirectLinkToClipboard() {
copyToClipboard(this.directLink)
clipboardCopy(this.directLink)
this.copiedDirect = true
this.clipboardSuccessHandler()
this.showMessage({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
</template>

<script>
import { mapActions } from 'vuex'
import copyToClipboard from 'copy-to-clipboard'
import { unref } from 'vue'
import { mapActions } from 'vuex'
import { clipboardCopy } from 'web-pkg/src/helpers'
export default {
name: 'PrivateLinkItem',
Expand All @@ -41,7 +41,7 @@ export default {
methods: {
...mapActions(['showMessage']),
copyPrivateLinkToClipboard() {
copyToClipboard(unref(this.displayedItem).privateLink)
clipboardCopy(unref(this.displayedItem).privateLink)
this.clipboardSuccessHandler()
this.showMessage({
title: this.$gettext('Private link copied'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<script>
import { mapActions } from 'vuex'
import { defineComponent } from 'vue'
import copyToClipboard from 'copy-to-clipboard'
import { clipboardCopy } from 'web-pkg/src/helpers'
export default defineComponent({
name: 'NameAndCopy',
Expand Down Expand Up @@ -60,7 +60,7 @@ export default defineComponent({
methods: {
...mapActions(['showMessage']),
copyLinkToClipboard() {
copyToClipboard(this.link.url)
clipboardCopy(this.link.url)
this.clipboardSuccessHandler()
this.showMessage({
title: this.link.quicklink
Expand Down
4 changes: 2 additions & 2 deletions packages/web-app-files/src/helpers/share/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { DateTime } from 'luxon'
import { Share } from 'web-client/src/helpers/share'
import { Store } from 'vuex'
import { clientService } from 'web-pkg/src/services'
import copyToClipboard from 'copy-to-clipboard'
import { clipboardCopy } from 'web-pkg/src/helpers'

interface CreateQuicklink {
store: Store<any>
Expand Down Expand Up @@ -45,7 +45,7 @@ export const createQuicklink = async (args: CreateQuicklink): Promise<Share> =>
storageId: resource.fileId || resource.id
})

copyToClipboard(link.url)
clipboardCopy(link.url)

await store.dispatch('showMessage', {
title: $gettext('The quicklink has been copied to your clipboard.')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
import PrivateLinkItem from 'web-app-files/src/components/SideBar/PrivateLinkItem.vue'
import { mockDeep } from 'jest-mock-extended'
import { mock } from 'jest-mock-extended'
import { Resource } from 'web-client'
import { createStore, defaultPlugins, mount, defaultStoreMockOptions } from 'web-test-helpers'
import PrivateLinkItem from 'web-app-files/src/components/SideBar/PrivateLinkItem.vue'

jest.useFakeTimers()

const folder = mock<Resource>({
type: 'folder',
ownerId: 'marie',
ownerDisplayName: 'Marie',
mdate: 'Wed, 21 Oct 2015 07:28:00 GMT',
size: '740',
name: 'lorem.txt',
privateLink: 'https://example.com/fake-private-link'
})

describe('PrivateLinkItem', () => {
it('should render a button', () => {
const { wrapper } = getWrapper()
expect(wrapper.html()).toMatchSnapshot()
})
it('upon clicking it should copy the private link to the clipboard button, render a success message and change icon for half a second', async () => {
jest.spyOn(window, 'prompt').mockImplementation()
Object.assign(window.navigator, {
clipboard: {
writeText: jest.fn().mockImplementation(() => Promise.resolve())
}
})

const { wrapper } = getWrapper()
const spyShowMessage = jest.spyOn(wrapper.vm, 'showMessage')
expect(spyShowMessage).not.toHaveBeenCalled()

await wrapper.trigger('click')
expect(wrapper.html()).toMatchSnapshot()
expect(window.navigator.clipboard.writeText).toHaveBeenCalledWith(folder.privateLink)
expect(spyShowMessage).toHaveBeenCalledTimes(1)

jest.advanceTimersByTime(550)
Expand All @@ -29,15 +45,6 @@ describe('PrivateLinkItem', () => {
})

function getWrapper() {
const folder = mockDeep<Resource>({
type: 'folder',
ownerId: 'marie',
ownerDisplayName: 'Marie',
mdate: 'Wed, 21 Oct 2015 07:28:00 GMT',
size: '740',
name: 'lorem.txt',
privateLink: 'https://example.com/fake-private-link'
})
const storeOptions = { ...defaultStoreMockOptions }
storeOptions.getters.capabilities.mockImplementation(() => ({ files: { privateLinks: true } }))
storeOptions.modules.Files.getters.highlightedFile.mockImplementation(() => folder)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@ describe('NameAndCopy', () => {
expect(wrapper.html()).toMatchSnapshot()
})
it('upon clicking it should copy the private link to the clipboard button, render a success message and change icon for half a second', async () => {
const windowSpy = jest.spyOn(window, 'prompt').mockImplementation()
Object.assign(window.navigator, {
clipboard: {
writeText: jest.fn().mockImplementation(() => Promise.resolve())
}
})

const { wrapper } = getWrapper()
const spyShowMessage = jest.spyOn(wrapper.vm, 'showMessage')
expect(spyShowMessage).not.toHaveBeenCalled()
expect(windowSpy).not.toHaveBeenCalled()

await wrapper.find('.oc-files-public-link-copy-url').trigger('click')
expect(window.navigator.clipboard.writeText).toHaveBeenCalledWith(exampleLink.url)
expect(wrapper.html()).toMatchSnapshot()
expect(spyShowMessage).toHaveBeenCalledTimes(1)
expect(windowSpy).toHaveBeenCalledTimes(1)
expect(windowSpy).toHaveBeenCalledWith('Copy to clipboard: Ctrl+C, Enter', exampleLink.url)

jest.advanceTimersByTime(550)

Expand Down
1 change: 1 addition & 0 deletions packages/web-pkg/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"main": "src/index.ts",
"peerDependencies": {
"axios": "^0.27.2",
"copy-to-clipboard": "^3.3.1",
"filesize": "^9.0.11",
"lodash-es": "^4.17.21",
"luxon": "^2.4.0",
Expand Down
9 changes: 9 additions & 0 deletions packages/web-pkg/src/helpers/clipboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import copyToClipboard from 'copy-to-clipboard'

export function clipboardCopy(string): Promise<void> | boolean {
if (navigator.clipboard) {
return navigator.clipboard.writeText(string)
} else {
return copyToClipboard(string)
}
}
1 change: 1 addition & 0 deletions packages/web-pkg/src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './cache'
export * from './clipboard'
export * from './preview'
export * from './datetime'
export * from './filesize'
Expand Down
5 changes: 0 additions & 5 deletions packages/web-runtime/src/defaults/vue.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import 'vue-resize/dist/vue-resize.css'
import Vue from 'vue'
import WebPlugin from '../plugins/web'
import Avatar from '../components/Avatar.vue'
import focusMixin from '../mixins/focusMixin'
import lifecycleMixin from '../mixins/lifecycleMixin'
import VueEvents from 'vue-events'
import VueScrollTo from 'vue-scrollto'
import VueResize from 'vue-resize'
import VueMeta from 'vue-meta'
import PortalVue from 'portal-vue'
import AsyncComputed from 'vue-async-computed'
Expand All @@ -15,10 +12,8 @@ import Vuex from 'vuex'

Vue.use(Vuex)
Vue.use(VueRouter)
Vue.use(VueEvents)
Vue.use(VueScrollTo)
Vue.use(WebPlugin)
Vue.use(VueResize)
Vue.use(VueMeta, {
refreshOnceOnNavigation: true
})
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1ef49a1

Please sign in to comment.