mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
Merge pull request #48893 from nextcloud/backport/48625/stable30
[stable30] fix(files): handle empty view with error
This commit is contained in:
commit
5c188bf977
21 changed files with 90 additions and 42 deletions
|
|
@ -33,7 +33,7 @@
|
|||
<script>
|
||||
import { encodePath } from '@nextcloud/paths'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { getToken, isPublic } from '../utils/davUtils.js'
|
||||
import { isPublicShare, getSharingToken } from '@nextcloud/sharing/public'
|
||||
|
||||
// preview width generation
|
||||
const previewWidth = 256
|
||||
|
|
@ -107,8 +107,8 @@ export default {
|
|||
return this.previewUrl
|
||||
}
|
||||
// TODO: find a nicer standard way of doing this?
|
||||
if (isPublic()) {
|
||||
return generateUrl(`/apps/files_sharing/publicpreview/${getToken()}?fileId=${this.fileid}&file=${encodePath(this.filename)}&x=${previewWidth}&y=${previewWidth}&a=1`)
|
||||
if (isPublicShare()) {
|
||||
return generateUrl(`/apps/files_sharing/publicpreview/${getSharingToken()}?fileId=${this.fileid}&file=${encodePath(this.filename)}&x=${previewWidth}&y=${previewWidth}&a=1`)
|
||||
}
|
||||
return generateUrl(`/core/preview?fileId=${this.fileid}&x=${previewWidth}&y=${previewWidth}&a=1`)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
|
||||
export const isPublic = function() {
|
||||
return !getCurrentUser()
|
||||
}
|
||||
|
||||
export const getToken = function() {
|
||||
return document.getElementById('sharingToken') && document.getElementById('sharingToken').value
|
||||
}
|
||||
41
apps/files/src/utils/davUtils.ts
Normal file
41
apps/files/src/utils/davUtils.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import type { WebDAVClientError } from 'webdav'
|
||||
|
||||
/**
|
||||
* Whether error is a WebDAVClientError
|
||||
* @param error - Any exception
|
||||
* @return {boolean} - Whether error is a WebDAVClientError
|
||||
*/
|
||||
function isWebDAVClientError(error: unknown): error is WebDAVClientError {
|
||||
return error instanceof Error && 'status' in error && 'response' in error
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a localized error message from webdav request
|
||||
* @param error - An exception from webdav request
|
||||
* @return {string} Localized error message for end user
|
||||
*/
|
||||
export function humanizeWebDAVError(error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
if (isWebDAVClientError(error)) {
|
||||
const status = error.status || error.response?.status || 0
|
||||
if ([400, 404, 405].includes(status)) {
|
||||
return t('files', 'Folder not found')
|
||||
} else if (status === 403) {
|
||||
return t('files', 'This operation is forbidden')
|
||||
} else if (status === 500) {
|
||||
return t('files', 'This directory is unavailable, please check the logs or contact the administrator')
|
||||
} else if (status === 503) {
|
||||
return t('files', 'Storage is temporarily not available')
|
||||
}
|
||||
}
|
||||
return t('files', 'Unexpected error: {error}', { error: error.message })
|
||||
}
|
||||
|
||||
return t('files', 'Unknown error')
|
||||
}
|
||||
|
|
@ -75,16 +75,32 @@
|
|||
|
||||
<!-- Empty content placeholder -->
|
||||
<template v-else-if="!loading && isEmptyDir">
|
||||
<div v-if="currentView?.emptyView" class="files-list__empty-view-wrapper">
|
||||
<!-- Empty due to error -->
|
||||
<NcEmptyContent v-if="error" :name="error" data-cy-files-content-error>
|
||||
<template #action>
|
||||
<NcButton type="secondary" @click="fetchContent">
|
||||
<template #icon>
|
||||
<IconReload :size="20" />
|
||||
</template>
|
||||
{{ t('files', 'Retry') }}
|
||||
</NcButton>
|
||||
</template>
|
||||
<template #icon>
|
||||
<IconAlertCircleOutline />
|
||||
</template>
|
||||
</NcEmptyContent>
|
||||
<!-- Custom empty view -->
|
||||
<div v-else-if="currentView?.emptyView" class="files-list__empty-view-wrapper">
|
||||
<div ref="customEmptyView" />
|
||||
</div>
|
||||
<!-- Default empty directory view -->
|
||||
<NcEmptyContent v-else
|
||||
:name="currentView?.emptyTitle || t('files', 'No files in here')"
|
||||
:description="currentView?.emptyCaption || t('files', 'Upload some content or sync with your devices!')"
|
||||
data-cy-files-content-empty>
|
||||
<template v-if="directory !== '/'" #action>
|
||||
<!-- Uploader -->
|
||||
<UploadPicker v-if="currentFolder && canUpload && !isQuotaExceeded"
|
||||
<UploadPicker v-if="canUpload && !isQuotaExceeded"
|
||||
allow-folders
|
||||
class="files-list__header-upload-button"
|
||||
:content="getContent"
|
||||
|
|
@ -93,10 +109,7 @@
|
|||
multiple
|
||||
@failed="onUploadFail"
|
||||
@uploaded="onUpload" />
|
||||
<NcButton v-else
|
||||
:aria-label="t('files', 'Go to the previous folder')"
|
||||
:to="toPreviousDir"
|
||||
type="primary">
|
||||
<NcButton v-else :to="toPreviousDir" type="primary">
|
||||
{{ t('files', 'Go back') }}
|
||||
</NcButton>
|
||||
</template>
|
||||
|
|
@ -134,6 +147,8 @@ import { UploadPicker, UploadStatus } from '@nextcloud/upload'
|
|||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
import IconAlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'
|
||||
import IconReload from 'vue-material-design-icons/Reload.vue'
|
||||
import LinkIcon from 'vue-material-design-icons/Link.vue'
|
||||
import ListViewIcon from 'vue-material-design-icons/FormatListBulletedSquare.vue'
|
||||
import NcAppContent from '@nextcloud/vue/dist/Components/NcAppContent.js'
|
||||
|
|
@ -161,6 +176,7 @@ import filesListWidthMixin from '../mixins/filesListWidth.ts'
|
|||
import filesSortingMixin from '../mixins/filesSorting.ts'
|
||||
import logger from '../logger.ts'
|
||||
import DragAndDropNotice from '../components/DragAndDropNotice.vue'
|
||||
import { humanizeWebDAVError } from '../utils/davUtils.ts'
|
||||
|
||||
const isSharingEnabled = (getCapabilities() as { files_sharing?: boolean })?.files_sharing !== undefined
|
||||
|
||||
|
|
@ -182,6 +198,8 @@ export default defineComponent({
|
|||
AccountPlusIcon,
|
||||
UploadPicker,
|
||||
ViewGridIcon,
|
||||
IconAlertCircleOutline,
|
||||
IconReload,
|
||||
},
|
||||
|
||||
mixins: [
|
||||
|
|
@ -227,6 +245,7 @@ export default defineComponent({
|
|||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
error: null as string | null,
|
||||
promise: null as CancelablePromise<ContentsWithRoot> | Promise<ContentsWithRoot> | null,
|
||||
|
||||
dirContentsFiltered: [] as INode[],
|
||||
|
|
@ -482,6 +501,7 @@ export default defineComponent({
|
|||
methods: {
|
||||
async fetchContent() {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
const dir = this.directory
|
||||
const currentView = this.currentView
|
||||
|
||||
|
|
@ -530,6 +550,7 @@ export default defineComponent({
|
|||
})
|
||||
} catch (error) {
|
||||
logger.error('Error while fetching content', { error })
|
||||
this.error = humanizeWebDAVError(error)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
|
|
|
|||
2
dist/3235-3235.js
vendored
2
dist/3235-3235.js
vendored
File diff suppressed because one or more lines are too long
1
dist/3235-3235.js.map
vendored
1
dist/3235-3235.js.map
vendored
File diff suppressed because one or more lines are too long
1
dist/3235-3235.js.map.license
vendored
1
dist/3235-3235.js.map.license
vendored
|
|
@ -1 +0,0 @@
|
|||
3235-3235.js.license
|
||||
2
dist/9918-9918.js
vendored
Normal file
2
dist/9918-9918.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/9918-9918.js.map
vendored
Normal file
1
dist/9918-9918.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/9918-9918.js.map.license
vendored
Symbolic link
1
dist/9918-9918.js.map.license
vendored
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
9918-9918.js.license
|
||||
4
dist/comments-comments-app.js
vendored
4
dist/comments-comments-app.js
vendored
File diff suppressed because one or more lines are too long
2
dist/comments-comments-app.js.map
vendored
2
dist/comments-comments-app.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/core-common.js
vendored
4
dist/core-common.js
vendored
File diff suppressed because one or more lines are too long
2
dist/core-common.js.map
vendored
2
dist/core-common.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/core-unified-search.js
vendored
4
dist/core-unified-search.js
vendored
File diff suppressed because one or more lines are too long
2
dist/core-unified-search.js.map
vendored
2
dist/core-unified-search.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/files-init.js
vendored
4
dist/files-init.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files-init.js.map
vendored
2
dist/files-init.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/files-main.js
vendored
4
dist/files-main.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files-main.js.map
vendored
2
dist/files-main.js.map
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue