Skip to content

Commit 0a5898f

Browse files
committed
feat(sidebar): Show node owner in metadata subline
Resolves: #46178 Signed-off-by: fenn-cs <fenn25.fn@gmail.com>
1 parent 0451be7 commit 0a5898f

File tree

3 files changed

+72
-25
lines changed

3 files changed

+72
-25
lines changed

apps/files/src/services/WebdavClient.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5-
import { davGetClient } from '@nextcloud/files'
5+
import { davGetClient, davGetDefaultPropfind, davResultToNode, davRootPath } from '@nextcloud/files'
6+
import type { FileStat, ResponseDataDetailed } from 'webdav'
7+
import type { Node } from '@nextcloud/files'
68

79
export const client = davGetClient()
10+
11+
export const fetchNode = async (node: Node): Promise<Node> => {
12+
const propfindPayload = davGetDefaultPropfind()
13+
const result = await client.stat(`${davRootPath}${node.path}`, {
14+
details: true,
15+
data: propfindPayload,
16+
}) as ResponseDataDetailed<FileStat>
17+
return davResultToNode(result.data)
18+
}

apps/files/src/store/files.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,16 @@
44
*/
55

66
import type { FilesStore, RootsStore, RootOptions, Service, FilesState, FileSource } from '../types'
7-
import type { FileStat, ResponseDataDetailed } from 'webdav'
87
import type { Folder, Node } from '@nextcloud/files'
98

10-
import { davGetDefaultPropfind, davResultToNode, davRootPath } from '@nextcloud/files'
119
import { defineStore } from 'pinia'
1210
import { subscribe } from '@nextcloud/event-bus'
1311
import logger from '../logger'
1412
import Vue from 'vue'
1513

16-
import { client } from '../services/WebdavClient.ts'
14+
import { fetchNode } from '../services/WebdavClient.ts'
1715
import { usePathsStore } from './paths.ts'
1816

19-
const fetchNode = async (node: Node): Promise<Node> => {
20-
const propfindPayload = davGetDefaultPropfind()
21-
const result = await client.stat(`${davRootPath}${node.path}`, {
22-
details: true,
23-
data: propfindPayload,
24-
}) as ResponseDataDetailed<FileStat>
25-
return davResultToNode(result.data)
26-
}
27-
2817
export const useFilesStore = function(...args) {
2918
const store = defineStore('files', {
3019
state: (): FilesState => ({

apps/files/src/views/Sidebar.vue

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,19 @@
1717
@closing="handleClosing"
1818
@closed="handleClosed">
1919
<template v-if="fileInfo" #subname>
20-
<NcIconSvgWrapper v-if="fileInfo.isFavourited"
21-
:path="mdiStar"
22-
:name="t('files', 'Favorite')"
23-
inline />
24-
{{ size }}
25-
<NcDateTime :timestamp="fileInfo.mtime" />
20+
<div class="sidebar__subname">
21+
<NcIconSvgWrapper v-if="fileInfo.isFavourited"
22+
:path="mdiStar"
23+
:name="t('files', 'Favorite')"
24+
inline />
25+
<span>{{ size }}</span>
26+
<span class="sidebar__subname-separator">•</span>
27+
<NcDateTime :timestamp="fileInfo.mtime" />
28+
<span class="sidebar__subname-separator">•</span>
29+
<span>{{ t('files', 'Owner') }}</span>
30+
<NcUserBubble :user="ownerId"
31+
:display-name="nodeOwnerLabel" />
32+
</div>
2633
</template>
2734

2835
<!-- TODO: create a standard to allow multiple elements here? -->
@@ -96,6 +103,7 @@ import { encodePath } from '@nextcloud/paths'
96103
import { generateUrl } from '@nextcloud/router'
97104
import { ShareType } from '@nextcloud/sharing'
98105
import { mdiStar, mdiStarOutline } from '@mdi/js'
106+
import { useFilesStore } from '../store/files.ts'
99107
import axios from '@nextcloud/axios'
100108
import $ from 'jquery'
101109
@@ -104,6 +112,7 @@ import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
104112
import NcDateTime from '@nextcloud/vue/dist/Components/NcDateTime.js'
105113
import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js'
106114
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
115+
import NcUserBubble from '@nextcloud/vue/dist/Components/NcUserBubble.js'
107116
108117
import FileInfo from '../services/FileInfo.js'
109118
import LegacyView from '../components/LegacyView.vue'
@@ -123,15 +132,17 @@ export default {
123132
NcIconSvgWrapper,
124133
SidebarTab,
125134
SystemTags,
135+
NcUserBubble,
126136
},
127137
128138
setup() {
129139
const currentUser = getCurrentUser()
140+
const filesStore = useFilesStore()
130141
131142
// Non reactive properties
132143
return {
133144
currentUser,
134-
145+
filesStore,
135146
mdiStar,
136147
mdiStarOutline,
137148
}
@@ -146,6 +157,7 @@ export default {
146157
error: null,
147158
loading: true,
148159
fileInfo: null,
160+
node: null,
149161
isFullScreen: false,
150162
hasLowHeight: false,
151163
}
@@ -287,6 +299,25 @@ export default {
287299
isSystemTagsEnabled() {
288300
return getCapabilities()?.systemtags?.enabled === true
289301
},
302+
ownerId() {
303+
return this.node?.attributes?.['owner-id'] ?? this.currentUser.uid
304+
},
305+
currentUserIsOwner() {
306+
return this.ownerId === this.currentUser.uid
307+
},
308+
nodeOwnerLabel() {
309+
let ownerDisplayName = this.node?.attributes?.['owner-display-name']
310+
if (this.currentUserIsOwner) {
311+
ownerDisplayName = `${ownerDisplayName} (${t('files', 'You')})`
312+
}
313+
return ownerDisplayName
314+
},
315+
sharedMultipleTimes() {
316+
if (Array.isArray(node.attributes?.['share-types']) && node.attributes?.['share-types'].length > 1) {
317+
return t('files', 'Shared multiple times with different people')
318+
}
319+
return null
320+
},
290321
},
291322
created() {
292323
subscribe('files:node:deleted', this.onNodeDeleted)
@@ -460,6 +491,7 @@ export default {
460491
this.fileInfo = await FileInfo(this.davPath)
461492
// adding this as fallback because other apps expect it
462493
this.fileInfo.dir = this.file.split('/').slice(0, -1).join('/')
494+
this.node = this.filesStore.getNode((this.fileInfo.path + '/' + this.fileInfo.name).replace('//', '/'))
463495
464496
// DEPRECATED legacy views
465497
// TODO: remove
@@ -589,10 +621,25 @@ export default {
589621
}
590622
}
591623
592-
.sidebar__description {
593-
display: flex;
594-
flex-direction: column;
595-
width: 100%;
596-
gap: 8px 0;
624+
.sidebar__subname {
625+
display: flex;
626+
align-items: center;
627+
gap: 0 8px;
628+
629+
&-separator {
630+
display: inline-block;
631+
font-weight: bold !important;
632+
}
633+
634+
.user-bubble__wrapper {
635+
display: inline-flex;
636+
}
597637
}
638+
639+
.sidebar__description {
640+
display: flex;
641+
flex-direction: column;
642+
width: 100%;
643+
gap: 8px 0;
644+
}
598645
</style>

0 commit comments

Comments
 (0)