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

feat: Use simple input field instead of multiselect for plain URLs #787

Merged
merged 4 commits into from
Aug 30, 2024
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
6 changes: 2 additions & 4 deletions cypress/e2e/column-text-link.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ describe('Test column text-link', () => {

cy.loadTable('Test text-link')
cy.get('.NcTable').contains('Create row').click({ force: true })
cy.get('.modal__content .slot input').first().type('https://nextcloud.com').tick(500)
cy.get('.icon-label-container .labels').contains('https://nextcloud.com').click()
cy.get('.modal__content .slot input').first().type('https://nextcloud.com')

cy.intercept({ method: 'GET', url: '**/search/providers/files/*' }).as('filesResults')
cy.get('.modal__content .slot input').eq(1).type('pdf').tick(500)
Expand All @@ -58,8 +57,7 @@ describe('Test column text-link', () => {
cy.loadTable('Test text-link')
cy.get('.NcTable tr td button').click({ force: true })

cy.get('.modal__content .slot input').first().clear().type('https://github.com').tick(500)
cy.get('[data-cy*="github"]').click()
cy.get('.modal__content .slot input').first().clear().type('https://github.com')

cy.get('.modal__content .slot input').eq(1).type('photo-test').tick(500)
cy.get('[data-cy*="photo-test"]').first().click()
Expand Down
4 changes: 3 additions & 1 deletion cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,9 @@ Cypress.Commands.add('createTextLinkColumn', (title, ressourceProvider, isFirstC
cy.get('.typeSelection span').contains('Url', { matchCase: false }).click()
cy.get('.typeSelection span').contains('Files').click()
// TODO is the contacts search provider deactivated by default beginning with nc28
// cy.get('.typeSelection span label').contains('Contacts').click()
if (['stable27'].includes(Cypress.env('ncVersion'))) {
cy.get('.typeSelection span').contains('Contacts').click()
}

ressourceProvider.forEach(provider =>
cy.get('.typeSelection span').contains(provider, { matchCase: false }).click(),
Expand Down
22 changes: 4 additions & 18 deletions src/modules/sidebar/sections/SidebarIntegration.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,18 @@
import { mapGetters, mapState } from 'vuex'
import { generateUrl } from '@nextcloud/router'
import permissionsMixin from '../../../shared/components/ncTable/mixins/permissionsMixin.js'
import copyToClipboard from '../../../shared/mixins/copyToClipboard.js'

import NcInputField from '@nextcloud/vue/dist/Components/NcInputField.js'
import ContentCopy from 'vue-material-design-icons/ContentCopy.vue'
import { showError, showSuccess } from '@nextcloud/dialogs'

export default {
components: {
NcInputField,
ContentCopy,
},

mixins: [permissionsMixin],
mixins: [permissionsMixin, copyToClipboard],

data() {
return {
Expand All @@ -80,22 +81,7 @@ export default {
},
methods: {
copyUrl() {
if (navigator?.clipboard) {
navigator.clipboard.writeText(this.apiEndpointUrl).then(function() {
showSuccess(t('files', 'Integration URL copied to clipboard'))
}, function(err) {
showError(t('files', 'Clipboard is not available'))
console.error('Async: Could not copy text: ', err)
})
} else {
try {
document.querySelector('input#urlTextField').select()
document.execCommand('copy')
showSuccess(t('files', 'Integration URL copied to clipboard'))
} catch (e) {
showError(t('files', 'Clipboard is not available'))
}
}
this.copyToClipboard(this.apiEndpointUrl, false)
},
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
{{ t('tables', 'You can not insert any links in this field. Please configure at least one link provider in the column configuration.') }}
</NcNoteCard>
</div>
<div class="col-4">
<NcSelect v-model="localValue"
<div class="link-input">
<NcTextField v-if="isPlainUrl" :value.sync="plainLink" :placeholder="t('tables', 'URL')" />
<NcSelect v-else v-model="localValue"
:options="results"
:clearable="true"
label="title"
Expand All @@ -22,35 +23,58 @@
<LinkWidget :thumbnail-url="props.thumbnailUrl" :icon-url="props.icon" :title="props.title" :subline="props.subline" :icon-size="40" />
</template>
<template #selected-option="props">
<LinkWidget :thumbnail-url="props.thumbnailUrl" :icon-url="props.icon" :title="props.title" :subline="props.subline" :icon-size="40" />
<LinkWidget :thumbnail-url="props.thumbnailUrl" :icon-url="props.icon" :title="props.title" :icon-size="24" />
</template>
</NcSelect>
<NcButton type="tertiary" :disabled="!localValue" :title="t('tables', 'Copy link')" @click="copyLink">
<template #icon>
<ContentCopy v-if="!copied" :size="20" />
<ClipboardCheckMultipleOutline v-else :size="20" />
</template>
</NcButton>
<NcButton type="tertiary" :disabled="!localValue" :title="t('tables', 'Open link')" @click="openLink">
<template #icon>
<OpenInNew :size="20" />
</template>
</NcButton>
</div>
<NcReferenceList :text="localValue?.value" :limit="1" :interactive="false" />
</div>
</RowFormWrapper>
</template>

<script>
import ContentCopy from 'vue-material-design-icons/ContentCopy.vue'
import ClipboardCheckMultipleOutline from 'vue-material-design-icons/ClipboardCheckMultipleOutline.vue'
import OpenInNew from 'vue-material-design-icons/OpenInNew.vue'
import { NcReferenceList } from '@nextcloud/vue/dist/Components/NcRichText.js'
import RowFormWrapper from './RowFormWrapper.vue'
import axios from '@nextcloud/axios'
import { generateOcsUrl } from '@nextcloud/router'
import displayError from '../../../../utils/displayError.js'
import { NcNoteCard, NcSelect } from '@nextcloud/vue'
import { NcButton, NcTextField, NcNoteCard, NcSelect } from '@nextcloud/vue'
import debounce from 'debounce'
import generalHelper from '../../../../mixins/generalHelper.js'
import copyToClipboard from '../../../../mixins/copyToClipboard.js'
import LinkWidget from '../LinkWidget.vue'
import { translate as t } from '@nextcloud/l10n'

export default {

components: {
ContentCopy,
ClipboardCheckMultipleOutline,
OpenInNew,
NcButton,
NcNoteCard,
RowFormWrapper,
NcSelect,
NcReferenceList,
NcTextField,
RowFormWrapper,
LinkWidget,
},

mixins: [generalHelper],
mixins: [generalHelper, copyToClipboard],

props: {
column: {
Expand All @@ -69,10 +93,24 @@ export default {
results: [],
term: '',
providerLoading: {},
copied: false,
}
},

computed: {
plainLink: {
get() {
return this.localValue?.value ?? ''
},
set(v) {
this.$emit('update:value', JSON.stringify({
title: v,
subline: t('tables', 'URL'),
providerId: 'url',
value: v,
}))
},
},
localValue: {
get() {
// if we got an old value (string not object as json)
Expand All @@ -95,6 +133,9 @@ export default {
this.$emit('update:value', value)
},
},
isPlainUrl() {
return this.providers?.length === 1 && this.providers[0] === 'url'
},
isLoadingResults() {
for (const [key, value] of Object.entries(this.providerLoading)) {
console.debug('is still loading results at least for: ' + key, value)
Expand Down Expand Up @@ -155,7 +196,9 @@ export default {
this.removeResultsByProviderId(providerId)

if (providerId === 'url') {
this.addUrlResult(term)
if (this.isValidUrl(term)) {
this.addUrlResult(term)
}
this.setProviderLoading(providerId, false)
return
}
Expand Down Expand Up @@ -186,6 +229,14 @@ export default {
this.setProviderLoading(providerId, false)
},

isValidUrl(string) {
try {
return new URL(string)
} catch (err) {
return false
}
},

addUrlResult(term) {
this.results.push({
title: term,
Expand All @@ -198,6 +249,42 @@ export default {
removeResultsByProviderId(providerId) {
this.results = this.results.filter(item => item.providerId !== providerId)
},

copyLink() {
if (this.localValue) {
if (this.copied !== false) {
clearTimeout(this.copied)
}

this.copied = this.copyToClipboard(this.localValue.value)
? setTimeout(() => {
this.copied = false
}, 3000)
: false
}
},

openLink() {
if (this.localValue) {
window.open(this.localValue.value, '_blank')
}
},
},
}
</script>
<style lang="scss" scoped>
.link-input {
display: flex;
align-items: center;
margin-bottom: var(--default-grid-baseline);

:deep(.v-select:not(.vs--open) .vs__search) {
position: absolute;
}

:deep(.vs__selected) {
flex-grow: 1;
height: auto !important;
}
}
</style>
30 changes: 30 additions & 0 deletions src/shared/mixins/copyToClipboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { showError, showSuccess } from '@nextcloud/dialogs'

export default {
methods: {
async copyToClipboard(content, silent = true) {
try {
if (navigator?.clipboard) {
await navigator.clipboard.writeText(content)
} else {
throw new Error('Clipboard is not available')
}

if (!silent) {
showSuccess(t('files', 'Copied to clipboard.'))
}
return true
} catch (e) {
console.error('Error copying to clipboard', e)
if (!silent) {
showError(t('files', 'Clipboard is not available'))
}
}
},
},
}
Loading