Skip to content

Commit 5734ffd

Browse files
authored
Merge pull request #53118 from nextcloud/backport/53055/master
fix(files_sharing): show note, label and list of uploaded files on file drop
2 parents 27149b7 + 9ff1134 commit 5734ffd

16 files changed

+109
-29
lines changed

apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ public function renderPage(IShare $share, string $token, string $path): Template
9191
'disclaimer',
9292
$this->appConfig->getValueString('core', 'shareapi_public_link_disclaimertext'),
9393
);
94+
// file drops do not request the root folder so we need to provide label and note if available
95+
$this->initialState->provideInitialState('label', $share->getLabel());
96+
$this->initialState->provideInitialState('note', $share->getNote());
9497
}
9598
// Set up initial state
9699
$this->initialState->provideInitialState('isPublic', true);

apps/files_sharing/src/files_views/publicFileDrop.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
*/
55
import type { VueConstructor } from 'vue'
66

7-
import { Folder, Permission, View, davRemoteURL, davRootPath, getNavigation } from '@nextcloud/files'
7+
import { Folder, Permission, View, getNavigation } from '@nextcloud/files'
8+
import { defaultRemoteURL, defaultRootPath } from '@nextcloud/files/dav'
89
import { loadState } from '@nextcloud/initial-state'
910
import { translate as t } from '@nextcloud/l10n'
1011
import svgCloudUpload from '@mdi/svg/svg/cloud-upload.svg?raw'
@@ -45,8 +46,8 @@ export default () => {
4546
// Fake a writeonly folder as root
4647
folder: new Folder({
4748
id: 0,
48-
source: `${davRemoteURL}${davRootPath}`,
49-
root: davRootPath,
49+
source: `${defaultRemoteURL}${defaultRootPath}`,
50+
root: defaultRootPath,
5051
owner: null,
5152
permissions: Permission.CREATE,
5253
}),

apps/files_sharing/src/views/FilesViewFileDropEmptyContent.vue

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,29 @@
55
<template>
66
<NcEmptyContent class="file-drop-empty-content"
77
data-cy-files-sharing-file-drop
8-
:name="t('files_sharing', 'File drop')">
8+
:name="name">
99
<template #icon>
1010
<NcIconSvgWrapper :svg="svgCloudUpload" />
1111
</template>
1212
<template #description>
13-
{{ t('files_sharing', 'Upload files to {foldername}.', { foldername }) }}
14-
{{ disclaimer === '' ? '' : t('files_sharing', 'By uploading files, you agree to the terms of service.') }}
13+
<p>
14+
{{ shareNote || t('files_sharing', 'Upload files to {foldername}.', { foldername }) }}
15+
</p>
16+
<p v-if="disclaimer">
17+
{{ t('files_sharing', 'By uploading files, you agree to the terms of service.') }}
18+
</p>
19+
<NcNoteCard v-if="getSortedUploads().length"
20+
class="file-drop-empty-content__note-card"
21+
type="success">
22+
<h2 id="file-drop-empty-content__heading">
23+
{{ t('files_sharing', 'Successfully uploaded files') }}
24+
</h2>
25+
<ul aria-labelledby="file-drop-empty-content__heading" class="file-drop-empty-content__list">
26+
<li v-for="file in getSortedUploads()" :key="file">
27+
{{ file }}
28+
</li>
29+
</ul>
30+
</NcNoteCard>
1531
</template>
1632
<template #action>
1733
<template v-if="disclaimer">
@@ -34,34 +50,87 @@
3450
</NcEmptyContent>
3551
</template>
3652

53+
<script lang="ts">
54+
/* eslint-disable import/first */
55+
56+
// We need this on module level rather than on the instance as view will be refreshed by the files app after uploading
57+
const uploads = new Set<string>()
58+
</script>
59+
3760
<script setup lang="ts">
3861
import { loadState } from '@nextcloud/initial-state'
3962
import { translate as t } from '@nextcloud/l10n'
40-
import { getUploader, UploadPicker } from '@nextcloud/upload'
63+
import { getUploader, UploadPicker, UploadStatus } from '@nextcloud/upload'
4164
import { ref } from 'vue'
4265
4366
import NcButton from '@nextcloud/vue/components/NcButton'
4467
import NcDialog from '@nextcloud/vue/components/NcDialog'
4568
import NcEmptyContent from '@nextcloud/vue/components/NcEmptyContent'
4669
import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
70+
import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
4771
import svgCloudUpload from '@mdi/svg/svg/cloud-upload.svg?raw'
4872
4973
defineProps<{
5074
foldername: string
5175
}>()
5276
5377
const disclaimer = loadState<string>('files_sharing', 'disclaimer', '')
78+
const shareLabel = loadState<string>('files_sharing', 'label', '')
79+
const shareNote = loadState<string>('files_sharing', 'note', '')
80+
81+
const name = shareLabel || t('files_sharing', 'File drop')
82+
5483
const showDialog = ref(false)
5584
const uploadDestination = getUploader().destination
56-
</script>
5785
58-
<style scoped>
59-
:deep(.terms-of-service-dialog) {
60-
min-height: min(100px, 20vh);
86+
getUploader()
87+
.addNotifier((upload) => {
88+
if (upload.status === UploadStatus.FINISHED && upload.file.name) {
89+
// if a upload is finished and is not a meta upload (name is set)
90+
// then we add the upload to the list of finished uploads to be shown to the user
91+
uploads.add(upload.file.name)
92+
}
93+
})
94+
95+
/**
96+
* Get the previous uploads as sorted list
97+
*/
98+
function getSortedUploads() {
99+
return [...uploads].sort((a, b) => a.localeCompare(b))
61100
}
62-
/* TODO fix in library */
63-
.file-drop-empty-content :deep(.empty-content__action) {
64-
display: flex;
65-
gap: var(--default-grid-baseline);
101+
</script>
102+
103+
<style scoped lang="scss">
104+
.file-drop-empty-content {
105+
margin: auto;
106+
max-width: max(50vw, 300px);
107+
108+
.file-drop-empty-content__note-card {
109+
width: fit-content;
110+
margin-inline: auto;
111+
}
112+
113+
#file-drop-empty-content__heading {
114+
margin-block: 0 10px;
115+
font-weight: bold;
116+
font-size: 20px;
117+
}
118+
119+
.file-drop-empty-content__list {
120+
list-style: inside;
121+
max-height: min(350px, 33vh);
122+
overflow-y: scroll;
123+
padding-inline-end: calc(2 * var(--default-grid-baseline));
124+
}
125+
126+
:deep(.terms-of-service-dialog) {
127+
min-height: min(100px, 20vh);
128+
}
129+
130+
/* TODO fix in library */
131+
:deep(.empty-content__action) {
132+
display: flex;
133+
gap: var(--default-grid-baseline);
134+
}
66135
}
67136
</style>

apps/files_sharing/tests/Controller/ShareControllerTest.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,8 @@ public function testShowFileDropShare(): void {
399399
->setPassword('password')
400400
->setShareOwner('ownerUID')
401401
->setSharedBy('initiatorUID')
402+
->setNote('The note')
403+
->setLabel('A label')
402404
->setNode($file)
403405
->setTarget("/$filename")
404406
->setToken('token');
@@ -478,6 +480,8 @@ public function testShowFileDropShare(): void {
478480
'disclaimer' => 'My disclaimer text',
479481
'owner' => 'ownerUID',
480482
'ownerDisplayName' => 'ownerDisplay',
483+
'note' => 'The note',
484+
'label' => 'A label',
481485
];
482486

483487
$response = $this->shareController->showShare();
@@ -487,9 +491,9 @@ public function testShowFileDropShare(): void {
487491
$csp = new ContentSecurityPolicy();
488492
$csp->addAllowedFrameDomain('\'self\'');
489493
$expectedResponse = new PublicTemplateResponse('files', 'index');
490-
$expectedResponse->setParams(['pageTitle' => $filename]);
494+
$expectedResponse->setParams(['pageTitle' => 'A label']);
491495
$expectedResponse->setContentSecurityPolicy($csp);
492-
$expectedResponse->setHeaderTitle($filename);
496+
$expectedResponse->setHeaderTitle('A label');
493497
$expectedResponse->setHeaderDetails('shared by ownerDisplay');
494498
$expectedResponse->setHeaderActions([
495499
new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', 'shareUrl'),

cypress/e2e/files_sharing/public-share/view_file-drop.cy.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,12 @@ describe('files_sharing: Public share - File drop', { testIsolation: true }, ()
149149
after(() => cy.runOccCommand('config:app:delete core shareapi_public_link_disclaimertext'))
150150

151151
it('shows ToS on file-drop view', () => {
152-
cy.contains(`Upload files to ${shareName}`)
152+
cy.get('[data-cy-files-sharing-file-drop]')
153+
.contains(`Upload files to ${shareName}`)
154+
.should('be.visible')
155+
cy.get('[data-cy-files-sharing-file-drop]')
156+
.contains('agree to the terms of service')
153157
.should('be.visible')
154-
.should('contain.text', 'agree to the terms of service')
155158
cy.findByRole('button', { name: /Terms of service/i })
156159
.should('be.visible')
157160
.click()

dist/358-358.js

Lines changed: 0 additions & 2 deletions
This file was deleted.

dist/358-358.js.map

Lines changed: 0 additions & 1 deletion
This file was deleted.

dist/358-358.js.map.license

Lines changed: 0 additions & 1 deletion
This file was deleted.

dist/4309-4309.js

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

0 commit comments

Comments
 (0)