Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 35 additions & 13 deletions apps/files/src/actions/viewInFolderAction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,27 @@ const viewFiles = {
name: 'Files',
} as IView

const viewSearch = {
id: 'search',
name: 'Search',
} as IView

const rootFolder = new Folder({
id: 10,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/',
owner: 'admin',
permissions: Permission.ALL,
root: '/files/admin',
})

const nestedFolder = new Folder({
id: 11,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo/Bar/',
owner: 'admin',
permissions: Permission.ALL,
root: '/files/admin',
})

describe('View in folder action conditions tests', () => {
test('Default values', () => {
expect(action.id).toBe('view-in-folder')
Expand All @@ -41,10 +62,10 @@ describe('View in folder action conditions tests', () => {
})

describe('View in folder action enabled tests', () => {
test('Enabled for trashbin', () => {
test('Enabled when search result is shown outside its parent folder', () => {
const file = new File({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt',
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo/Bar/foobar.txt',
owner: 'admin',
mime: 'text/plain',
permissions: Permission.ALL,
Expand All @@ -54,16 +75,16 @@ describe('View in folder action enabled tests', () => {
expect(action.enabled).toBeDefined()
expect(action.enabled!({
nodes: [file],
view,
folder: {} as Folder,
view: viewSearch,
folder: rootFolder,
contents: [],
})).toBe(true)
})

test('Disabled for files', () => {
test('Disabled when file is already shown in its parent folder', () => {
const file = new File({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt',
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo/Bar/foobar.txt',
owner: 'admin',
mime: 'text/plain',
permissions: Permission.ALL,
Expand All @@ -74,7 +95,7 @@ describe('View in folder action enabled tests', () => {
expect(action.enabled!({
nodes: [file],
view: viewFiles,
folder: {} as Folder,
folder: nestedFolder,
contents: [],
})).toBe(false)
})
Expand All @@ -93,7 +114,7 @@ describe('View in folder action enabled tests', () => {
expect(action.enabled!({
nodes: [file],
view,
folder: {} as Folder,
folder: nestedFolder,
contents: [],
})).toBe(false)
})
Expand All @@ -111,7 +132,7 @@ describe('View in folder action enabled tests', () => {
expect(action.enabled!({
nodes: [file],
view,
folder: {} as Folder,
folder: nestedFolder,
contents: [],
})).toBe(false)
})
Expand All @@ -136,7 +157,7 @@ describe('View in folder action enabled tests', () => {
expect(action.enabled!({
nodes: [file1, file2],
view,
folder: {} as Folder,
folder: nestedFolder,
contents: [],
})).toBe(false)
})
Expand All @@ -154,16 +175,17 @@ describe('View in folder action enabled tests', () => {
expect(action.enabled!({
nodes: [folder],
view,
folder: {} as Folder,
folder: rootFolder,
contents: [],
})).toBe(false)
})

test('Disabled for files outside the user root folder', () => {
const file = new Folder({
const file = new File({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/trashbin/admin/trash/image.jpg.d1731053878',
owner: 'admin',
mime: 'image/jpeg',
permissions: Permission.READ,
root: '/trashbin/admin',
})
Expand All @@ -172,7 +194,7 @@ describe('View in folder action enabled tests', () => {
expect(action.enabled!({
nodes: [file],
view,
folder: {} as Folder,
folder: rootFolder,
contents: [],
})).toBe(false)
})
Expand Down
12 changes: 6 additions & 6 deletions apps/files/src/actions/viewInFolderAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,12 @@ export const action: IFileAction = {
},
iconSvgInline: () => FolderEyeSvg,

enabled({ nodes, view }) {
enabled({ nodes, folder }) {
// Not enabled for public shares
if (isPublicShare()) {
return false
}

// Only works outside of the main files view
if (view.id === 'files') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when is this needed? Meaning when is this action ever useful in the files view?
As the files view is already the action target view this would end up in?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When doing search this action never appears, even though the file is within some xyz folder, and instead of just the file. I want to go to the actual folder. So instead of just disabling by view.id files, we can check if we are in the same folder as actual file, then no need to show this.

return false
}

// Only works on single node
if (nodes.length !== 1 || !nodes[0]) {
return false
Expand All @@ -43,6 +38,11 @@ export const action: IFileAction = {
return false
}

// Only show if not in same folder
if (folder.path === node.dirname) {
return false
}

if (node.permissions === Permission.NONE) {
return false
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { File, Permission } from '@nextcloud/files'
import { enableAutoDestroy, shallowMount } from '@vue/test-utils'
import { afterEach, describe, expect, test } from 'vitest'
import FilesSidebarSubname from './FilesSidebarSubname.vue'

enableAutoDestroy(afterEach)

describe('FilesSidebarSubname', () => {
test('renders full path for nested file', () => {
const file = new File({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Documents/Projects/report.pdf',
owner: 'admin',
mime: 'application/pdf',
permissions: Permission.READ,
root: '/files/admin',
size: 1024,
})

const wrapper = shallowMount(FilesSidebarSubname, {
propsData: { node: file },
})

expect(wrapper.text()).toContain('/Documents/Projects/report.pdf')
})

test('renders full path for root-level file', () => {
const file = new File({
id: 2,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/report.pdf',
owner: 'admin',
mime: 'application/pdf',
permissions: Permission.READ,
root: '/files/admin',
size: 1024,
})

const wrapper = shallowMount(FilesSidebarSubname, {
propsData: { node: file },
})

expect(wrapper.text()).toContain('/report.pdf')
})

test('updates path when selected node changes', async () => {
const firstFile = new File({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/First.txt',
owner: 'admin',
mime: 'text/plain',
permissions: Permission.READ,
root: '/files/admin',
})
const secondFile = new File({
id: 2,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Documents/Second.txt',
owner: 'admin',
mime: 'text/plain',
permissions: Permission.READ,
root: '/files/admin',
})

const wrapper = shallowMount(FilesSidebarSubname, {
propsData: { node: firstFile },
})
expect(wrapper.text()).toContain('/First.txt')

await wrapper.setProps({ node: secondFile })

expect(wrapper.text()).not.toContain('/First.txt')
expect(wrapper.text()).toContain('/Documents/Second.txt')
})
})
60 changes: 42 additions & 18 deletions apps/files/src/components/FilesSidebar/FilesSidebarSubname.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,56 @@ const size = computed(() => formatFileSize(props.node.size ?? 0))

<template>
<div :class="$style.filesSidebarSubname">
<NcIconSvgWrapper
v-if="isFavourited"
inline
:path="mdiStar"
:name="t('files', 'Favorite')" />
<div :class="$style.filesSidebarSubname__path">
<span :class="$style.filesSidebarSubname__label">{{ t('files', 'Location') }}:</span>
{{ node.path }}
</div>

<span>{{ size }}</span>
<div :class="$style.filesSidebarSubname__metadata">
<NcIconSvgWrapper
v-if="isFavourited"
inline
:path="mdiStar"
:name="t('files', 'Favorite')" />

<span v-if="node.mtime">
<span :class="$style.filesSidebarSubname__separator">•</span>
<NcDateTime :timestamp="node.mtime" />
</span>
<span>{{ size }}</span>

<template v-if="node.owner">
<span :class="$style.filesSidebarSubname__separator">•</span>
<NcUserBubble
:class="$style.filesSidebarSubname__userBubble"
:title="t('files', 'Owner')"
:user="node.owner"
:display-name="node.attributes['owner-display-name']" />
</template>
<span v-if="node.mtime">
<span :class="$style.filesSidebarSubname__separator">•</span>
<NcDateTime :timestamp="node.mtime" />
</span>

<template v-if="node.owner">
<span :class="$style.filesSidebarSubname__separator">•</span>
<NcUserBubble
:class="$style.filesSidebarSubname__userBubble"
:title="t('files', 'Owner')"
:user="node.owner"
:display-name="node.attributes['owner-display-name']" />
</template>
</div>
</div>
</template>

<style module>
.filesSidebarSubname {
display: flex;
flex-direction: column;
gap: 4px;
}

.filesSidebarSubname__path {
display: flex;
flex-direction: column;
width: 100%;
}

.filesSidebarSubname__label {
color: var(--color-text-maxcontrast);
font-weight: bold;
}

.filesSidebarSubname__metadata {
display: flex;
align-items: center;
flex-wrap: wrap;
Expand Down