-
Notifications
You must be signed in to change notification settings - Fork 159
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6437 from owncloud/spaces-sidebar
Implement right sidebar for spaces
- Loading branch information
Showing
31 changed files
with
1,252 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
59 changes: 59 additions & 0 deletions
59
packages/web-app-files/src/components/SideBar/Actions/SpaceActions.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
230
packages/web-app-files/src/components/SideBar/Details/SpaceDetails.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.