Skip to content

Commit

Permalink
Merge pull request #6437 from owncloud/spaces-sidebar
Browse files Browse the repository at this point in the history
Implement right sidebar for spaces
  • Loading branch information
kulmann authored Feb 19, 2022
2 parents 9453255 + 52dcfb3 commit 2abfe48
Show file tree
Hide file tree
Showing 31 changed files with 1,252 additions and 63 deletions.
6 changes: 6 additions & 0 deletions changelog/unreleased/enhancement-spaces-sidebar
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enhancement: Implement the right sidebar for spaces

The right sidebar for a space functions similar to the files sidebar and gives the user basic information and actions for the current space.

https://github.com/owncloud/web/pull/6437
https://github.com/owncloud/web/issues/6284
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<template>
<oc-list id="oc-spaces-actions-sidebar" class-name="oc-mt-s">
<action-menu-item
v-for="(action, index) in actions"
:key="`action-${index}`"
:action="action"
:items="resources"
class="oc-py-xs"
/>
</oc-list>
</template>

<script>
import { mapGetters } from 'vuex'
import ActionMenuItem from '../../ActionMenuItem.vue'
import Rename from '../../../mixins/spaces/actions/rename'
import Delete from '../../../mixins/spaces/actions/delete'
import Disable from '../../../mixins/spaces/actions/disable'
import Restore from '../../../mixins/spaces/actions/restore'
import EditDescription from '../../../mixins/spaces/actions/editDescription'
export default {
name: 'SpaceActions',
title: ($gettext) => {
return $gettext('Actions')
},
components: { ActionMenuItem },
mixins: [Rename, Delete, EditDescription, Disable, Restore],
computed: {
...mapGetters('Files', ['highlightedFile']),
resources() {
return [this.highlightedFile]
},
actions() {
return [
...this.$_rename_items,
...this.$_editDescription_items,
...this.$_restore_items,
...this.$_delete_items,
...this.$_disable_items
].filter((item) => item.isEnabled({ resources: this.resources }))
}
}
}
</script>

