refactor(NcCollectionList): refactor code
- migrate service from Class to simple functions
- migrate store to composable
- make NcCollectionListItem a visual component
- update avatar styles
- use RouterLink for relative paths in the same app
- update message.pot translations
- add docs
Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>
(cherry picked from commit 94177fec58
)
Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>
This commit is contained in:
Родитель
3cd6d7ad4d
Коммит
100bb3a6f8
|
@ -27,6 +27,9 @@ msgstr ""
|
|||
msgid "Activities"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add to a project"
|
||||
msgstr ""
|
||||
|
||||
msgid "Animals & Nature"
|
||||
msgstr ""
|
||||
|
||||
|
@ -102,6 +105,9 @@ msgstr ""
|
|||
msgid "Confirm changes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Connect items to a project to make them easier to find"
|
||||
msgstr ""
|
||||
|
||||
msgid "Custom"
|
||||
msgstr ""
|
||||
|
||||
|
@ -136,6 +142,15 @@ msgstr ""
|
|||
msgid "External documentation for {name}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Failed to add the item to the project"
|
||||
msgstr ""
|
||||
|
||||
msgid "Failed to create a project"
|
||||
msgstr ""
|
||||
|
||||
msgid "Failed to rename the project"
|
||||
msgstr ""
|
||||
|
||||
msgid "Favorite"
|
||||
msgstr ""
|
||||
|
||||
|
@ -161,6 +176,9 @@ msgstr ""
|
|||
msgid "Gold"
|
||||
msgstr ""
|
||||
|
||||
msgid "Hide details"
|
||||
msgstr ""
|
||||
|
||||
msgid "Hide password"
|
||||
msgstr ""
|
||||
|
||||
|
@ -295,6 +313,9 @@ msgstr ""
|
|||
msgid "Related team resources"
|
||||
msgstr ""
|
||||
|
||||
msgid "Rename project"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: A color name for RGB(191, 103, 139)
|
||||
msgid "Rosy brown"
|
||||
msgstr ""
|
||||
|
@ -337,6 +358,9 @@ msgstr ""
|
|||
msgid "Settings navigation"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show details"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show password"
|
||||
msgstr ""
|
||||
|
||||
|
@ -370,6 +394,9 @@ msgstr ""
|
|||
msgid "Travel & Places"
|
||||
msgstr ""
|
||||
|
||||
msgid "Type to search for existing projects"
|
||||
msgstr ""
|
||||
|
||||
msgid "Type to search time zone"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -3,6 +3,19 @@
|
|||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<docs>
|
||||
Provides a Vue standalone component for Nextcloud Projects feature introduced in Nextcloud 16. Replaces deprecated `nextcloud-vue-collections` library.
|
||||
|
||||
Projects feature is deprecated since Nextcloud 25, and superseded by Related resources. See [NcRelatedResourcesPanel](#/Components/NcRelatedResourcesPanel) documentation for more information.
|
||||
|
||||
### Usage
|
||||
|
||||
To enable feature in Nextcloud, run following command:
|
||||
```sh
|
||||
occ config:system:set --value true 'projects.enabled'
|
||||
```
|
||||
</docs>
|
||||
|
||||
<template>
|
||||
<ul v-if="collections && type && id" id="collection-list" class="collection-list">
|
||||
<li @click="showSelect">
|
||||
|
@ -12,7 +25,7 @@
|
|||
<div id="collection-select-container">
|
||||
<NcSelect ref="select"
|
||||
v-model="value"
|
||||
:aria-label-combobox="t('core', 'Add to a project')"
|
||||
:aria-label-combobox="t('Add to a project')"
|
||||
:options="options"
|
||||
:placeholder="placeholder"
|
||||
label="title"
|
||||
|
@ -34,7 +47,7 @@
|
|||
</span>
|
||||
</template>
|
||||
<p class="hint">
|
||||
{{ t('core', 'Connect items to a project to make them easier to find') }}
|
||||
{{ t('Connect items to a project to make them easier to find') }}
|
||||
</p>
|
||||
</NcSelect>
|
||||
</div>
|
||||
|
@ -44,37 +57,30 @@
|
|||
{{ error }}
|
||||
</li>
|
||||
</transition>
|
||||
<NcCollectionListItem v-for="collection in collections" :key="collection.id" :collection="collection" />
|
||||
<NcCollectionListItem v-for="collection in collections"
|
||||
:key="collection.id"
|
||||
:collection="collection"
|
||||
:error="collectionsError[collection.id]"
|
||||
@rename-collection="renameCollectionFromItem"
|
||||
@remove-resource="removeResourceFromCollection" />
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import debounce from 'debounce'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { ref } from 'vue'
|
||||
import { t } from '../../l10n.js'
|
||||
|
||||
import NcAvatar from '../NcAvatar/index.js'
|
||||
import NcSelect from '../NcSelect/index.js'
|
||||
import NcCollectionListItem from './NcCollectionListItem.vue'
|
||||
|
||||
import { state, actions } from './collectionstore.js'
|
||||
import { useCollections } from './useCollections.js'
|
||||
import { searchService } from './service.ts'
|
||||
|
||||
const METHOD_CREATE_COLLECTION = 0
|
||||
const METHOD_ADD_TO_COLLECTION = 1
|
||||
|
||||
const _debouncedSearch = debounce(
|
||||
function(query, loading) {
|
||||
if (query !== '') {
|
||||
loading(true)
|
||||
actions.search(query).then((collections) => {
|
||||
this.searchCollections = collections
|
||||
}).catch(e => {
|
||||
console.error('Failed to search for collections', e)
|
||||
}).finally(() => {
|
||||
loading(false)
|
||||
})
|
||||
}
|
||||
}, 500)
|
||||
|
||||
export default {
|
||||
name: 'NcCollectionList',
|
||||
|
||||
|
@ -106,7 +112,9 @@ export default {
|
|||
type: String,
|
||||
default: '',
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether the component is active (to start fetch resources)
|
||||
*/
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
|
@ -114,8 +122,38 @@ export default {
|
|||
},
|
||||
|
||||
setup() {
|
||||
const {
|
||||
storedCollections,
|
||||
fetchCollectionsByResource,
|
||||
createCollection,
|
||||
addResourceToCollection,
|
||||
removeResourceFromCollection,
|
||||
renameCollection,
|
||||
} = useCollections()
|
||||
|
||||
const searchCollections = ref([])
|
||||
const search = debounce(function(query, loading) {
|
||||
if (query !== '') {
|
||||
loading(true)
|
||||
searchService(query).then(collections => {
|
||||
searchCollections.value = collections
|
||||
}).catch(e => {
|
||||
console.error('Failed to search for collections', e)
|
||||
}).finally(() => {
|
||||
loading(false)
|
||||
})
|
||||
}
|
||||
}, 500)
|
||||
|
||||
return {
|
||||
state,
|
||||
storedCollections,
|
||||
fetchCollectionsByResource,
|
||||
createCollection,
|
||||
addResourceToCollection,
|
||||
removeResourceFromCollection,
|
||||
renameCollection,
|
||||
searchCollections,
|
||||
search,
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -126,7 +164,7 @@ export default {
|
|||
codes: undefined,
|
||||
value: null,
|
||||
model: {},
|
||||
searchCollections: [],
|
||||
collectionsError: {},
|
||||
error: null,
|
||||
isSelectOpen: false,
|
||||
}
|
||||
|
@ -134,18 +172,20 @@ export default {
|
|||
|
||||
computed: {
|
||||
collections() {
|
||||
return this.state.collections.filter((collection) => {
|
||||
return typeof collection.resources.find((resource) => resource && resource.id === '' + this.id && resource.type === this.type) !== 'undefined'
|
||||
})
|
||||
return this.storedCollections.filter(collection => collection.resources
|
||||
.some(resource => resource && resource.id === String(this.id) && resource.type === this.type),
|
||||
)
|
||||
},
|
||||
|
||||
placeholder() {
|
||||
return this.isSelectOpen ? t('core', 'Type to search for existing projects') : t('core', 'Add to a project')
|
||||
return this.isSelectOpen
|
||||
? t('Type to search for existing projects')
|
||||
: t('Add to a project')
|
||||
},
|
||||
|
||||
options() {
|
||||
const options = []
|
||||
window.OCP.Collaboration.getTypes().sort().forEach((type) => {
|
||||
window.OCP.Collaboration.getTypes().sort().forEach(type => {
|
||||
options.push({
|
||||
method: METHOD_CREATE_COLLECTION,
|
||||
type,
|
||||
|
@ -155,7 +195,7 @@ export default {
|
|||
})
|
||||
})
|
||||
for (const index in this.searchCollections) {
|
||||
if (this.collections.findIndex((collection) => collection.id === this.searchCollections[index].id) === -1) {
|
||||
if (!this.collections.find(collection => collection.id === this.searchCollections[index].id)) {
|
||||
options.push({
|
||||
method: METHOD_ADD_TO_COLLECTION,
|
||||
title: this.searchCollections[index].name,
|
||||
|
@ -165,58 +205,43 @@ export default {
|
|||
}
|
||||
return options
|
||||
},
|
||||
|
||||
resourceIdentifier() {
|
||||
return {
|
||||
resourceType: this.type,
|
||||
resourceId: this.id,
|
||||
isActive: this.isActive,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
type() {
|
||||
if (this.isActive) {
|
||||
actions.fetchCollectionsByResource({
|
||||
resourceType: this.type,
|
||||
resourceId: this.id,
|
||||
})
|
||||
}
|
||||
resourceIdentifier: {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler(resourceIdentifier) {
|
||||
if (!resourceIdentifier.isActive || !resourceIdentifier.resourceId || !resourceIdentifier.resourceType) {
|
||||
return
|
||||
}
|
||||
this.fetchCollectionsByResource(resourceIdentifier)
|
||||
},
|
||||
},
|
||||
|
||||
id() {
|
||||
if (this.isActive) {
|
||||
actions.fetchCollectionsByResource({
|
||||
resourceType: this.type,
|
||||
resourceId: this.id,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
isActive(isActive) {
|
||||
if (isActive) {
|
||||
actions.fetchCollectionsByResource({
|
||||
resourceType: this.type,
|
||||
resourceId: this.id,
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
actions.fetchCollectionsByResource({
|
||||
resourceType: this.type,
|
||||
resourceId: this.id,
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
t,
|
||||
|
||||
select(selectedOption, id) {
|
||||
select(selectedOption) {
|
||||
if (selectedOption.method === METHOD_CREATE_COLLECTION) {
|
||||
selectedOption.action().then((id) => {
|
||||
actions.createCollection({
|
||||
selectedOption.action().then(resourceId => {
|
||||
this.createCollection({
|
||||
baseResourceType: this.type,
|
||||
baseResourceId: this.id,
|
||||
resourceType: selectedOption.type,
|
||||
resourceId: id,
|
||||
resourceId,
|
||||
name: this.name,
|
||||
}).catch((e) => {
|
||||
this.setError(t('core', 'Failed to create a project'), e)
|
||||
this.setError(t('Failed to create a project'), e)
|
||||
})
|
||||
}).catch((e) => {
|
||||
console.error('No resource selected', e)
|
||||
|
@ -224,16 +249,14 @@ export default {
|
|||
}
|
||||
|
||||
if (selectedOption.method === METHOD_ADD_TO_COLLECTION) {
|
||||
actions.addResourceToCollection({
|
||||
this.addResourceToCollection({
|
||||
collectionId: selectedOption.collectionId, resourceType: this.type, resourceId: this.id,
|
||||
}).catch((e) => {
|
||||
this.setError(t('core', 'Failed to add the item to the project'), e)
|
||||
this.setError(t('Failed to add the item to the project'), e)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
search(query, loading) {
|
||||
_debouncedSearch.bind(this)(query, loading)
|
||||
this.value = null
|
||||
},
|
||||
|
||||
showSelect() {
|
||||
|
@ -241,14 +264,6 @@ export default {
|
|||
this.$refs.select.$el.focus()
|
||||
},
|
||||
|
||||
hideSelect() {
|
||||
this.selectIsOpen = false
|
||||
},
|
||||
|
||||
isVueComponent(object) {
|
||||
return object._isVue
|
||||
},
|
||||
|
||||
setError(error, e) {
|
||||
console.error(error, e)
|
||||
this.error = error
|
||||
|
@ -256,6 +271,17 @@ export default {
|
|||
this.error = null
|
||||
}, 5000)
|
||||
},
|
||||
|
||||
renameCollectionFromItem({ collectionId, name }) {
|
||||
this.renameCollection({ collectionId, name })
|
||||
.catch((e) => {
|
||||
console.error(t('Failed to rename the project'), e)
|
||||
this.collectionsError[collectionId] = t('Failed to rename the project')
|
||||
setTimeout(() => {
|
||||
this.collectionsError[collectionId] = null
|
||||
}, 5000)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -267,11 +293,11 @@ export default {
|
|||
|
||||
.collection-list > li {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
& > .avatar {
|
||||
margin-top: auto;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,6 +352,8 @@ div.avatar {
|
|||
|
||||
.avatar {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-color: var(--color-background-darker) !important;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
class="collection-item-name"
|
||||
title=""
|
||||
@click="showDetails">{{ collection.name }}</span>
|
||||
<form v-else :class="{'shouldshake': error.rename }" @submit.prevent="renameCollection">
|
||||
<form v-else :class="{'should-shake': error }" @submit.prevent="renameCollection">
|
||||
<input v-model="newName"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
|
@ -18,37 +18,46 @@
|
|||
<input type="submit" value="" class="icon-confirm">
|
||||
</form>
|
||||
<div v-if="!detailsOpen && newName === null" class="linked-icons">
|
||||
<a v-for="resource in limitedResources(collection)"
|
||||
<component :is="getComponent(resource).component"
|
||||
v-for="resource in resources.slice(0, 2)"
|
||||
:key="resource.type + '|' + resource.id"
|
||||
:title="resource.name"
|
||||
:href="resource.link"
|
||||
:class="typeClass(resource)"><img :src="iconUrl(resource)"></a>
|
||||
:to="getComponent(resource).to"
|
||||
:href="getComponent(resource).href"
|
||||
:class="typeClass(resource)">
|
||||
<img :src="iconUrl(resource)" :alt="resource.name">
|
||||
</component>
|
||||
</div>
|
||||
|
||||
<span v-if="newName === null" class="sharingOptionsGroup">
|
||||
<NcActions>
|
||||
<NcActionButton icon="icon-info"
|
||||
@click.prevent="toggleDetails">
|
||||
{{ detailsOpen ? t('core', 'Hide details') : t('core', 'Show details') }}
|
||||
{{ detailsOpen ? t('Hide details') : t('Show details') }}
|
||||
</NcActionButton>
|
||||
<NcActionButton icon="icon-rename"
|
||||
@click.prevent="openRename">
|
||||
{{ t('core', 'Rename project') }}
|
||||
{{ t('Rename project') }}
|
||||
</NcActionButton>
|
||||
</NcActions>
|
||||
</span>
|
||||
|
||||
<transition name="fade">
|
||||
<div v-if="error.rename" class="error">
|
||||
{{ error.rename }}
|
||||
<div v-if="error" class="error">
|
||||
{{ error }}
|
||||
</div>
|
||||
</transition>
|
||||
<transition name="fade">
|
||||
<ul v-if="detailsOpen" class="resource-list-details">
|
||||
<li v-for="resource in collection.resources"
|
||||
<li v-for="resource in resources"
|
||||
:key="resource.type + '|' + resource.id"
|
||||
:class="typeClass(resource)">
|
||||
<a :href="resource.link"><img :src="iconUrl(resource)"><span class="resource-name">{{ resource.name || '' }}</span></a>
|
||||
<component :is="getComponent(resource).component"
|
||||
:to="getComponent(resource).to"
|
||||
:href="getComponent(resource).href">
|
||||
<img :src="iconUrl(resource)" :alt="resource.name">
|
||||
<span class="resource-name">{{ resource.name || '' }}</span>
|
||||
</component>
|
||||
<span class="icon-close" @click="removeResource(collection, resource)" />
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -57,15 +66,13 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { set } from 'vue'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { t } from '../../l10n.js'
|
||||
import { getRoute } from '../NcRichText/autolink.js'
|
||||
|
||||
import NcActions from '../NcActions/index.js'
|
||||
import NcActionButton from '../NcActionButton/index.js'
|
||||
import NcAvatar from '../NcAvatar/index.js'
|
||||
|
||||
import { actions } from './collectionstore.js'
|
||||
|
||||
export default {
|
||||
name: 'NcCollectionListItem',
|
||||
|
||||
|
@ -80,13 +87,19 @@ export default {
|
|||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
|
||||
error: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['remove-resource', 'rename-collection'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
detailsOpen: false,
|
||||
newName: null,
|
||||
error: {},
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -99,8 +112,19 @@ export default {
|
|||
return (resource) => 'resource-type-' + resource.type
|
||||
},
|
||||
|
||||
limitedResources() {
|
||||
return (collection) => collection.resources ? collection.resources.slice(0, 2) : []
|
||||
resources() {
|
||||
// invalid resources come from server as empty array ([]) and not an object
|
||||
return this.collection.resources?.filter(resource => !Array.isArray(resource)) ?? []
|
||||
},
|
||||
|
||||
getComponent() {
|
||||
return (resource) => {
|
||||
const route = getRoute(this.$router, resource.link)
|
||||
|
||||
return route
|
||||
? { component: 'router-link', to: route, href: undefined }
|
||||
: { component: 'a', to: undefined, href: resource.link }
|
||||
}
|
||||
},
|
||||
|
||||
iconUrl() {
|
||||
|
@ -127,13 +151,11 @@ export default {
|
|||
this.detailsOpen = true
|
||||
},
|
||||
|
||||
hideDetails() {
|
||||
this.detailsOpen = false
|
||||
},
|
||||
|
||||
removeResource(collection, resource) {
|
||||
actions.removeResource({
|
||||
collectionId: collection.id, resourceType: resource.type, resourceId: resource.id,
|
||||
this.$emit('remove-resource', {
|
||||
collectionId: collection.id,
|
||||
resourceType: resource.type,
|
||||
resourceId: resource.id,
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -142,22 +164,13 @@ export default {
|
|||
},
|
||||
|
||||
renameCollection() {
|
||||
if (this.newName === '') {
|
||||
this.newName = null
|
||||
return
|
||||
if (this.newName) {
|
||||
this.$emit('rename-collection', {
|
||||
collectionId: this.collection.id,
|
||||
name: this.newName,
|
||||
})
|
||||
}
|
||||
actions.renameCollection({
|
||||
collectionId: this.collection.id,
|
||||
name: this.newName,
|
||||
}).then((collection) => {
|
||||
this.newName = null
|
||||
}).catch((e) => {
|
||||
this.$set(this.error, 'rename', t('core', 'Failed to rename the project'))
|
||||
console.error(e)
|
||||
setTimeout(() => {
|
||||
set(this.error, 'rename', null)
|
||||
}, 3000)
|
||||
})
|
||||
this.newName = null
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -206,7 +219,7 @@ li.collection-list-item {
|
|||
margin-bottom: 0 !important;
|
||||
|
||||
.collection-avatar {
|
||||
margin-top: 6px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
form, .collection-item-name {
|
||||
|
@ -286,7 +299,7 @@ li.collection-list-item {
|
|||
}
|
||||
}
|
||||
|
||||
.shouldshake {
|
||||
.should-shake {
|
||||
animation: shake 0.6s 1 linear;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
|
||||
class CollectionService {
|
||||
|
||||
constructor() {
|
||||
this.http = axios
|
||||
}
|
||||
|
||||
listCollection(collectionId) {
|
||||
return this.http.get(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId }))
|
||||
}
|
||||
|
||||
renameCollection(collectionId, collectionName) {
|
||||
return this.http.put(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId }), {
|
||||
collectionName,
|
||||
}).then(result => {
|
||||
return result.data.ocs.data
|
||||
})
|
||||
}
|
||||
|
||||
getCollectionsByResource(resourceType, resourceId) {
|
||||
return this.http.get(generateOcsUrl('collaboration/resources/{resourceType}/{resourceId}', { resourceType, resourceId }))
|
||||
.then(result => {
|
||||
return result.data.ocs.data
|
||||
})
|
||||
}
|
||||
|
||||
createCollection(resourceType, resourceId, name) {
|
||||
return this.http.post(generateOcsUrl('collaboration/resources/{resourceType}/{resourceId}', { resourceType, resourceId }), {
|
||||
name,
|
||||
})
|
||||
.then((response) => {
|
||||
return response.data.ocs.data
|
||||
})
|
||||
}
|
||||
|
||||
addResource(collectionId, resourceType, resourceId) {
|
||||
resourceId = '' + resourceId
|
||||
return this.http.post(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId }), {
|
||||
resourceType,
|
||||
resourceId,
|
||||
}).then((response) => {
|
||||
return response.data.ocs.data
|
||||
})
|
||||
}
|
||||
|
||||
removeResource(collectionId, resourceType, resourceId) {
|
||||
return this.http.delete(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId }), { params: { resourceType, resourceId } })
|
||||
.then((response) => {
|
||||
return response.data.ocs.data
|
||||
})
|
||||
}
|
||||
|
||||
search(query) {
|
||||
return this.http.get(generateOcsUrl('collaboration/resources/collections/search/{query}', { query }))
|
||||
.then((response) => {
|
||||
return response.data.ocs.data
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const service = new CollectionService()
|
||||
|
||||
export default service
|
|
@ -1,82 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { reactive, set } from 'vue'
|
||||
import service from './collectionservice.js'
|
||||
|
||||
const state = reactive({
|
||||
collections: [],
|
||||
})
|
||||
|
||||
const mutations = {
|
||||
addCollections(collections) {
|
||||
set(state, 'collections', collections)
|
||||
},
|
||||
addCollection(collection) {
|
||||
state.collections.push(collection)
|
||||
},
|
||||
removeCollection(collectionId) {
|
||||
set(state, 'collections', state.collections.filter(item => item.id !== collectionId))
|
||||
},
|
||||
updateCollection(collection) {
|
||||
const index = state.collections.findIndex((_item) => _item.id === collection.id)
|
||||
if (index !== -1) {
|
||||
set(state.collections, index, collection)
|
||||
} else {
|
||||
state.collections.push(collection)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const actions = {
|
||||
fetchCollectionsByResource({ resourceType, resourceId }) {
|
||||
return service.getCollectionsByResource(resourceType, resourceId).then((collections) => {
|
||||
mutations.addCollections(collections)
|
||||
return collections
|
||||
})
|
||||
},
|
||||
createCollection({ baseResourceType, baseResourceId, resourceType, resourceId, name }) {
|
||||
return service.createCollection(baseResourceType, baseResourceId, name).then((collection) => {
|
||||
mutations.addCollection(collection)
|
||||
actions.addResourceToCollection({
|
||||
collectionId: collection.id,
|
||||
resourceType,
|
||||
resourceId,
|
||||
})
|
||||
})
|
||||
},
|
||||
renameCollection({ collectionId, name }) {
|
||||
return service.renameCollection(collectionId, name).then((collection) => {
|
||||
mutations.updateCollection(collection)
|
||||
return collection
|
||||
})
|
||||
},
|
||||
addResourceToCollection({ collectionId, resourceType, resourceId }) {
|
||||
return service.addResource(collectionId, resourceType, resourceId).then((collection) => {
|
||||
mutations.updateCollection(collection)
|
||||
return collection
|
||||
})
|
||||
},
|
||||
removeResource({ collectionId, resourceType, resourceId }) {
|
||||
return service.removeResource(collectionId, resourceType, resourceId).then((collection) => {
|
||||
if (collection.resources.length > 0) {
|
||||
mutations.updateCollection(collection)
|
||||
} else {
|
||||
mutations.removeCollection(collection)
|
||||
}
|
||||
})
|
||||
},
|
||||
search(query) {
|
||||
return service.search(query)
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
const store = {
|
||||
actions,
|
||||
state,
|
||||
}
|
||||
export default store
|
||||
export { actions, state }
|
|
@ -2,5 +2,4 @@
|
|||
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
export { default as NcCollectionListItem } from './NcCollectionListItem.vue'
|
||||
export { default } from './NcCollectionList.vue'
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import axios, { type AxiosResponse } from '@nextcloud/axios'
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import type { OCSResponse } from '@nextcloud/typings/ocs'
|
||||
|
||||
/**
|
||||
* Extracts the OCS data from a response
|
||||
* @param response OCS response
|
||||
*/
|
||||
function extractOcsData(response: AxiosResponse<OCSResponse>) {
|
||||
return response.data.ocs.data
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all collections
|
||||
* @param collectionId Collection ID
|
||||
*/
|
||||
export function listCollectionService(collectionId: number) {
|
||||
return axios.get(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId })).then(extractOcsData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a collection
|
||||
* @param collectionId Collection ID
|
||||
* @param collectionName New collection name
|
||||
*/
|
||||
export function renameCollectionService(collectionId: number, collectionName: string) {
|
||||
return axios.put(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId }), { collectionName }).then(extractOcsData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all collections for a resource
|
||||
* @param resourceType Resource type
|
||||
* @param resourceId Resource ID
|
||||
*/
|
||||
export function getCollectionsByResourceService(resourceType: string, resourceId: string) {
|
||||
return axios.get(generateOcsUrl('collaboration/resources/{resourceType}/{resourceId}', { resourceType, resourceId })).then(extractOcsData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a collection
|
||||
* @param resourceType Resource type
|
||||
* @param resourceId Resource ID
|
||||
* @param name Collection name
|
||||
*/
|
||||
export function createCollectionService(resourceType: string, resourceId: string, name: string) {
|
||||
return axios.post(generateOcsUrl('collaboration/resources/{resourceType}/{resourceId}', { resourceType, resourceId }), { name }).then(extractOcsData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a resource to a collection
|
||||
* @param collectionId Collection ID
|
||||
* @param resourceType Resource type
|
||||
* @param resourceId Resource ID
|
||||
*/
|
||||
export function addResourceService(collectionId: number, resourceType: string, resourceId: string) {
|
||||
return axios.post(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId }), { resourceType, resourceId }).then(extractOcsData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a resource from a collection
|
||||
* @param collectionId Collection ID
|
||||
* @param resourceType Resource type
|
||||
* @param resourceId Resource ID
|
||||
*/
|
||||
export function removeResourceService(collectionId: number, resourceType: string, resourceId: string) {
|
||||
return axios.delete(generateOcsUrl('collaboration/resources/collections/{collectionId}', { collectionId }), { params: { resourceType, resourceId } }).then(extractOcsData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for collections
|
||||
* @param query Search query
|
||||
*/
|
||||
export function searchService(query: string) {
|
||||
return axios.get(generateOcsUrl('collaboration/resources/collections/search/{query}', { query })).then(extractOcsData)
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { ref, set } from 'vue'
|
||||
import {
|
||||
renameCollectionService,
|
||||
getCollectionsByResourceService,
|
||||
createCollectionService,
|
||||
addResourceService,
|
||||
removeResourceService,
|
||||
} from './service.ts'
|
||||
|
||||
/**
|
||||
* Use collections composable
|
||||
*/
|
||||
export function useCollections() {
|
||||
// State
|
||||
const storedCollections = ref([])
|
||||
|
||||
// Mutations
|
||||
const addCollections = (collections) => {
|
||||
set(storedCollections, 'value', collections)
|
||||
}
|
||||
|
||||
const addCollection = (collection) => {
|
||||
set(storedCollections, 'value', [...storedCollections.value, collection])
|
||||
}
|
||||
|
||||
const removeCollection = (collectionId) => {
|
||||
set(storedCollections, 'value', storedCollections.value.filter(item => item.id !== collectionId))
|
||||
}
|
||||
|
||||
const updateCollection = (collection) => {
|
||||
const index = storedCollections.value.findIndex(item => item.id === collection.id)
|
||||
if (index !== -1) {
|
||||
set(storedCollections.value, index, collection)
|
||||
} else {
|
||||
addCollection(collection)
|
||||
}
|
||||
}
|
||||
|
||||
// Actions
|
||||
const fetchCollectionsByResource = async ({ resourceType, resourceId }) => {
|
||||
const collections = await getCollectionsByResourceService(resourceType, resourceId)
|
||||
|
||||
addCollections(collections)
|
||||
}
|
||||
|
||||
const createCollection = async ({ baseResourceType, baseResourceId, resourceType, resourceId, name }) => {
|
||||
const collection = await createCollectionService(baseResourceType, baseResourceId, name)
|
||||
|
||||
addCollection(collection)
|
||||
await addResourceToCollection({
|
||||
collectionId: collection.id,
|
||||
resourceType,
|
||||
resourceId,
|
||||
})
|
||||
}
|
||||
|
||||
const renameCollection = async ({ collectionId, name }) => {
|
||||
const collection = await renameCollectionService(collectionId, name)
|
||||
|
||||
updateCollection(collection)
|
||||
}
|
||||
|
||||
const addResourceToCollection = async ({ collectionId, resourceType, resourceId }) => {
|
||||
const collection = await addResourceService(collectionId, resourceType, String(resourceId))
|
||||
|
||||
updateCollection(collection)
|
||||
}
|
||||
|
||||
const removeResourceFromCollection = async ({ collectionId, resourceType, resourceId }) => {
|
||||
const collection = await removeResourceService(collectionId, resourceType, String(resourceId))
|
||||
|
||||
if (collection.resources.length > 0) {
|
||||
updateCollection(collection)
|
||||
} else {
|
||||
removeCollection(collectionId)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
storedCollections,
|
||||
fetchCollectionsByResource,
|
||||
createCollection,
|
||||
renameCollection,
|
||||
addResourceToCollection,
|
||||
removeResourceFromCollection,
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ export { default as NcBreadcrumbs } from './NcBreadcrumbs/index.js'
|
|||
export { default as NcButton } from './NcButton/index'
|
||||
export { default as NcCheckboxRadioSwitch } from './NcCheckboxRadioSwitch/index.js'
|
||||
export { default as NcChip } from './NcChip/index'
|
||||
export { default as NcCollectionList, NcCollectionListItem } from './NcCollectionList/index.js'
|
||||
export { default as NcCollectionList } from './NcCollectionList/index.js'
|
||||
export { default as NcColorPicker } from './NcColorPicker/index.js'
|
||||
export { default as NcContent } from './NcContent/index.js'
|
||||
export { default as NcCounterBubble } from './NcCounterBubble/index.js'
|
||||
|
|
|
@ -120,6 +120,7 @@ module.exports = async () => {
|
|||
'src/components/NcAppSidebar*/*.vue',
|
||||
'src/components/NcBreadcrumb*/*.vue',
|
||||
'src/components/NcCheckboxRadioSwitch/NcCheckboxContent.vue',
|
||||
'src/components/NcCollectionList/!(NcCollectionList).vue',
|
||||
'src/components/NcContent/*.vue',
|
||||
'src/components/NcDashboard*/*.vue',
|
||||
'src/components/NcDialog*/*.vue',
|
||||
|
|
Загрузка…
Ссылка в новой задаче