Signed-off-by: Julius Härtl <jus@bitgrid.net> Move to vuex Signed-off-by: Julius Härtl <jus@bitgrid.net>tags/v16.0.0alpha1
@@ -17,7 +17,8 @@ | |||
"dependencies": { | |||
"nextcloud-axios": "^0.1.3", | |||
"nextcloud-vue": "^0.5.0", | |||
"vue": "^2.5.17" | |||
"vue": "^2.5.17", | |||
"vuex": "^3.1.0" | |||
}, | |||
"devDependencies": { | |||
"@babel/core": "^7.1.0", |
@@ -0,0 +1,36 @@ | |||
<!-- | |||
- @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> | |||
- | |||
- @author Julius Härtl <jus@bitgrid.net> | |||
- | |||
- @license GNU AGPL version 3 or any later version | |||
- | |||
- This program is free software: you can redistribute it and/or modify | |||
- it under the terms of the GNU Affero General Public License as | |||
- published by the Free Software Foundation, either version 3 of the | |||
- License, or (at your option) any later version. | |||
- | |||
- This program is distributed in the hope that it will be useful, | |||
- but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
- GNU Affero General Public License for more details. | |||
- | |||
- You should have received a copy of the GNU Affero General Public License | |||
- along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
- | |||
--> | |||
<template> | |||
<collection-list type="files"></collection-list> | |||
</template> | |||
<script> | |||
import CollectionList from './components/CollectionList' | |||
export default { | |||
name: 'CollaborationView', | |||
components: { | |||
CollectionList | |||
} | |||
} | |||
</script> |
@@ -21,46 +21,19 @@ | |||
*/ | |||
import Vue from 'vue' | |||
import Vuex from 'vuex' | |||
import { PopoverMenu } from 'nextcloud-vue' | |||
import ClickOutside from 'vue-click-outside' | |||
import { VTooltip } from 'v-tooltip' | |||
import { Store } from './services/collections'; | |||
Vue.prototype.t = t; | |||
Vue.component('PopoverMenu', PopoverMenu) | |||
Vue.directive('ClickOutside', ClickOutside) | |||
Vue.directive('Tooltip', VTooltip) | |||
Vue.use(Vuex); | |||
import View from './views/CollaborationView' | |||
import View from './CollaborationView' | |||
let selectAction = {}; | |||
let icons = {}; | |||
let types = {}; | |||
console.log('register types'); | |||
/* TODO: temporary data for testing */ | |||
window.OCP.Collaboration.registerType('calendar', { | |||
action: () => { | |||
return new Promise((resolve, reject) => { | |||
var id = window.prompt("calendar id", "1"); | |||
resolve(id); | |||
}) | |||
}, | |||
icon: 'icon-calendar-dark', | |||
typeString: 'calendar', | |||
link: (id) => '#' + id, | |||
}); | |||
window.OCP.Collaboration.registerType('contact', { | |||
action: () => { | |||
return new Promise((resolve, reject) => { | |||
var id = window.prompt("contacts id", "1"); | |||
resolve(id); | |||
}) | |||
}, | |||
icon: 'icon-contacts-dark', | |||
link: (id) => '#' + id, | |||
/** used in "Link to a {typeString}" */ | |||
typeString: 'contact' | |||
}); | |||
export { Vue, View } | |||
export { Vue, View, Store } |
@@ -0,0 +1,198 @@ | |||
<!-- | |||
- @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> | |||
- | |||
- @author Julius Härtl <jus@bitgrid.net> | |||
- | |||
- @license GNU AGPL version 3 or any later version | |||
- | |||
- This program is free software: you can redistribute it and/or modify | |||
- it under the terms of the GNU Affero General Public License as | |||
- published by the Free Software Foundation, either version 3 of the | |||
- License, or (at your option) any later version. | |||
- | |||
- This program is distributed in the hope that it will be useful, | |||
- but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
- GNU Affero General Public License for more details. | |||
- | |||
- You should have received a copy of the GNU Affero General Public License | |||
- along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
- | |||
--> | |||
<template> | |||
<ul id="shareWithList" class="shareWithList" v-if="collections"> | |||
<li @click="showSelect"> | |||
<div class="avatar"><span class="icon-category-integration icon-white"></span></div> | |||
<multiselect v-model="value" :options="options" :placeholder="placeholder" tag-placeholder="Create a new collection" ref="select" @select="select" @search-change="search" label="title" track-by="title" :reset-after="true" :limit="5"> | |||
<template slot="singleLabel" slot-scope="props"> | |||
<span class="option__desc"> | |||
<span class="option__title">{{ props.option.title }}</span> | |||
</span> | |||
</template> | |||
<template slot="option" slot-scope="props"> | |||
<span class="option__wrapper"> | |||
<span v-if="props.option.class" :class="props.option.class" class="avatar"></span> | |||
<avatar v-else :displayName="props.option.title" :allowPlaceholder="true"></avatar> | |||
<span class="option__title">{{ props.option.title }}</span> | |||
</span> | |||
</template> | |||
</multiselect> | |||
</li> | |||
<collection-list-item v-for="collection in collections" :collection="collection" :key="collection.id" /> | |||
</ul> | |||
</template> | |||
<style lang="scss" scoped> | |||
.multiselect { | |||
width: 100%; | |||
margin-left: 3px; | |||
} | |||
span.avatar { | |||
padding: 16px; | |||
display: block; | |||
background-repeat: no-repeat; | |||
background-position: center; | |||
opacity: 0.7; | |||
&:hover { | |||
opacity: 1; | |||
} | |||
} | |||
/** TODO provide white icon in core */ | |||
.icon-category-integration.icon-white { | |||
filter: invert(100%); | |||
padding: 16px; | |||
display: block; | |||
background-repeat: no-repeat; | |||
background-position: center; | |||
} | |||
.option__wrapper { | |||
display: flex; | |||
.avatar { | |||
display: block; | |||
background-color: var(--color-background-darker) !important; | |||
} | |||
.option__title { | |||
padding: 4px; | |||
} | |||
} | |||
</style> | |||
<style lang="scss"> | |||
/** TODO check why this doesn't work when scoped */ | |||
.shareWithList .multiselect:not(.multiselect--active ) .multiselect__tags { | |||
border: none !important; | |||
input::placeholder { | |||
color: var(--color-main-text); | |||
} | |||
} | |||
</style> | |||
<script> | |||
import { Multiselect, Avatar } from 'nextcloud-vue'; | |||
import CollectionListItem from '../components/CollectionListItem'; | |||
const METHOD_CREATE_COLLECTION = 0; | |||
const METHOD_ADD_TO_COLLECTION = 1; | |||
export default { | |||
name: 'CollectionList', | |||
components: { | |||
CollectionListItem, | |||
Avatar, | |||
Multiselect: Multiselect, | |||
}, | |||
props: { | |||
'type': { | |||
type: String, | |||
default: '' | |||
} | |||
}, | |||
data() { | |||
return { | |||
selectIsOpen: false, | |||
generatingCodes: false, | |||
codes: undefined, | |||
value: null, | |||
model: {}, | |||
searchCollections: [] | |||
}; | |||
}, | |||
mounted() { | |||
this.$store.dispatch('fetchCollectionsByResource', { | |||
resourceType: this.type, | |||
resourceId: this.$root.model.id | |||
}) | |||
}, | |||
computed: { | |||
collections() { | |||
return this.$store.getters.collectionsByResource(this.type, this.$root.model.id) | |||
}, | |||
placeholder() { | |||
return t('files_sharing', 'Add to a collection'); | |||
}, | |||
options() { | |||
let options = []; | |||
let types = window.OCP.Collaboration.getTypes().sort(); | |||
for(let type in types) { | |||
options.push({ | |||
method: METHOD_CREATE_COLLECTION, | |||
type: types[type], | |||
title: window.OCP.Collaboration.getLabel(types[type]), | |||
class: window.OCP.Collaboration.getIcon(types[type]), | |||
action: () => window.OCP.Collaboration.trigger(types[type]) | |||
}) | |||
} | |||
for(let index in this.searchCollections) { | |||
if (this.collections.findIndex((collection) => collection.id === this.searchCollections[index].id) === -1) { | |||
options.push({ | |||
method: METHOD_ADD_TO_COLLECTION, | |||
title: this.searchCollections[index].name, | |||
collectionId: this.searchCollections[index].id | |||
}) | |||
} | |||
} | |||
return options; | |||
} | |||
}, | |||
methods: { | |||
select(selectedOption, id) { | |||
if (selectedOption.method === METHOD_CREATE_COLLECTION) { | |||
selectedOption.action().then((id) => { | |||
this.$store.dispatch('createCollection', { | |||
baseResourceType: this.type, | |||
baseResourceId: this.$root.model.id, | |||
resourceType: selectedOption.type, | |||
resourceId: id, | |||
name: this.$root.model.name, | |||
}) | |||
}).catch((e) => { | |||
console.error('No resource selected', e); | |||
}); | |||
} | |||
if (selectedOption.method === METHOD_ADD_TO_COLLECTION) { | |||
this.$store.dispatch('addResourceToCollection', { | |||
collectionId: selectedOption.collectionId, resourceType: this.type, resourceId: this.$root.model.id | |||
}) | |||
} | |||
}, | |||
search(query) { | |||
this.$store.dispatch('search', query).then((collections) => { | |||
this.searchCollections = collections | |||
}) | |||
}, | |||
showSelect() { | |||
this.selectIsOpen = true | |||
this.$refs.select.$el.focus() | |||
}, | |||
hideSelect() { | |||
this.selectIsOpen = false | |||
}, | |||
isVueComponent(object) { | |||
return object._isVue | |||
} | |||
} | |||
} | |||
</script> |
@@ -21,12 +21,16 @@ | |||
--> | |||
<template> | |||
<li class="collection-list"> | |||
<li class="collection-list" v-click-outside="hideDetails"> | |||
<avatar :displayName="collection.name" :allowPlaceholder="true"></avatar> | |||
<span class="username" title="">{{ collection.name }}</span> | |||
<span class="username" title="" @click="showDetails" v-if="this.newName === ''">{{ collection.name }}</span> | |||
<form v-else @submit.prevent="renameCollection"> | |||
<input type="text" v-model="newName" autocomplete="off" autocapitalize="off"> | |||
<input type="submit" value="" class="icon-confirm"> | |||
</form> | |||
<transition name="fade"> | |||
<div class="linked-icons" v-if="!detailsOpen"> | |||
<a v-for="resource in collection.resources" :href="getLink(resource)" v-tooltip="resource.name" :key="resource.id"><span :class="getIcon(resource)"></span></a> | |||
<a v-for="resource in collection.resources" :href="resource.link" v-tooltip="resource.name" :key="resource.id"><span :class="getIcon(resource)"></span></a> | |||
</div> | |||
</transition> | |||
@@ -40,10 +44,10 @@ | |||
</div> | |||
</span> | |||
<transition name="fade"> | |||
<ul class="resource-list-details" v-if="detailsOpen" v-click-outside="hideDetails"> | |||
<ul class="resource-list-details" v-if="detailsOpen"> | |||
<li v-for="resource in collection.resources"> | |||
<a :href="getLink(resource)"><span :class="getIcon(resource)"></span> {{ resource.name || '' }}</a> | |||
<span class="icon-delete"></span> | |||
<a :href="resource.link"><span :class="getIcon(resource)"></span><span class="resource-name">{{ resource.name || '' }}</span></a> | |||
<span class="icon-delete" @click="removeResource(collection, resource)"></span> | |||
</li> | |||
</ul> | |||
</transition> | |||
@@ -51,6 +55,8 @@ | |||
</template> | |||
<script> | |||
import service from '../services/collections'; | |||
import { Avatar } from 'nextcloud-vue'; | |||
export default { | |||
@@ -66,7 +72,8 @@ | |||
data() { | |||
return { | |||
isOpen: false, | |||
detailsOpen: false | |||
detailsOpen: false, | |||
newName: '', | |||
} | |||
}, | |||
computed: { | |||
@@ -81,21 +88,14 @@ | |||
text: t('files_sharing', 'Details'), | |||
}, | |||
{ | |||
action: () => { }, | |||
action: () => this.openRename(), | |||
icon: 'icon-rename', | |||
text: t('files_sharing', 'Rename collection'), | |||
},{ | |||
action: () => { }, | |||
icon: 'icon-delete', | |||
text: t('files_sharing', 'Remove collection'), | |||
} | |||
] | |||
}, | |||
getIcon() { | |||
return (resource) => [window.OCP.Collaboration.getIcon(resource.type), resource.iconClass] | |||
}, | |||
getLink() { | |||
return (resource) => window.OCP.Collaboration.getLink(resource.type, resource.id) | |||
return (resource) => [resource.iconClass] | |||
} | |||
}, | |||
methods: { | |||
@@ -108,8 +108,27 @@ | |||
toggle() { | |||
this.isOpen = !this.isOpen | |||
}, | |||
showDetails() { | |||
this.detailsOpen = true | |||
}, | |||
hideDetails() { | |||
this.detailsOpen = false | |||
}, | |||
removeResource(collection, resource) { | |||
this.$store.dispatch('removeResource', { | |||
collectionId: collection.id, resourceType: resource.type, resourceId: resource.id | |||
}) | |||
}, | |||
openRename() { | |||
this.newName = this.collection.name; | |||
}, | |||
renameCollection() { | |||
this.$store.dispatch('renameCollection', { | |||
collectionId: this.collection.id, | |||
name: this.newName | |||
}).then((collection) => { | |||
this.newName = ''; | |||
}); | |||
} | |||
} | |||
} | |||
@@ -138,7 +157,15 @@ | |||
} | |||
.collection-list { | |||
flex-wrap: wrap; | |||
height: auto; | |||
form, .username { | |||
flex-basis: 10%; | |||
flex-grow: 1; | |||
display: flex; | |||
} | |||
.resource-list-details { | |||
flex-basis: 100%; | |||
width: 100%; | |||
li { | |||
display: flex; | |||
@@ -146,14 +173,23 @@ | |||
a { | |||
flex-grow: 1; | |||
padding: 3px; | |||
max-width: calc(100% - 30px); | |||
display: flex; | |||
} | |||
} | |||
span { | |||
display: inline-block; | |||
padding: 8px; | |||
vertical-align: top; | |||
margin-right: 10px; | |||
} | |||
span.resource-name { | |||
text-overflow: ellipsis; | |||
overflow: hidden; | |||
position: relative; | |||
vertical-align: top; | |||
white-space: nowrap; | |||
flex-grow: 1; | |||
} | |||
} | |||
} | |||
</style> |
@@ -1,10 +1,5 @@ | |||
// CSP config for webpack dynamic chunk loading | |||
// eslint-disable-next-line | |||
__webpack_nonce__ = btoa(OC.requestToken) | |||
// Correct the root of the app for chunk loading | |||
// OC.linkTo matches the apps folders | |||
// eslint-disable-next-line | |||
__webpack_nonce__ = btoa(OC.requestToken) | |||
__webpack_public_path__ = OC.linkTo('files_sharing', 'js/dist/') | |||
import '../js/app' | |||
@@ -26,10 +21,8 @@ window.OCP.Collaboration.registerType('files', { | |||
}, false); | |||
}) | |||
}, | |||
link: (id) => OC.generateUrl('/f/') + id, | |||
icon: 'nav-icon-files', | |||
/** used in "Link to a {typeString}" */ | |||
typeString: 'file' | |||
typeString: t('files_sharing', 'file') | |||
}); | |||
window.OCA.Sharing = OCA.Sharing |
@@ -21,39 +21,165 @@ | |||
*/ | |||
import axios from 'nextcloud-axios'; | |||
import Vuex from 'vuex'; | |||
import Vue from 'vue'; | |||
class Service { | |||
constructor() { | |||
this.service = axios.create(); | |||
this.http = axios; | |||
this.baseUrl = OC.linkToOCS(`collaboration/resources`); | |||
} | |||
listCollection(collectionId) { | |||
return this.service.get(`/collaboration/resources/collections/${collectionId}`) | |||
return this.http.get(`${this.baseUrl}collections/${collectionId}`) | |||
} | |||
addResource(collectionId, resource) { | |||
return this.service.post(`/collaboration/resources/collections/${collectionId}`) | |||
renameCollection(collectionId, collectionName) { | |||
const resourceBase = OC.linkToOCS(`collaboration/resources/collections`, 2); | |||
return this.http.put(`${resourceBase}${collectionId}?format=json`, { | |||
collectionName | |||
}).then(result => { | |||
return result.data.ocs.data; | |||
}) | |||
} | |||
removeResource() { | |||
return this.service.post(`/collaboration/resources/collections/${collectionId}`) | |||
getCollectionsByResource(resourceType, resourceId) { | |||
const resourceBase = OC.linkToOCS(`collaboration/resources/${resourceType}`); | |||
return this.http.get(`${resourceBase}${resourceId}?format=json`) | |||
.then(result => { | |||
return result.data.ocs.data; | |||
}) | |||
.catch(error => { | |||
console.error(error); | |||
return Promise.reject(error); | |||
}); | |||
} | |||
createCollectionOnResource(resourceType, resourceId) { | |||
return this.service.post(`/collaboration/resources/${resourceType}/${resourceId}`) | |||
createCollection(resourceType, resourceId, name) { | |||
const resourceBase = OC.linkToOCS(`collaboration/resources/${resourceType}`, 2); | |||
return this.http.post(`${resourceBase}${resourceId}?format=json`, { | |||
name: name | |||
}) | |||
.then((response) => { | |||
return response.data.ocs.data | |||
}) | |||
.catch(error => { | |||
console.error(error); | |||
return Promise.reject(error); | |||
}); | |||
} | |||
getCollectionByResource(resourceType, resourceId) { | |||
return this.service.get(`/collaboration/resources/${resourceType}/${resourceId}`) | |||
addResource(collectionId, resourceType, resourceId) { | |||
resourceId = '' + resourceId; | |||
const resourceBase = OC.linkToOCS(`collaboration/resources/collections`, 2); | |||
return this.http.post(`${resourceBase}${collectionId}?format=json`, { | |||
resourceType, | |||
resourceId | |||
}).then((response) => { | |||
return response.data.ocs.data | |||
}); | |||
} | |||
getProviders() { | |||
removeResource(collectionId, resourceType, resourceId) { | |||
return this.http.delete(`${this.baseUrl}/collections/${collectionId}`, { params: { resourceType, resourceId } } ) | |||
.then((response) => { | |||
return response.data.ocs.data | |||
}); | |||
} | |||
search(query) { | |||
const searchBase = OC.linkToOCS(`collaboration/resources/collections/search`); | |||
return this.http.get(`${searchBase}%25${query}%25?format=json`) | |||
.then((response) => { | |||
return response.data.ocs.data | |||
}); | |||
} | |||
search() { | |||
} | |||
const service = new Service(); | |||
const StoreModule = { | |||
state: { | |||
collections: [] | |||
}, | |||
mutations: { | |||
addCollections (state, collections) { | |||
state.collections = collections; | |||
}, | |||
addCollection (state, collection) { | |||
state.collections.push(collection) | |||
}, | |||
removeCollection (state, collectionId) { | |||
state.collections = state.collections.filter(item => item.id !== collectionId) | |||
}, | |||
updateCollection(state, collection) { | |||
let index = state.collections.findIndex((_item) => _item.id === collection.id) | |||
if (index !== -1) { | |||
Vue.set(state.collections, index, collection); | |||
} else { | |||
state.collections.push(collection) | |||
} | |||
} | |||
}, | |||
getters: { | |||
collectionsByResource(state) { | |||
return (resourceType, resourceId) => { | |||
return state.collections.filter((collection) => { | |||
return typeof collection.resources.find((resource) => resource && resource.id === ''+resourceId && resource.type === resourceType) !== 'undefined' | |||
}) | |||
} | |||
}, | |||
getSearchResults(state) { | |||
return (term) => { | |||
return state.collections.filter((collection) => collection.name.contains(term)) | |||
} | |||
} | |||
}, | |||
actions: { | |||
fetchCollectionsByResource(context, {resourceType, resourceId}) { | |||
return service.getCollectionsByResource(resourceType, resourceId).then((collections) => { | |||
context.commit('addCollections', collections) | |||
return collections; | |||
}); | |||
}, | |||
createCollection(context, {baseResourceType, baseResourceId, resourceType, resourceId, name}) { | |||
return service.createCollection(baseResourceType, baseResourceId, name).then((collection) => { | |||
context.commit('addCollection', collection) | |||
context.dispatch('addResourceToCollection', { | |||
collectionId: collection.id, | |||
resourceType, resourceId | |||
}) | |||
}) | |||
}, | |||
renameCollection(context, {collectionId, name}) { | |||
return service.renameCollection(collectionId, name).then((collection) => { | |||
context.commit('updateCollection', collection) | |||
return collection | |||
}) | |||
}, | |||
addResourceToCollection(context, {collectionId, resourceType, resourceId}) { | |||
return service.addResource(collectionId, resourceType, resourceId).then((collection) => { | |||
context.commit('updateCollection', collection) | |||
return collection | |||
}) | |||
}, | |||
removeResource(context, {collectionId, resourceType, resourceId}) { | |||
return service.removeResource(collectionId, resourceType, resourceId).then((collection) => { | |||
if (collection.resources.length > 0) { | |||
context.commit('updateCollection', collection) | |||
} else { | |||
context.commit('removeCollection', collectionId) | |||
} | |||
}) | |||
}, | |||
search(context, query) { | |||
return service.search(query) | |||
} | |||
} | |||
} | |||
export default new Service; | |||
const Store = () => new Vuex.Store(StoreModule); | |||
export default service; | |||
export { StoreModule, Store }; |
@@ -87,6 +87,7 @@ | |||
var vm = new Resources.Vue({ | |||
el: '#collaborationResources', | |||
render: h => h(Resources.View), | |||
store: Resources.Store(), | |||
data: { | |||
model: this.model.toJSON() | |||
}, |
@@ -1,188 +0,0 @@ | |||
<template> | |||
<div :class="{'icon-loading': !collections}"> | |||
<ul id="shareWithList" class="shareWithList" v-if="collections"> | |||
<li @click="showSelect"> | |||
<div class="avatar"><span class="icon-category-integration icon-white"></span></div> | |||
<multiselect v-model="value" :options="options" :placeholder="placeholder" tag-placeholder="Create a new collection" ref="select" @select="select" label="title" track-by="title" :reset-after="true"> | |||
<template slot="singleLabel" slot-scope="props"> | |||
<span class="option__desc"> | |||
<span class="option__title">{{ props.option.title }}</span></span> | |||
</template> | |||
<template slot="option" slot-scope="props"> | |||
<span class="option__wrapper"> | |||
<span v-if="props.option.class" :class="props.option.class" class="avatar"></span> | |||
<avatar v-else :displayName="props.option.title" :allowPlaceholder="true"></avatar> | |||
<span class="option__title">{{ props.option.title }}</span> | |||
</span> | |||
</template> | |||
</multiselect> | |||
</li> | |||
<collection-list-item v-for="collection in collections" :collection="collection" :key="collection.id" /> | |||
</ul> | |||
</div> | |||
</template> | |||
<style lang="scss" scoped> | |||
.multiselect { | |||
width: 100%; | |||
margin-left: 3px; | |||
} | |||
span.avatar { | |||
padding: 16px; | |||
display: block; | |||
background-repeat: no-repeat; | |||
background-position: center; | |||
opacity: 0.7; | |||
&:hover { | |||
opacity: 1; | |||
} | |||
} | |||
/** TODO provide white icon in core */ | |||
.icon-category-integration.icon-white { | |||
filter: invert(100%); | |||
padding: 16px; | |||
display: block; | |||
background-repeat: no-repeat; | |||
background-position: center; | |||
} | |||
.option__wrapper { | |||
display: flex; | |||
.avatar { | |||
display: block; | |||
background-color: var(--color-background-darker) !important; | |||
} | |||
.option__title { | |||
padding: 4px; | |||
} | |||
} | |||
</style> | |||
<style lang="scss"> | |||
/** TODO check why this doesn't work when scoped */ | |||
.shareWithList .multiselect:not(.multiselect--active ) .multiselect__tags { | |||
border: none !important; | |||
input::placeholder { | |||
color: var(--color-main-text); | |||
} | |||
} | |||
</style> | |||
<script> | |||
import { Multiselect, Avatar } from 'nextcloud-vue'; | |||
import CollectionListItem from '../components/CollectionListItem'; | |||
import axios from 'nextcloud-axios'; | |||
export default { | |||
name: 'CollaborationView', | |||
components: { | |||
CollectionListItem, | |||
Avatar, | |||
Multiselect: Multiselect, | |||
}, | |||
data() { | |||
return { | |||
selectIsOpen: false, | |||
generatingCodes: false, | |||
codes: undefined, | |||
value: null, | |||
model: {}, | |||
collections: null | |||
}; | |||
}, | |||
mounted() { | |||
let resourceId = this.$root.model.id | |||
/** TODO move to service */ | |||
const resourceBase = OC.linkToOCS(`collaboration/resources/files`); | |||
axios.get(`${resourceBase}${resourceId}?format=json`, { | |||
headers: { | |||
'OCS-APIRequest': true, | |||
'Content-Type': 'application/json; charset=UTF-8' | |||
} | |||
}).then((response) => { | |||
this.collections = response.data.ocs.data | |||
}); | |||
}, | |||
computed: { | |||
placeholder() { | |||
return t('files_sharing', 'Add to a collection'); | |||
}, | |||
options() { | |||
let options = []; | |||
let types = window.OCP.Collaboration.getTypes(); | |||
for(let type in types) { | |||
options.push({ | |||
type: types[type], | |||
title: window.OCP.Collaboration.getLabel(types[type]), | |||
class: window.OCP.Collaboration.getIcon(types[type]), | |||
action: () => window.OCP.Collaboration.trigger(types[type]) | |||
}) | |||
} | |||
for(let index in this.collections) { | |||
options.push({ | |||
title: this.collections[index].name | |||
}) | |||
} | |||
return options; | |||
} | |||
}, | |||
created: function() { | |||
}, | |||
methods: { | |||
select(selectedOption, id) { | |||
selectedOption.action().then((id) => { | |||
console.log('Create a new collection with') | |||
console.log('This file ', this.$root.model.id) | |||
console.log('Selected resource ', selectedOption.type, id) | |||
this.createCollection(this.$root.model.id, selectedOption.type, id) | |||
}).catch((e) => { | |||
console.error('No resource selected'); | |||
}); | |||
}, | |||
showSelect() { | |||
this.selectIsOpen = true | |||
this.$refs.select.$el.focus() | |||
}, | |||
hideSelect() { | |||
this.selectIsOpen = false | |||
}, | |||
isVueComponent(object) { | |||
return object._isVue | |||
}, | |||
createCollection(resourceIdBase, resourceType, resourceId) { | |||
/** TODO move to service */ | |||
const resourceBase = OC.linkToOCS(`collaboration/resources/files`, 2); | |||
axios.post(`${resourceBase}${resourceIdBase}?format=json`, { | |||
name: 'Example collection' | |||
}, { | |||
headers: { | |||
'OCS-APIRequest': true, | |||
'Content-Type': 'application/json; charset=UTF-8' | |||
} | |||
}).then((response) => { | |||
let newCollection = response.data.ocs.data | |||
console.log('Add new collection', newCollection) | |||
this.collections.push(newCollection) | |||
this.addResourceToCollection(newCollection.id, resourceType.toString(), resourceId.toString()) | |||
}); | |||
}, | |||
addResourceToCollection(collectionId, resourceType, resourceId) { | |||
/** TODO move to service */ | |||
const resourceBase = OC.linkToOCS(`collaboration/resources/collections`, 2); | |||
return axios.post(`${resourceBase}${collectionId}?format=json`, { | |||
resourceType, | |||
resourceId | |||
}, { | |||
headers: { | |||
'OCS-APIRequest': true, | |||
'Content-Type': 'application/json; charset=UTF-8' | |||
} | |||
}).then((response) => { | |||
console.log('Add new collection', response.data.ocs.data) | |||
this.collections.find((_item) => _item.id === collectionId).resources.push(response.data.ocs.data) | |||
}); | |||
} | |||
} | |||
} | |||
</script> |
@@ -193,7 +193,7 @@ class CollaborationResourcesController extends OCSController { | |||
return [ | |||
'id' => $collection->getId(), | |||
'name' => $collection->getName(), | |||
'resources' => array_map([$this, 'prepareResources'], $collection->getResources()), | |||
'resources' => array_values(array_filter(array_map([$this, 'prepareResources'], $collection->getResources()))), | |||
]; | |||
} | |||
@@ -49,7 +49,7 @@ export default { | |||
return Object.keys(types); | |||
}, | |||
getIcon(type) { | |||
return types[type].icon; | |||
return types[type].icon || ''; | |||
}, | |||
getLabel(type) { | |||
return t('files_sharing', 'Link to a {label}', { label: types[type].typeString || type }, 1) |