mirror of https://github.com/nextcloud/bookmarks
329 lines
9.6 KiB
Vue
329 lines
9.6 KiB
Vue
<!--
|
|
- Copyright (c) 2020. The Nextcloud Bookmarks contributors.
|
|
-
|
|
- This file is licensed under the Affero General Public License version 3 or later. See the COPYING file.
|
|
-->
|
|
|
|
<template>
|
|
<NcAppSidebar v-if="isActive"
|
|
class="sidebar"
|
|
:title="folder.title"
|
|
:active.sync="activeTab"
|
|
@close="onClose">
|
|
<NcAppSidebarTab id="folder-details"
|
|
:name="t('bookmarks', 'Details')"
|
|
icon="icon-info"
|
|
:order="0">
|
|
<h3>{{ t('bookmarks', 'Owner') }}</h3>
|
|
<NcUserBubble :user="folder.userId" :display-name="folder.userId" />
|
|
<h3>{{ t('bookmarks', 'Bookmarks') }}</h3>
|
|
{{ bookmarkCount }}
|
|
</NcAppSidebarTab>
|
|
<NcAppSidebarTab v-if="isSharable"
|
|
id="folder-sharing"
|
|
:name="t('bookmarks', 'Sharing')"
|
|
icon="icon-shared"
|
|
:order="1">
|
|
<div class="participant-select">
|
|
<figure :class="{'icon-user': true, 'share__avatar': true }" />
|
|
<NcMultiselect v-model="participant"
|
|
label="displayName"
|
|
track-by="user"
|
|
class="participant-select__selection"
|
|
:user-select="true"
|
|
:options="participantSearchResults"
|
|
:loading="isSearching"
|
|
:placeholder="t('bookmarks', 'Select a user or group')"
|
|
@select="onAddShare"
|
|
@search-change="onParticipantSearch" />
|
|
</div>
|
|
<div class="share">
|
|
<figure :class="{'icon-public': true, 'share__avatar': true, active: publicLink }" />
|
|
<h3 class="share__title">
|
|
{{ t('bookmarks', 'Share link') }}
|
|
</h3>
|
|
<div class="share__privs">
|
|
<div v-if="publicLink"
|
|
v-tooltip="t('bookmarks', 'Reading allowed')"
|
|
:aria-label="t('bookmarks', 'Reading allowed')">
|
|
<EyeIcon :size="20"
|
|
:fill-color="colorMainText" />
|
|
</div>
|
|
</div>
|
|
<NcActions class="share__actions">
|
|
<template v-if="publicLink">
|
|
<NcActionButton icon="icon-clippy" @click="onCopyPublicLink">
|
|
{{ t('bookmarks', 'Copy link') }}
|
|
</NcActionButton>
|
|
<NcActionButton icon="icon-clippy" @click="onCopyRssLink">
|
|
{{ t('bookmarks', 'Copy RSS feed') }}
|
|
</NcActionButton>
|
|
<NcActionSeparator />
|
|
<NcActionButton icon="icon-delete" @click="onDeletePublicLink">
|
|
{{ t('bookmarks', 'Delete link') }}
|
|
</NcActionButton>
|
|
</template>
|
|
<NcActionButton v-else icon="icon-add" @click="onAddPublicLink">
|
|
{{ t('bookmarks', 'Create public link') }}
|
|
</NcActionButton>
|
|
</NcActions>
|
|
</div>
|
|
<div v-for="share of shares" :key="share.id">
|
|
<div class="share">
|
|
<NcAvatar :user="share.participant" class="share__avatar" :size="44" />
|
|
<h3 class="share__title">
|
|
{{ share.participant }}
|
|
</h3>
|
|
<div class="share__privs">
|
|
<div v-if="share.canShare"
|
|
v-tooltip="t('bookmarks', 'Resharing allowed')"
|
|
:aria-label="t('bookmarks','Resharing allowed')">
|
|
<ShareAllIcon :size="20"
|
|
:fill-color="colorMainText" />
|
|
</div>
|
|
<div v-if="share.canWrite"
|
|
v-tooltip="t('bookmarks','Editing allowed')"
|
|
:aria-label="t('bookmarks','Editing allowed')">
|
|
<PencilIcon :size="20"
|
|
:fill-color="colorMainText" />
|
|
</div>
|
|
<div v-tooltip="t('bookmarks','Reading allowed')"
|
|
:aria-label="t('bookmarks', 'Reading allowed')">
|
|
<EyeIcon :size="20"
|
|
:fill-color="colorMainText" />
|
|
</div>
|
|
</div>
|
|
<NcActions class="share__actions">
|
|
<NcActionCheckbox :checked="share.canWrite" @update:checked="onEditShare(share.id, {canWrite: $event, canShare: share.canShare})">
|
|
{{ t('bookmarks', 'Allow editing') }}
|
|
</NcActionCheckbox>
|
|
<NcActionCheckbox :checked="share.canShare" @update:checked="onEditShare(share.id, {canWrite: share.canWrite, canShare: $event})">
|
|
{{ t('bookmarks', 'Allow resharing') }}
|
|
</NcActionCheckbox>
|
|
<NcActionButton icon="icon-delete" @click="onDeleteShare(share.id)">
|
|
{{ t('bookmarks', 'Remove share') }}
|
|
</NcActionButton>
|
|
</NcActions>
|
|
</div>
|
|
</div>
|
|
</NcAppSidebarTab>
|
|
<NcAppSidebarTab v-if="!isPublic"
|
|
id="bookmark-projects"
|
|
:name="t('bookmarks', 'Projects')"
|
|
icon="icon-projects"
|
|
:order="2">
|
|
<CollectionList v-if="folder"
|
|
:id="''+folder.id"
|
|
:name="folder.title"
|
|
type="bookmarks-folder" />
|
|
</NcAppSidebarTab>
|
|
</NcAppSidebar>
|
|
</template>
|
|
<script>
|
|
import { NcAppSidebar, NcUserBubble, NcActionSeparator, NcActionCheckbox, NcActionButton, NcActions, NcMultiselect, NcAvatar, NcAppSidebarTab } from '@nextcloud/vue'
|
|
import { getCurrentUser } from '@nextcloud/auth'
|
|
import { generateUrl, generateOcsUrl } from '@nextcloud/router'
|
|
import axios from '@nextcloud/axios'
|
|
import copy from 'copy-text-to-clipboard'
|
|
import { actions, mutations } from '../store/index.js'
|
|
import EyeIcon from 'vue-material-design-icons/Eye.vue'
|
|
import PencilIcon from 'vue-material-design-icons/Pencil.vue'
|
|
import ShareAllIcon from 'vue-material-design-icons/ShareAll.vue'
|
|
import { CollectionList } from 'nextcloud-vue-collections'
|
|
|
|
export default {
|
|
name: 'SidebarFolder',
|
|
components: { NcAppSidebar, NcAppSidebarTab, NcAvatar, NcMultiselect, NcActionButton, NcActionCheckbox, NcActions, NcUserBubble, NcActionSeparator, EyeIcon, PencilIcon, ShareAllIcon, CollectionList },
|
|
data() {
|
|
return {
|
|
participantSearchResults: [],
|
|
participant: null,
|
|
isSearching: false,
|
|
activeTab: '',
|
|
}
|
|
},
|
|
computed: {
|
|
isActive() {
|
|
if (!this.$store.state.sidebar) return false
|
|
return this.$store.state.sidebar.type === 'folder'
|
|
},
|
|
folder() {
|
|
if (!this.isActive) return
|
|
const folders = this.$store.getters.getFolder(this.$store.state.sidebar.id)
|
|
const folder = folders[0]
|
|
if (folder.userId === getCurrentUser()) {
|
|
this.$store.dispatch(actions.LOAD_SHARES_OF_FOLDER, folder.id)
|
|
this.$store.dispatch(actions.LOAD_PUBLIC_LINK, folder.id)
|
|
}
|
|
return folder
|
|
},
|
|
isOwner() {
|
|
if (!this.folder) return
|
|
const currentUser = getCurrentUser()
|
|
return currentUser && this.folder.userId === currentUser.uid
|
|
},
|
|
permissions() {
|
|
return this.$store.getters.getPermissionsForFolder(this.folder.id)
|
|
},
|
|
isSharable() {
|
|
if (!this.folder) return
|
|
return this.isOwner || (!this.isOwner && this.permissions.canShare)
|
|
},
|
|
isEditable() {
|
|
if (!this.folder) return
|
|
return this.isOwner || (!this.isOwner && this.permissions.canWrite)
|
|
},
|
|
shares() {
|
|
if (!this.folder) return
|
|
return this.$store.getters.getSharesOfFolder(this.folder.id)
|
|
},
|
|
token() {
|
|
if (!this.folder) return
|
|
return this.$store.getters.getTokenOfFolder(this.folder.id)
|
|
},
|
|
publicLink() {
|
|
if (!this.token) return
|
|
return window.location.origin + generateUrl('/apps/bookmarks/public/' + this.token)
|
|
},
|
|
rssLink() {
|
|
return (
|
|
window.location.origin
|
|
+ generateUrl(
|
|
'/apps/bookmarks/public/rest/v2/bookmark?'
|
|
+ new URLSearchParams(
|
|
Object.assign({}, this.$store.state.fetchState.query, {
|
|
format: 'rss',
|
|
page: -1,
|
|
token: this.token,
|
|
})
|
|
).toString()
|
|
)
|
|
)
|
|
},
|
|
bookmarkCount() {
|
|
return this.$store.state.countsByFolder[this.folder.id]
|
|
},
|
|
},
|
|
|
|
watch: {
|
|
'$store.state.sidebar.tab'(newActiveTab) {
|
|
this.activeTab = newActiveTab
|
|
},
|
|
},
|
|
|
|
methods: {
|
|
onClose() {
|
|
this.$store.commit(mutations.SET_SIDEBAR, null)
|
|
},
|
|
async onAddPublicLink() {
|
|
await this.$store.dispatch(actions.CREATE_PUBLIC_LINK, this.folder.id)
|
|
this.onCopyPublicLink()
|
|
},
|
|
onCopyPublicLink() {
|
|
copy(this.publicLink)
|
|
this.$store.commit(mutations.SET_NOTIFICATION, t('bookmarks', 'Link copied'))
|
|
},
|
|
onCopyRssLink() {
|
|
copy(this.rssLink)
|
|
this.$store.commit(mutations.SET_NOTIFICATION, t('bookmarks', 'RSS feed copied'))
|
|
},
|
|
async onDeletePublicLink() {
|
|
await this.$store.dispatch(actions.DELETE_PUBLIC_LINK, this.folder.id)
|
|
},
|
|
async onParticipantSearch(searchTerm) {
|
|
if (!searchTerm) {
|
|
return
|
|
}
|
|
this.isSearching = true
|
|
const { data: { ocs: { data, meta } } } = await axios.get(generateOcsUrl('apps/files_sharing/api/v1', 1) + `/sharees?format=json&itemType=folder&search=${searchTerm}&lookup=false&perPage=200&shareType[]=0&shareType[]=1`)
|
|
if (meta.status !== 'ok') {
|
|
this.participantSearchResults = []
|
|
return
|
|
}
|
|
const users = data.exact.users.concat(data.users)
|
|
const groups = data.exact.groups.concat(data.groups)
|
|
this.participantSearchResults = users.map(result => ({
|
|
user: result.value.shareWith,
|
|
displayName: result.label,
|
|
icon: 'icon-user',
|
|
isNoUser: false,
|
|
})).concat(groups.map(result => ({
|
|
user: result.value.shareWith,
|
|
displayName: result.label,
|
|
icon: 'icon-group',
|
|
isNoUser: true,
|
|
})))
|
|
this.isSearching = false
|
|
},
|
|
async onAddShare(user) {
|
|
await this.$store.dispatch(actions.CREATE_SHARE, { folderId: this.folder.id, participant: user.user, type: user.isNoUser ? 1 : 0 })
|
|
},
|
|
async onEditShare(shareId, { canWrite, canShare }) {
|
|
await this.$store.dispatch(actions.EDIT_SHARE, { shareId, canWrite, canShare })
|
|
},
|
|
async onDeleteShare(shareId) {
|
|
await this.$store.dispatch(actions.DELETE_SHARE, shareId)
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
<style>
|
|
.sidebar span[class^='icon-'] {
|
|
display: inline-block;
|
|
position: relative;
|
|
top: 3px;
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.participant-select {
|
|
display: flex;
|
|
}
|
|
|
|
.participant-select__selection {
|
|
flex: 1;
|
|
margin-top: 5px !important;
|
|
}
|
|
|
|
.participant-select__actions {
|
|
flex-grow: 0;
|
|
}
|
|
|
|
.share {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.share__avatar {
|
|
flex-grow: 0;
|
|
height: 44px;
|
|
width: 44px;
|
|
}
|
|
|
|
.share__avatar.active {
|
|
background-color: var(--color-primary-light);
|
|
border-radius: 44px;
|
|
}
|
|
|
|
.share__privs {
|
|
display: flex;
|
|
width: 70px;
|
|
flex-direction: row;
|
|
justify-content: end;
|
|
}
|
|
|
|
.share__privs > * {
|
|
padding-right: 5px;
|
|
}
|
|
|
|
.share__title {
|
|
flex: 1;
|
|
padding-left: 10px;
|
|
margin: 0 !important;
|
|
}
|
|
|
|
.share__actions {
|
|
flex-grow: 0;
|
|
}
|
|
</style>
|