Skip to content

Commit a2999d8

Browse files
authored
Merge pull request #44661 from nextcloud/fix/hide-hidden-recent-files-v2
fix(files): Do not show files from hidden folders in "Recent"-view if hidden files are disabled by user
2 parents d9a1a9b + acbb699 commit a2999d8

File tree

13 files changed

+206
-15
lines changed

13 files changed

+206
-15
lines changed

apps/files/src/main.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import Vue from 'vue'
2-
import { createPinia, PiniaVuePlugin } from 'pinia'
1+
import { PiniaVuePlugin } from 'pinia'
32
import { getNavigation } from '@nextcloud/files'
43
import { getRequestToken } from '@nextcloud/auth'
4+
import Vue from 'vue'
55

6+
import { pinia } from './store/index.ts'
67
import router from './router/router'
78
import RouterService from './services/RouterService'
89
import SettingsModel from './models/Setting.js'
@@ -30,7 +31,6 @@ Object.assign(window.OCP.Files, { Router })
3031

3132
// Init Pinia store
3233
Vue.use(PiniaVuePlugin)
33-
const pinia = createPinia()
3434

3535
// Init Navigation Service
3636
// This only works with Vue 2 - with Vue 3 this will not modify the source but return just a oberserver

apps/files/src/services/Recent.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,37 @@
2020
* along with this program. If not, see <http://www.gnu.org/licenses/>.
2121
*
2222
*/
23-
import type { ContentsWithRoot } from '@nextcloud/files'
23+
import type { ContentsWithRoot, Node } from '@nextcloud/files'
2424
import type { FileStat, ResponseDataDetailed } from 'webdav'
2525

2626
import { getCurrentUser } from '@nextcloud/auth'
2727
import { Folder, Permission, davGetRecentSearch, davGetClient, davResultToNode, davRootPath, davRemoteURL } from '@nextcloud/files'
28+
import { useUserConfigStore } from '../store/userconfig.ts'
29+
import { pinia } from '../store/index.ts'
2830

2931
const client = davGetClient()
3032

3133
const lastTwoWeeksTimestamp = Math.round((Date.now() / 1000) - (60 * 60 * 24 * 14))
3234

35+
/**
36+
* Get recently changed nodes
37+
*
38+
* This takes the users preference about hidden files into account.
39+
* If hidden files are not shown, then also recently changed files *in* hidden directories are filtered.
40+
*
41+
* @param path Path to search for recent changes
42+
*/
3343
export const getContents = async (path = '/'): Promise<ContentsWithRoot> => {
44+
const store = useUserConfigStore(pinia)
45+
/**
46+
* Filter function that returns only the visible nodes - or hidden if explicitly configured
47+
* @param node The node to check
48+
*/
49+
const filterHidden = (node: Node) =>
50+
path !== '/' // We need to hide files from hidden directories in the root if not configured to show
51+
|| store.userConfig.show_hidden // If configured to show hidden files we can early return
52+
|| !node.dirname.split('/').some((dir) => dir.startsWith('.')) // otherwise only include the file if non of the parent directories is hidden
53+
3454
const contentsResponse = await client.getDirectoryContents(path, {
3555
details: true,
3656
data: davGetRecentSearch(lastTwoWeeksTimestamp),
@@ -53,6 +73,6 @@ export const getContents = async (path = '/'): Promise<ContentsWithRoot> => {
5373
owner: getCurrentUser()?.uid || null,
5474
permissions: Permission.READ,
5575
}),
56-
contents: contents.map((r) => davResultToNode(r)),
76+
contents: contents.map((r) => davResultToNode(r)).filter(filterHidden),
5777
}
5878
}