<style lang="scss">
#oc-spaces-actions-sidebar {
> li a,
> li a:hover {
color: var(--oc-color-swatch-passive-default);
display: inline-flex;
gap: 10px;
vertical-align: top;
}
}
</style>
230 changes: 230 additions & 0 deletions packages/web-app-files/src/components/SideBar/Details/SpaceDetails.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
<template>
<div id="oc-space-details-sidebar">
<div class="oc-space-details-sidebar-image oc-text-center">
<oc-spinner v-if="loadImageTask.isRunning" />
<img
v-else-if="spaceImage"
:src="'data:image/jpeg;base64,' + spaceImage"
alt=""
class="oc-mb-m"
/>
<oc-icon
v-else
name="layout-grid"
size="xxlarge"
class="space-default-image oc-px-m oc-py-m"
/>
</div>
<div v-if="hasPeopleShares || hasLinkShares" class="oc-flex oc-flex-middle oc-mb-m">
<oc-icon v-if="hasPeopleShares" name="group" class="oc-mr-s" />
<oc-icon v-if="hasLinkShares" name="link" class="oc-mr-s" />
<span class="oc-text-small" v-text="shareLabel" />
</div>
<div>
<table class="details-table" :aria-label="detailsTableLabel">
<tr>
<th scope="col" class="oc-pr-s" v-text="$gettext('Last activity')" />
<td v-text="lastModifyDate" />
</tr>
<tr v-if="space.description">
<th scope="col" class="oc-pr-s" v-text="$gettext('Subtitle')" />
<td v-text="space.description" />
</tr>
<tr>
<th scope="col" class="oc-pr-s" v-text="$gettext('Manager')" />
<td>
<span v-if="!loadOwnersTask.isRunning" v-text="ownerUsernames" />
</td>
</tr>
<tr>
<th scope="col" class="oc-pr-s" v-text="$gettext('Quota')" />
<td>
<space-quota :space-quota="space.spaceQuota" />
</td>
</tr>
</table>
</div>
</div>
</template>
<script>
import { ref } from '@vue/composition-api'
import Mixins from '../../../mixins'
import MixinResources from '../../../mixins/resources'
import { mapGetters } from 'vuex'
import { useTask } from 'vue-concurrency'
import { buildWebDavSpacesPath } from '../../../helpers/resources'
import { useStore } from 'web-pkg/src/composables'
import { clientService } from 'web-pkg/src/services'
import SpaceQuota from '../../SpaceQuota.vue'
export default {
name: 'SpaceDetails',
components: { SpaceQuota },
mixins: [Mixins, MixinResources],
inject: ['displayedItem'],
title: ($gettext) => {
return $gettext('Details')
},
setup() {
const store = useStore()
const spaceImage = ref('')
const owners = ref([])
const graphClient = clientService.graphAuthenticated(
store.getters.configuration.server,
store.getters.getToken
)
const loadImageTask = useTask(function* (signal, ref) {
if (!ref.space?.spaceImageData) {
return
}
const fileContents = yield ref.$client.files.getFileContents(
buildWebDavSpacesPath(ref.space.id, ref.space.spaceImageData.name),
{ responseType: 'arrayBuffer' }
)
spaceImage.value = Buffer.from(fileContents).toString('base64')
})
const loadOwnersTask = useTask(function* (signal, ref) {
const promises = []
for (const userId of ref.ownerUserIds) {
promises.push(graphClient.users.getUser(userId))
}
if (promises.length > 0) {
yield Promise.all(promises).then((resolvedData) => {
resolvedData.forEach((response) => {
owners.value.push(response.data)
})
})
}
})
return { loadImageTask, loadOwnersTask, spaceImage, owners }
},
computed: {
...mapGetters(['user']),
space() {
return this.displayedItem.value
},
detailsTableLabel() {
return this.$gettext('Overview of the information about the selected space')
},
lastModifyDate() {
return this.formDateFromISO(this.space.mdate)
},
ownerUserIds() {
const permissions = this.space.spacePermissions?.filter((permission) =>
permission.roles.includes('manager')
)
if (!permissions.length) {
return []
}
const userIds = permissions.reduce((acc, item) => {
const ids = item.grantedTo.map((user) => user.user.id)
acc = acc.concat(ids)
return acc
}, [])
return [...new Set(userIds)]
},
ownerUsernames() {
const userId = this.user?.id
return this.owners
.map((owner) => {
if (owner.onPremisesSamAccountName === userId) {
return this.$gettextInterpolate(this.$gettext('%{displayName} (me)'), {
displayName: owner.displayName
})
}
return owner.displayName
})
.join(', ')
},
hasPeopleShares() {
return false // @TODO
},
hasLinkShares() {
return false // @TODO
},
peopleShareCount() {
return 0 // @TODO
},
linkShareCount() {
return 0 // @TODO
},
shareLabel() {
let peopleString, linksString
if (this.hasPeopleShares) {
peopleString = this.$gettextInterpolate(
this.$ngettext(
'This space has been shared with %{peopleShareCount} person.',
'This space has been shared with %{peopleShareCount} people.',
this.peopleShareCount
),
{
peopleShareCount: this.peopleShareCount
}
)
}
if (this.hasLinkShares) {
linksString = this.$gettextInterpolate(
this.$ngettext(
'%{linkShareCount} link giving access.',
'%{linkShareCount} links giving access.',
this.linkShareCount
),
{
linkShareCount: this.linkShareCount
}
)
}
if (peopleString && linksString) {
return `${peopleString} ${linksString}`
}
if (peopleString) {
return peopleString
}
if (linksString) {
return linksString
}
return ''
}
},
mounted() {
this.loadImageTask.perform(this)
this.loadOwnersTask.perform(this)
}
}
</script>
<style lang="scss" scoped>
.oc-space-details-sidebar {
&-image img {
max-height: 150px;
object-fit: cover;
width: 100%;
}
}
.details-table {
text-align: left;
tr {
height: 1.5rem;
}
th {
font-weight: 600;
}
}
</style>
19 changes: 14 additions & 5 deletions packages/web-app-files/src/components/SideBar/SideBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@
</oc-button>
</div>
<file-info
v-if="isSingleResource"
v-if="isSingleResource && !highlightedFileIsSpace"
class="sidebar-panel__file_info"
:is-content-displayed="isContentDisplayed"
/>
<space-info v-if="highlightedFileIsSpace" class="sidebar-panel__space_info" />
<div class="sidebar-panel__body">
<template v-if="isContentDisplayed">
<component :is="panel.component" class="sidebar-panel__body-content" />
Expand Down Expand Up @@ -95,12 +96,13 @@ import { isLocationCommonActive, isLocationSharesActive } from '../../router'
import { computed } from '@vue/composition-api'
import FileInfo from './FileInfo.vue'
import SpaceInfo from './SpaceInfo.vue'
let visibilityObserver
let hiddenObserver
export default {
components: { FileInfo },
components: { FileInfo, SpaceInfo },
provide() {
return {
Expand Down Expand Up @@ -181,7 +183,7 @@ export default {
return null
},
isSingleResource() {
return !this.areMultipleSelected && !this.isRootFolder
return !this.areMultipleSelected && (!this.isRootFolder || this.highlightedFileIsSpace)
},
areMultipleSelected() {
return this.selectedFiles && this.selectedFiles.length > 1
Expand All @@ -194,6 +196,9 @@ export default {
},
highlightedFileFavorite() {
return this.highlightedFile?.starred
},
highlightedFileIsSpace() {
return this.highlightedFile?.type === 'space'
}
},
watch: {
Expand Down Expand Up @@ -292,7 +297,10 @@ export default {
return
}
if (isLocationCommonActive(this.$router, 'files-common-trash')) {
if (
isLocationCommonActive(this.$router, 'files-common-trash') ||
this.highlightedFileIsSpace
) {
this.selectedFile = this.highlightedFile
return
}
Expand Down Expand Up @@ -391,7 +399,8 @@ export default {
}
}
&__file_info {
&__file_info,
&__space_info {
border-bottom: 1px solid var(--oc-color-border);
background-color: var(--oc-color-background-default);
padding: 0 10px;
Expand Down
Loading

0 comments on commit 2abfe48

Please sign in to comment.