From e0e224307d972eabe00bc8db5d8f152864b7f4a6 Mon Sep 17 00:00:00 2001
From: Ferdinand Thiessen <opensource@fthiessen.de>
Date: Thu, 4 Apr 2024 13:02:51 +0200
Subject: [PATCH] fix(files): Do not show files from hidden folders in
 "Recent"-view if hidden files are disabled by user

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
---
 apps/files/src/views/FilesList.vue     |  2 +-
 apps/files/src/views/recent.ts         | 28 ++++++++-
 cypress/e2e/files/files-settings.cy.ts | 81 ++++++++++++++++++++++++++
 3 files changed, 108 insertions(+), 3 deletions(-)
 create mode 100644 cypress/e2e/files/files-settings.cy.ts

diff --git a/apps/files/src/views/FilesList.vue b/apps/files/src/views/FilesList.vue
index 53620cf2f6d96..b0f1a47a6640e 100644
--- a/apps/files/src/views/FilesList.vue
+++ b/apps/files/src/views/FilesList.vue
@@ -476,7 +476,7 @@ export default defineComponent({
 			}
 
 			// Fetch the current dir contents
-			this.promise = currentView.getContents(dir) as Promise<ContentsWithRoot>
+			this.promise = currentView.getContents(dir, this.fetchContent) as Promise<ContentsWithRoot>
 			try {
 				const { folder, contents } = await this.promise
 				logger.debug('Fetched contents', { dir, folder, contents })
diff --git a/apps/files/src/views/recent.ts b/apps/files/src/views/recent.ts
index bb536674ec19a..7af9049407222 100644
--- a/apps/files/src/views/recent.ts
+++ b/apps/files/src/views/recent.ts
@@ -19,13 +19,27 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  *
  */
+import { subscribe } from '@nextcloud/event-bus'
+import { View, getNavigation } from '@nextcloud/files'
+import { loadState } from '@nextcloud/initial-state'
 import { translate as t } from '@nextcloud/l10n'
 import HistorySvg from '@mdi/svg/svg/history.svg?raw'
 
 import { getContents } from '../services/Recent'
-import { View, getNavigation } from '@nextcloud/files'
 
 export default () => {
+	// Callback to use to trigger reload
+	let reloadCallback = () => {}
+	// Current state of user config for hidden files
+	let { show_hidden: showHiddenFiles } = loadState<{ show_hidden: boolean }>('files', 'config')
+	// When the user config changes and the hidden files setting is changed we need to reload the directory content
+	subscribe('files:config:updated', ({ key, value }: { key: string, value: boolean}) => {
+		if (key === 'show_hidden') {
+			showHiddenFiles = value
+			reloadCallback()
+		}
+	})
+
 	const Navigation = getNavigation()
 	Navigation.register(new View({
 		id: 'recent',
@@ -40,6 +54,16 @@ export default () => {
 
 		defaultSortKey: 'mtime',
 
-		getContents,
+		getContents: async (path = '/', callback: () => void) => {
+			// Only use the real reload callback on the root directory
+			// as otherwise the files app will handle it correctly and we would cause a doubled WebDAV request
+			reloadCallback = path === '/' ? callback : () => {}
+			const content = await getContents(path)
+			if (path === '/' && !showHiddenFiles) {
+				// We need to hide files from hidden directories in the root if not configured
+				content.contents = content.contents.filter((node) => node.dirname.split('/').some((dir) => dir.startsWith('.')))
+			}
+			return content
+		},
 	}))
 }
diff --git a/cypress/e2e/files/files-settings.cy.ts b/cypress/e2e/files/files-settings.cy.ts
new file mode 100644
index 0000000000000..fe07c37320017
--- /dev/null
+++ b/cypress/e2e/files/files-settings.cy.ts
@@ -0,0 +1,81 @@
+import type { User } from '@nextcloud/cypress'
+import { getRowForFile } from './FilesUtils'
+
+const showHiddenFiles = () => {
+	// Open the files settings
+	cy.get('[data-cy-files-navigation-settings-button] a').click({ force: true })
+	// Toggle the hidden files setting
+	cy.get('[data-cy-files-settings-setting="show_hidden"]').within(() => {
+		cy.get('input').should('not.be.checked')
+		cy.get('input').check({ force: true })
+	})
+	// Close the dialog
+	cy.get('[data-cy-files-navigation-settings] button[aria-label="Close"]').click()
+}
+
+describe('files: Hide or show hidden files', { testIsolation: true }, () => {
+	let user: User
+
+	beforeEach(() => {
+		cy.createRandomUser().then(($user) => {
+			user = $user
+
+			cy.uploadContent(user, new Blob([]), 'text/plain', '/.file')
+			cy.mkdir(user, '/.folder')
+			cy.login(user)
+		})
+	})
+
+	context('view: All files', { testIsolation: false }, () => {
+		it('hides dot-files by default', () => {
+			cy.visit('/apps/files')
+
+			getRowForFile('.file').should('not.exist')
+			getRowForFile('.folder').should('not.exist')
+		})
+
+		it('can show hidden files', () => {
+			showHiddenFiles()
+			// Now the files should be visible
+			getRowForFile('.file').should('be.visible')
+			getRowForFile('.folder').should('be.visible')
+		})
+	})
+
+	context('view: Personal files', { testIsolation: false }, () => {
+		it('hides dot-files by default', () => {
+			cy.visit('/apps/files/personal')
+
+			getRowForFile('.file').should('not.exist')
+			getRowForFile('.folder').should('not.exist')
+		})
+
+		it('can show hidden files', () => {
+			showHiddenFiles()
+			// Now the files should be visible
+			getRowForFile('.file').should('be.visible')
+			getRowForFile('.folder').should('be.visible')
+		})
+	})
+
+	context('view: Recent files', { testIsolation: false }, () => {
+		it('hides dot-files by default', () => {
+			// also add hidden file in hidden folder
+			cy.uploadContent(user, new Blob([]), 'text/plain', '/.folder/other-file')
+			cy.login(user)
+			cy.visit('/apps/files/recent')
+
+			getRowForFile('.file').should('not.exist')
+			getRowForFile('.folder').should('not.exist')
+			getRowForFile('other-file').should('not.exist')
+		})
+
+		it('can show hidden files', () => {
+			showHiddenFiles()
+			// Now the files should be visible
+			getRowForFile('.file').should('be.visible')
+			getRowForFile('.folder').should('be.visible')
+			getRowForFile('other-file').should('be.visible')
+		})
+	})
+})
