Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: #8134 - copy to clipboard #8173

Merged
merged 8 commits into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions changelog/unreleased/enhancement-clipboard-copy
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Enhancement: Make clipboard copy available to more browsers

We have added more functionality for copying (e.g. links) to the user's clipboard.
By switching libraries we now use the standard browser API (if available) with a
fallback and only offer copy-to-clipboard buttons if the browser supports it.

https://github.com/owncloud/web/pull/8136
https://github.com/owncloud/web/pull/8173
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 @@ -95,6 +95,7 @@
v-text="file.path"
/>
<oc-button
v-if="isClipboardCopySupported"
v-oc-tooltip="copyEosPathLabel"
:aria-label="copyEosPathLabel"
appearance="raw"
Expand All @@ -116,13 +117,9 @@
<th scope="col" class="oc-pr-s" v-text="directLinkLabel" />
<td>
<div class="oc-flex oc-flex-middle oc-flex-between oc-width-1-1">
<p
ref="directLink"
v-oc-tooltip="directLink"
class="oc-my-rm oc-text-truncate"
v-text="directLink"
/>
<p v-oc-tooltip="directLink" class="oc-my-rm oc-text-truncate" v-text="directLink" />
<oc-button
v-if="isClipboardCopySupported"
v-oc-tooltip="copyDirectLinkLabel"
:aria-label="copyDirectLinkLabel"
appearance="raw"
Expand Down Expand Up @@ -160,7 +157,7 @@
</div>
</template>
<script lang="ts">
import { ComputedRef, defineComponent, inject } from 'vue'
import { computed, ComputedRef, defineComponent, inject, ref, unref } from 'vue'
import { mapActions, mapGetters } from 'vuex'
import { ImageDimension } from '../../../constants'
import { loadPreview } from 'web-pkg/src/helpers/preview'
Expand All @@ -173,10 +170,11 @@ import {
useCapabilityFilesTags,
usePublicLinkContext,
useStore,
useTranslations,
useUserContext
} from 'web-pkg/src/composables'
import { getIndicators } from '../../../helpers/statusIndicators'
import copyToClipboard from 'copy-to-clipboard'
import { useClipboard } from '@vueuse/core'
import { encodePath } from 'web-pkg/src/utils'
import { formatDateFromHTTP, formatFileSize } from 'web-pkg/src/helpers'
import { eventBus } from 'web-pkg/src/services/eventBus'
Expand All @@ -190,22 +188,59 @@ export default defineComponent({
name: 'FileDetails',
setup() {
const store = useStore()
const { $gettext } = useTranslations()

const copiedDirect = ref(false)
const copiedEos = ref(false)
const {
copy,
copied,
isSupported: isClipboardCopySupported
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JammingBen not sure if isSupported is needed here/in general in the v-ifs. I didn't look into it any further but as pointed out in #8136 (comment), from a cursory glance it is always true since we set legacy: true in useClipboard

Copy link
Contributor Author

@JammingBen JammingBen Jan 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dschmidt and I decided to leave it for now, as we are not 100% sure if needed or not (same as you :) )

} = useClipboard({ legacy: true, copiedDuring: 550 })

const file = inject<ComputedRef<Resource>>('displayedItem')

const directLink = computed(() => {
return `${store.getters.configuration.server}files/spaces/personal/home${encodePath(
unref(file).path
)}`
})

const copyEosPathToClipboard = () => {
copy(unref(file).path)
copiedEos.value = unref(copied)
store.dispatch('showMessage', {
title: $gettext('EOS path copied'),
desc: $gettext('The EOS path has been copied to your clipboard.')
})
}

const copyDirectLinkToClipboard = () => {
copy(unref(directLink))
copiedDirect.value = unref(copied)
store.dispatch('showMessage', {
title: $gettext('Direct link copied'),
desc: $gettext('The direct link has been copied to your clipboard.')
})
}

return {
copiedEos,
copyEosPathToClipboard,
copiedDirect,
copyDirectLinkToClipboard,
isClipboardCopySupported,
isUserContext: useUserContext({ store }),
isPublicLinkContext: usePublicLinkContext({ store }),
accessToken: useAccessToken({ store }),
space: inject<ComputedRef<Resource>>('displayedSpace'),
file: inject<ComputedRef<Resource>>('displayedItem'),
directLink,
file,
hasTags: useCapabilityFilesTags()
}
},

data: () => ({
loading: false,
copiedDirect: false,
copiedEos: false,
timeout: null
loading: false
}),
computed: {
...mapGetters('runtime/spaces', ['spaces']),
Expand Down Expand Up @@ -320,9 +355,6 @@ export default defineComponent({
ownerAdditionalInfo() {
return this.file.owner?.[0].additionalInfo
},
directLink() {
return `${this.configuration.server}files/spaces/personal/home${encodePath(this.file.path)}`
},
directLinkLabel() {
return this.$gettext('Direct link')
},
Expand Down Expand Up @@ -468,31 +500,6 @@ export default defineComponent({
await Promise.all(calls.map((p) => p.catch((e) => e)))
this.loading = false
},
copyEosPathToClipboard() {
copyToClipboard(this.file.path)
this.copiedEos = true
this.clipboardSuccessHandler()
this.showMessage({
title: this.$gettext('EOS path copied'),
desc: this.$gettext('The EOS path has been copied to your clipboard.')
})
},
copyDirectLinkToClipboard() {
copyToClipboard(this.directLink)
this.copiedDirect = true
this.clipboardSuccessHandler()
this.showMessage({
title: this.$gettext('Direct link copied'),
desc: this.$gettext('The direct link has been copied to your clipboard.')
})
},
clipboardSuccessHandler() {
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.copiedDirect = false
this.copiedEos = false
}, 550)
},
getTagLink(tag) {
return createLocationCommon('files-common-search', {
query: { term: `Tags:${tag}`, provider: 'files.sdk' }
Expand Down
65 changes: 35 additions & 30 deletions packages/web-app-files/src/components/SideBar/PrivateLinkItem.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<template>
<oc-button
v-if="isClipboardCopySupported"
v-oc-tooltip="buttonLabel"
appearance="raw"
:aria-label="buttonLabel"
class="oc-files-private-link-copy-url"
:variation="copied ? 'success' : 'passive'"
@click="copyPrivateLinkToClipboard"
@click="copyLinkToClipboard"
>
<span v-text="buttonText" />
<oc-icon
Expand All @@ -18,43 +19,47 @@
</oc-button>
</template>

<script>
import { mapActions } from 'vuex'
import copyToClipboard from 'copy-to-clipboard'
import { unref } from 'vue'
<script lang="ts">
import { computed, defineComponent, inject, unref } from 'vue'
import { useClipboard } from '@vueuse/core'
import { Resource } from 'web-client'
import { useStore, useTranslations } from 'web-pkg/src'

export default {
export default defineComponent({
name: 'PrivateLinkItem',
inject: ['displayedItem'],
data: () => ({
copied: false,
timeout: null
}),
setup() {
const { $gettext } = useTranslations()
const store = useStore<any>()
const displayedItem = inject<Resource>('displayedItem')
const privateLink = computed(() => unref(displayedItem))

const {
copy,
copied,
isSupported: isClipboardCopySupported
} = useClipboard({ legacy: true, copiedDuring: 550 })

const copyLinkToClipboard = () => {
copy(unref(privateLink).privateLink)
store.dispatch('showMessage', {
title: $gettext('Private link copied'),
desc: $gettext('The private link has been copied to your clipboard.')
})
}

return {
copied,
copyLinkToClipboard,
isClipboardCopySupported
}
},
computed: {
buttonText() {
return this.$gettext('Private link')
},
buttonLabel() {
return this.$gettext('Copy private link to clipboard')
}
},
methods: {
...mapActions(['showMessage']),
copyPrivateLinkToClipboard() {
copyToClipboard(unref(this.displayedItem).privateLink)
this.clipboardSuccessHandler()
this.showMessage({
title: this.$gettext('Private link copied'),
desc: this.$gettext('The private link has been copied to your clipboard.')
})
},
clipboardSuccessHandler() {
this.copied = true
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.copied = false
}, 550)
}
}
}
})
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
/>
</div>
<oc-button
v-if="isClipboardCopySupported"
v-oc-tooltip="copyBtnHint"
appearance="raw"
:aria-label="copyBtnHint"
Expand All @@ -26,10 +27,10 @@
</div>
</template>

<script>
import { mapActions } from 'vuex'
<script lang="ts">
import { defineComponent } from 'vue'
import copyToClipboard from 'copy-to-clipboard'
import { useStore, useTranslations } from 'web-pkg/src/composables'
import { useClipboard } from '@vueuse/core'

export default defineComponent({
name: 'NameAndCopy',
Expand All @@ -39,10 +40,37 @@ export default defineComponent({
required: true
}
},
data: () => ({
copied: false,
timeout: null
}),
setup(props) {
const { $gettext, $gettextInterpolate } = useTranslations()
const store = useStore<any>()

const {
copy,
copied,
isSupported: isClipboardCopySupported
} = useClipboard({ legacy: true, copiedDuring: 550 })

const copyLinkToClipboard = () => {
copy(props.link.url)
store.dispatch('showMessage', {
title: props.link.quicklink
? $gettext('The quicklink has been copied to your clipboard.')
: $gettextInterpolate(
$gettext('The link "%{linkName}" has been copied to your clipboard.'),
{
linkName: props.link.linkName
},
true
)
})
}

return {
copied,
copyLinkToClipboard,
isClipboardCopySupported
}
},
computed: {
linkName() {
return this.link.name
Expand All @@ -56,31 +84,6 @@ export default defineComponent({
copiedLabel() {
return this.$gettext('Copied')
}
},
methods: {
...mapActions(['showMessage']),
copyLinkToClipboard() {
copyToClipboard(this.link.url)
this.clipboardSuccessHandler()
this.showMessage({
title: this.link.quicklink
? this.$gettext('The quicklink has been copied to your clipboard.')
: this.$gettextInterpolate(
this.$gettext('The link "%{linkName}" has been copied to your clipboard.'),
{
linkName: this.linkName
},
true
)
})
},
clipboardSuccessHandler() {
this.copied = true
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.copied = false
}, 550)
}
}
})
</script>
Expand Down
5 changes: 3 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 { useClipboard } from '@vueuse/core'

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

copyToClipboard(link.url)
const { copy } = useClipboard({ legacy: true })
copy(link.url)

await store.dispatch('showMessage', {
title: $gettext('The quicklink has been copied to your clipboard.')
Expand Down
Loading