aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFerdinand Thiessen <opensource@fthiessen.de>2025-05-22 17:01:48 +0200
committerFerdinand Thiessen <opensource@fthiessen.de>2025-05-26 15:11:36 +0200
commit60ccc85e7685c9375365230bb53cf20d15bc31e8 (patch)
treed6b1cc3816a5e029421d032b6413a534d571cc6c
parent27149b7f247490eb9d3c6018ce7b2ea61b29000b (diff)
downloadnextcloud-server-60ccc85e7685c9375365230bb53cf20d15bc31e8.tar.gz
nextcloud-server-60ccc85e7685c9375365230bb53cf20d15bc31e8.zip
fix(files_sharing): show note, label and list of uploaded files on file drop
This was missing from the Vue migration of the public share view: - Show the note as the description of the file drop - Show the label as the heading of the file drop if available - Show list of uploaded files for verification Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
-rw-r--r--apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php3
-rw-r--r--apps/files_sharing/src/files_views/publicFileDrop.ts7
-rw-r--r--apps/files_sharing/src/views/FilesViewFileDropEmptyContent.vue93
-rw-r--r--apps/files_sharing/tests/Controller/ShareControllerTest.php8
-rw-r--r--cypress/e2e/files_sharing/public-share/view_file-drop.cy.ts7
5 files changed, 99 insertions, 19 deletions
diff --git a/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php b/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php
index 910d00a5972..645250ab2b5 100644
--- a/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php
+++ b/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php
@@ -91,6 +91,9 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider
'disclaimer',
$this->appConfig->getValueString('core', 'shareapi_public_link_disclaimertext'),
);
+ // file drops do not request the root folder so we need to provide label and note if available
+ $this->initialState->provideInitialState('label', $share->getLabel());
+ $this->initialState->provideInitialState('note', $share->getNote());
}
// Set up initial state
$this->initialState->provideInitialState('isPublic', true);
diff --git a/apps/files_sharing/src/files_views/publicFileDrop.ts b/apps/files_sharing/src/files_views/publicFileDrop.ts
index 0d782d48fc7..65756e83c74 100644
--- a/apps/files_sharing/src/files_views/publicFileDrop.ts
+++ b/apps/files_sharing/src/files_views/publicFileDrop.ts
@@ -4,7 +4,8 @@
*/
import type { VueConstructor } from 'vue'
-import { Folder, Permission, View, davRemoteURL, davRootPath, getNavigation } from '@nextcloud/files'
+import { Folder, Permission, View, getNavigation } from '@nextcloud/files'
+import { defaultRemoteURL, defaultRootPath } from '@nextcloud/files/dav'
import { loadState } from '@nextcloud/initial-state'
import { translate as t } from '@nextcloud/l10n'
import svgCloudUpload from '@mdi/svg/svg/cloud-upload.svg?raw'
@@ -45,8 +46,8 @@ export default () => {
// Fake a writeonly folder as root
folder: new Folder({
id: 0,
- source: `${davRemoteURL}${davRootPath}`,
- root: davRootPath,
+ source: `${defaultRemoteURL}${defaultRootPath}`,
+ root: defaultRootPath,
owner: null,
permissions: Permission.CREATE,
}),
diff --git a/apps/files_sharing/src/views/FilesViewFileDropEmptyContent.vue b/apps/files_sharing/src/views/FilesViewFileDropEmptyContent.vue
index 5571e5e9f5d..33fec9af028 100644
--- a/apps/files_sharing/src/views/FilesViewFileDropEmptyContent.vue
+++ b/apps/files_sharing/src/views/FilesViewFileDropEmptyContent.vue
@@ -5,13 +5,29 @@
<template>
<NcEmptyContent class="file-drop-empty-content"
data-cy-files-sharing-file-drop
- :name="t('files_sharing', 'File drop')">
+ :name="name">
<template #icon>
<NcIconSvgWrapper :svg="svgCloudUpload" />
</template>
<template #description>
- {{ t('files_sharing', 'Upload files to {foldername}.', { foldername }) }}
- {{ disclaimer === '' ? '' : t('files_sharing', 'By uploading files, you agree to the terms of service.') }}
+ <p>
+ {{ shareNote || t('files_sharing', 'Upload files to {foldername}.', { foldername }) }}
+ </p>
+ <p v-if="disclaimer">
+ {{ t('files_sharing', 'By uploading files, you agree to the terms of service.') }}
+ </p>
+ <NcNoteCard v-if="getSortedUploads().length"
+ class="file-drop-empty-content__note-card"
+ type="success">
+ <h2 id="file-drop-empty-content__heading">
+ {{ t('files_sharing', 'Successfully uploaded files') }}
+ </h2>
+ <ul aria-labelledby="file-drop-empty-content__heading" class="file-drop-empty-content__list">
+ <li v-for="file in getSortedUploads()" :key="file">
+ {{ file }}
+ </li>
+ </ul>
+ </NcNoteCard>
</template>
<template #action>
<template v-if="disclaimer">
@@ -34,16 +50,24 @@
</NcEmptyContent>
</template>
+<script lang="ts">
+/* eslint-disable import/first */
+
+// We need this on module level rather than on the instance as view will be refreshed by the files app after uploading
+const uploads = new Set<string>()
+</script>
+
<script setup lang="ts">
import { loadState } from '@nextcloud/initial-state'
import { translate as t } from '@nextcloud/l10n'
-import { getUploader, UploadPicker } from '@nextcloud/upload'
+import { getUploader, UploadPicker, UploadStatus } from '@nextcloud/upload'
import { ref } from 'vue'
import NcButton from '@nextcloud/vue/components/NcButton'
import NcDialog from '@nextcloud/vue/components/NcDialog'
import NcEmptyContent from '@nextcloud/vue/components/NcEmptyContent'
import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
+import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
import svgCloudUpload from '@mdi/svg/svg/cloud-upload.svg?raw'
defineProps<{
@@ -51,17 +75,62 @@ defineProps<{
}>()
const disclaimer = loadState<string>('files_sharing', 'disclaimer', '')
+const shareLabel = loadState<string>('files_sharing', 'label', '')
+const shareNote = loadState<string>('files_sharing', 'note', '')
+
+const name = shareLabel || t('files_sharing', 'File drop')
+
const showDialog = ref(false)
const uploadDestination = getUploader().destination
-</script>
-<style scoped>
-:deep(.terms-of-service-dialog) {
- min-height: min(100px, 20vh);
+getUploader()
+ .addNotifier((upload) => {
+ if (upload.status === UploadStatus.FINISHED && upload.file.name) {
+ // if a upload is finished and is not a meta upload (name is set)
+ // then we add the upload to the list of finished uploads to be shown to the user
+ uploads.add(upload.file.name)
+ }
+ })
+
+/**
+ * Get the previous uploads as sorted list
+ */
+function getSortedUploads() {
+ return [...uploads].sort((a, b) => a.localeCompare(b))
}
-/* TODO fix in library */
-.file-drop-empty-content :deep(.empty-content__action) {
- display: flex;
- gap: var(--default-grid-baseline);
+</script>
+
+<style scoped lang="scss">
+.file-drop-empty-content {
+ margin: auto;
+ max-width: max(50vw, 300px);
+
+ .file-drop-empty-content__note-card {
+ width: fit-content;
+ margin-inline: auto;
+ }
+
+ #file-drop-empty-content__heading {
+ margin-block: 0 10px;
+ font-weight: bold;
+ font-size: 20px;
+ }
+
+ .file-drop-empty-content__list {
+ list-style: inside;
+ max-height: min(350px, 33vh);
+ overflow-y: scroll;
+ padding-inline-end: calc(2 * var(--default-grid-baseline));
+ }
+
+ :deep(.terms-of-service-dialog) {
+ min-height: min(100px, 20vh);
+ }
+
+ /* TODO fix in library */
+ :deep(.empty-content__action) {
+ display: flex;
+ gap: var(--default-grid-baseline);
+ }
}
</style>
diff --git a/apps/files_sharing/tests/Controller/ShareControllerTest.php b/apps/files_sharing/tests/Controller/ShareControllerTest.php
index 524336787cb..a6bef1bed56 100644
--- a/apps/files_sharing/tests/Controller/ShareControllerTest.php
+++ b/apps/files_sharing/tests/Controller/ShareControllerTest.php
@@ -399,6 +399,8 @@ class ShareControllerTest extends \Test\TestCase {
->setPassword('password')
->setShareOwner('ownerUID')
->setSharedBy('initiatorUID')
+ ->setNote('The note')
+ ->setLabel('A label')
->setNode($file)
->setTarget("/$filename")
->setToken('token');
@@ -478,6 +480,8 @@ class ShareControllerTest extends \Test\TestCase {
'disclaimer' => 'My disclaimer text',
'owner' => 'ownerUID',
'ownerDisplayName' => 'ownerDisplay',
+ 'note' => 'The note',
+ 'label' => 'A label',
];
$response = $this->shareController->showShare();
@@ -487,9 +491,9 @@ class ShareControllerTest extends \Test\TestCase {
$csp = new ContentSecurityPolicy();
$csp->addAllowedFrameDomain('\'self\'');
$expectedResponse = new PublicTemplateResponse('files', 'index');
- $expectedResponse->setParams(['pageTitle' => $filename]);
+ $expectedResponse->setParams(['pageTitle' => 'A label']);
$expectedResponse->setContentSecurityPolicy($csp);
- $expectedResponse->setHeaderTitle($filename);
+ $expectedResponse->setHeaderTitle('A label');
$expectedResponse->setHeaderDetails('shared by ownerDisplay');
$expectedResponse->setHeaderActions([
new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', 'shareUrl'),
diff --git a/cypress/e2e/files_sharing/public-share/view_file-drop.cy.ts b/cypress/e2e/files_sharing/public-share/view_file-drop.cy.ts
index 3a38f0c9ec7..f95115ee591 100644
--- a/cypress/e2e/files_sharing/public-share/view_file-drop.cy.ts
+++ b/cypress/e2e/files_sharing/public-share/view_file-drop.cy.ts
@@ -149,9 +149,12 @@ describe('files_sharing: Public share - File drop', { testIsolation: true }, ()
after(() => cy.runOccCommand('config:app:delete core shareapi_public_link_disclaimertext'))
it('shows ToS on file-drop view', () => {
- cy.contains(`Upload files to ${shareName}`)
+ cy.get('[data-cy-files-sharing-file-drop]')
+ .contains(`Upload files to ${shareName}`)
+ .should('be.visible')
+ cy.get('[data-cy-files-sharing-file-drop]')
+ .contains('agree to the terms of service')
.should('be.visible')
- .should('contain.text', 'agree to the terms of service')
cy.findByRole('button', { name: /Terms of service/i })
.should('be.visible')
.click()