apps/files/src/store/index.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* @copyright Copyright (c) 2024 Ferdinand Thiessen <[email protected]>
3+
*
4+
* @author Ferdinand Thiessen <[email protected]>
5+
*
6+
* @license AGPL-3.0-or-later
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as
10+
* published by the Free Software Foundation, either version 3 of the
11+
* License, or (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License
19+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
23+
import { createPinia } from 'pinia'
24+
25+
export const pinia = createPinia()

apps/files/src/views/FilesList.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ export default defineComponent({
214214
loading: true,
215215
promise: null,
216216
Type,
217+
218+
_unsubscribeStore: () => {},
217219
}
218220
},
219221
@@ -452,10 +454,16 @@ export default defineComponent({
452454
subscribe('files:node:updated', this.onUpdatedNode)
453455
subscribe('nextcloud:unified-search.search', this.onSearch)
454456
subscribe('nextcloud:unified-search.reset', this.onSearch)
457+
458+
// reload on settings change
459+
this._unsubscribeStore = this.userConfigStore.$subscribe(() => this.fetchContent(), { deep: true })
455460
},
456461
457462
unmounted() {
458463
unsubscribe('files:node:updated', this.onUpdatedNode)
464+
unsubscribe('nextcloud:unified-search.search', this.onSearch)
465+
unsubscribe('nextcloud:unified-search.reset', this.onSearch)
466+
this._unsubscribeStore()
459467
},
460468
461469
methods: {

apps/files/src/views/recent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
* along with this program. If not, see <http://www.gnu.org/licenses/>.
2020
*
2121
*/
22+
import { View, getNavigation } from '@nextcloud/files'
2223
import { translate as t } from '@nextcloud/l10n'
2324
import HistorySvg from '@mdi/svg/svg/history.svg?raw'
2425

2526
import { getContents } from '../services/Recent'
26-
import { View, getNavigation } from '@nextcloud/files'
2727

2828
export default () => {
2929
const Navigation = getNavigation()
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* @copyright Copyright (c) 2024 Ferdinand Thiessen <[email protected]>
3+
*
4+
* @author Ferdinand Thiessen <[email protected]>
5+
*
6+
* @license AGPL-3.0-or-later
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as
10+
* published by the Free Software Foundation, either version 3 of the
11+
* License, or (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License
19+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
23+
import type { User } from '@nextcloud/cypress'
24+
import { getRowForFile } from './FilesUtils'
25+
26+
const showHiddenFiles = () => {
27+
// Open the files settings
28+
cy.get('[data-cy-files-navigation-settings-button] a').click({ force: true })
29+
// Toggle the hidden files setting
30+
cy.get('[data-cy-files-settings-setting="show_hidden"]').within(() => {
31+
cy.get('input').should('not.be.checked')
32+
cy.get('input').check({ force: true })
33+
})
34+
// Close the dialog
35+
cy.get('[data-cy-files-navigation-settings] button[aria-label="Close"]').click()
36+
}
37+
38+
describe('files: Hide or show hidden files', { testIsolation: true }, () => {
39+
let user: User
40+
41+
const setupFiles = () => cy.createRandomUser().then(($user) => {
42+
user = $user
43+
44+
cy.uploadContent(user, new Blob([]), 'text/plain', '/.file')
45+
cy.uploadContent(user, new Blob([]), 'text/plain', '/visible-file')
46+
cy.mkdir(user, '/.folder')
47+
cy.login(user)
48+
})
49+
50+
context('view: All files', { testIsolation: false }, () => {
51+
before(setupFiles)
52+
53+
it('hides dot-files by default', () => {
54+
cy.visit('/apps/files')
55+
56+
getRowForFile('visible-file').should('be.visible')
57+
getRowForFile('.file').should('not.exist')
58+
getRowForFile('.folder').should('not.exist')
59+
})
60+
61+
it('can show hidden files', () => {
62+
showHiddenFiles()
63+
// Now the files should be visible
64+
getRowForFile('.file').should('be.visible')
65+
getRowForFile('.folder').should('be.visible')
66+
})
67+
})
68+
69+
context('view: Personal files', { testIsolation: false }, () => {
70+
before(setupFiles)
71+
72+
it('hides dot-files by default', () => {
73+
cy.visit('/apps/files/personal')
74+
75+
getRowForFile('visible-file').should('be.visible')
76+
getRowForFile('.file').should('not.exist')
77+
getRowForFile('.folder').should('not.exist')
78+
})
79+
80+
it('can show hidden files', () => {
81+
showHiddenFiles()
82+
// Now the files should be visible
83+
getRowForFile('.file').should('be.visible')
84+
getRowForFile('.folder').should('be.visible')
85+
})
86+
})
87+
88+
context('view: Recent files', { testIsolation: false }, () => {
89+
before(() => {
90+
setupFiles().then(() => {
91+
// also add hidden file in hidden folder
92+
cy.uploadContent(user, new Blob([]), 'text/plain', '/.folder/other-file')
93+
cy.login(user)
94+
})
95+
})
96+
97+
it('hides dot-files by default', () => {
98+
cy.visit('/apps/files/recent')
99+
100+
getRowForFile('visible-file').should('be.visible')
101+
getRowForFile('.file').should('not.exist')
102+
getRowForFile('.folder').should('not.exist')
103+
getRowForFile('other-file').should('not.exist')
104+
})
105+
106+
it('can show hidden files', () => {
107+
showHiddenFiles()
108+
109+
getRowForFile('visible-file').should('be.visible')
110+
// Now the files should be visible
111+
getRowForFile('.file').should('be.visible')
112+
getRowForFile('.folder').should('be.visible')
113+
getRowForFile('other-file').should('be.visible')
114+
})
115+
})
116+
})

dist/core-common.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/core-common.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/files-init.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/files-init.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)