diff options
author | Grigorii K. Shartsev <me@shgk.me> | 2024-10-09 12:37:33 +0200 |
---|---|---|
committer | backportbot[bot] <backportbot[bot]@users.noreply.github.com> | 2024-10-28 12:08:41 +0000 |
commit | 5bb1211caef8f47387d17a2f4ab5ac7ae87dc408 (patch) | |
tree | 28b54deebe23ec89bf6f5efb2a80cc393cdda964 /apps | |
parent | d717176e9c7c50da0fcc04f5bbbc65e3dcd0cdaa (diff) | |
download | nextcloud-server-5bb1211caef8f47387d17a2f4ab5ac7ae87dc408.tar.gz nextcloud-server-5bb1211caef8f47387d17a2f4ab5ac7ae87dc408.zip |
fix(files): handle empty view with error
Signed-off-by: Grigorii K. Shartsev <me@shgk.me>
Diffstat (limited to 'apps')
-rw-r--r-- | apps/files/src/utils/davUtils.ts | 36 | ||||
-rw-r--r-- | apps/files/src/views/FilesList.vue | 58 |
2 files changed, 77 insertions, 17 deletions
diff --git a/apps/files/src/utils/davUtils.ts b/apps/files/src/utils/davUtils.ts index 38137a04d16..cf49df5b278 100644 --- a/apps/files/src/utils/davUtils.ts +++ b/apps/files/src/utils/davUtils.ts @@ -4,6 +4,8 @@ */ import { getCurrentUser } from '@nextcloud/auth' +import { t } from '@nextcloud/l10n' +import type { WebDAVClientError } from 'webdav' /** * Check whether this is a public share @@ -21,3 +23,37 @@ export function getToken() { const tokenElement = document.getElementById('sharingToken') as (HTMLInputElement | null) return tokenElement?.value } + +/** + * 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') +} diff --git a/apps/files/src/views/FilesList.vue b/apps/files/src/views/FilesList.vue index ba206aacad1..314e95f7c49 100644 --- a/apps/files/src/views/FilesList.vue +++ b/apps/files/src/views/FilesList.vue @@ -89,22 +89,38 @@ :name="t('files', 'Loading current folder')" /> <!-- Empty content placeholder --> - <NcEmptyContent v-else-if="!loading && isEmptyDir" - :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 #action> - <NcButton v-if="dir !== '/'" - :aria-label="t('files', 'Go to the previous folder')" - type="primary" - :to="toPreviousDir"> - {{ t('files', 'Go back') }} - </NcButton> - </template> - <template #icon> - <NcIconSvgWrapper :svg="currentView.icon" /> - </template> - </NcEmptyContent> + <template v-else-if="!loading && isEmptyDir"> + <!-- 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> + <!-- 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="dir !== '/'" #action> + <NcButton :aria-label="t('files', 'Go to the previous folder')" + type="primary" + :to="toPreviousDir"> + {{ t('files', 'Go back') }} + </NcButton> + </template> + <template #icon> + <NcIconSvgWrapper :svg="currentView.icon" /> + </template> + </NcEmptyContent> + </template> <!-- File list --> <FilesListVirtual v-else @@ -135,6 +151,8 @@ import { join, dirname } from 'path' import { Parser } from 'xml2js' 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' @@ -160,6 +178,8 @@ import filesListWidthMixin from '../mixins/filesListWidth.ts' import filesSortingMixin from '../mixins/filesSorting.ts' import logger from '../logger.js' import DragAndDropNotice from '../components/DragAndDropNotice.vue' +import { humanizeWebDAVError } from '../utils/davUtils.ts' + import debounce from 'debounce' const isSharingEnabled = (getCapabilities() as { files_sharing?: boolean })?.files_sharing !== undefined @@ -182,6 +202,8 @@ export default defineComponent({ AccountPlusIcon, UploadPicker, ViewGridIcon, + IconAlertCircleOutline, + IconReload, }, mixins: [ @@ -221,8 +243,8 @@ export default defineComponent({ return { filterText: '', loading: true, + error: null as string | null, promise: null as Promise<ContentsWithRoot> | CancelablePromise<ContentsWithRoot> | null, - unsubscribeStoreCallback: () => {}, } }, @@ -475,6 +497,7 @@ export default defineComponent({ methods: { async fetchContent() { this.loading = true + this.error = null const dir = this.dir const currentView = this.currentView @@ -523,6 +546,7 @@ export default defineComponent({ }) } catch (error) { logger.error('Error while fetching content', { error }) + this.error = humanizeWebDAVError(error) } finally { this.loading = false } |