7373 <!-- Drag and drop notice -->
7474 <DragAndDropNotice v-if =" !loading && canUpload && currentFolder" :current-folder =" currentFolder" />
7575
76- <!-- Initial loading -->
77- <NcLoadingIcon v-if =" loading && !isRefreshing"
76+ <!--
77+ Initial current view loading0. This should never happen,
78+ views are supposed to be registered far earlier in the lifecycle.
79+ In case the URL is bad or a view is missing, we show a loading icon.
80+ -->
81+ <NcLoadingIcon v-if =" !currentView"
7882 class =" files-list__loading-icon"
7983 :size =" 38"
8084 :name =" t('files', 'Loading current folder')" />
8185
82- <!-- Empty content placeholder -->
83- <template v-else-if =" ! loading && isEmptyDir && currentFolder && currentView " >
84- <div class =" files-list__before" >
85- <!-- Headers -->
86- <FilesListHeader v-for =" header in headers"
87- :key =" header.id"
88- :current-folder =" currentFolder"
89- :current-view =" currentView"
90- :header =" header" />
91- </div >
92- <!-- Empty due to error -->
93- <NcEmptyContent v-if =" error" :name =" error" data-cy-files-content-error >
94- <template #action >
95- <NcButton type =" secondary" @click =" fetchContent" >
96- <template #icon >
97- <IconReload :size =" 20" />
98- </template >
99- {{ t('files', 'Retry') }}
100- </NcButton >
101- </template >
102- <template #icon >
103- <IconAlertCircleOutline />
104- </template >
105- </NcEmptyContent >
106- <!-- Custom empty view -->
107- <div v-else-if =" currentView?.emptyView" class =" files-list__empty-view-wrapper" >
108- <div ref =" customEmptyView" />
109- </div >
110- <!-- Default empty directory view -->
111- <NcEmptyContent v-else
112- :name =" currentView?.emptyTitle || t('files', 'No files in here')"
113- :description =" currentView?.emptyCaption || t('files', 'Upload some content or sync with your devices!')"
114- data-cy-files-content-empty >
115- <template v-if =" directory !== ' /' " #action >
116- <!-- Uploader -->
117- <UploadPicker v-if =" canUpload && !isQuotaExceeded"
118- allow-folders
119- class =" files-list__header-upload-button"
120- :content =" getContent"
121- :destination =" currentFolder"
122- :forbidden-characters =" forbiddenCharacters"
123- multiple
124- @failed =" onUploadFail"
125- @uploaded =" onUpload" />
126- <NcButton v-else :to =" toPreviousDir" type =" primary" >
127- {{ t('files', 'Go back') }}
128- </NcButton >
129- </template >
130- <template #icon >
131- <NcIconSvgWrapper :svg =" currentView.icon" />
132- </template >
133- </NcEmptyContent >
134- </template >
135-
136- <!-- File list -->
86+ <!-- File list - always mounted -->
13787 <FilesListVirtual v-else
13888 ref =" filesListVirtual"
13989 :current-folder =" currentFolder"
14090 :current-view =" currentView"
14191 :nodes =" dirContentsSorted"
142- :summary =" summary" />
92+ :summary =" summary" >
93+ <template #empty >
94+ <!-- Initial loading -->
95+ <NcLoadingIcon v-if =" loading && !isRefreshing"
96+ class =" files-list__loading-icon"
97+ :size =" 38"
98+ :name =" t('files', 'Loading current folder')" />
99+
100+ <!-- Empty due to error -->
101+ <NcEmptyContent v-else-if =" error" :name =" error" data-cy-files-content-error >
102+ <template #action >
103+ <NcButton type =" secondary" @click =" fetchContent" >
104+ <template #icon >
105+ <IconReload :size =" 20" />
106+ </template >
107+ {{ t('files', 'Retry') }}
108+ </NcButton >
109+ </template >
110+ <template #icon >
111+ <IconAlertCircleOutline />
112+ </template >
113+ </NcEmptyContent >
114+
115+ <!-- Custom empty view -->
116+ <div v-else-if =" currentView?.emptyView" class =" files-list__empty-view-wrapper" >
117+ <div ref =" customEmptyView" />
118+ </div >
119+
120+ <!-- Default empty directory view -->
121+ <NcEmptyContent v-else
122+ :name =" currentView?.emptyTitle || t('files', 'No files in here')"
123+ :description =" currentView?.emptyCaption || t('files', 'Upload some content or sync with your devices!')"
124+ data-cy-files-content-empty >
125+ <template v-if =" directory !== ' /' " #action >
126+ <!-- Uploader -->
127+ <UploadPicker v-if =" canUpload && !isQuotaExceeded"
128+ allow-folders
129+ class =" files-list__header-upload-button"
130+ :content =" getContent"
131+ :destination =" currentFolder"
132+ :forbidden-characters =" forbiddenCharacters"
133+ multiple
134+ @failed =" onUploadFail"
135+ @uploaded =" onUpload" />
136+ <NcButton v-else :to =" toPreviousDir" type =" primary" >
137+ {{ t('files', 'Go back') }}
138+ </NcButton >
139+ </template >
140+ <template #icon >
141+ <NcIconSvgWrapper :svg =" currentView?.icon" />
142+ </template >
143+ </NcEmptyContent >
144+ </template >
145+ </FilesListVirtual >
143146 </NcAppContent >
144147</template >
145148
146149<script lang="ts">
147- import type { ContentsWithRoot , FileListAction , Folder , INode } from ' @nextcloud/files'
150+ import type { ContentsWithRoot , FileListAction , INode } from ' @nextcloud/files'
148151import type { Upload } from ' @nextcloud/upload'
149152import type { CancelablePromise } from ' cancelable-promise'
150153import type { ComponentPublicInstance } from ' vue'
151154import type { Route } from ' vue-router'
152155import type { UserConfig } from ' ../types.ts'
153156
157+ import { getCurrentUser } from ' @nextcloud/auth'
154158import { getCapabilities } from ' @nextcloud/capabilities'
155159import { emit , subscribe , unsubscribe } from ' @nextcloud/event-bus'
156- import { Node , Permission , sortNodes , getFileListActions } from ' @nextcloud/files'
160+ import { Folder , Node , Permission , sortNodes , getFileListActions } from ' @nextcloud/files'
161+ import { getRemoteURL , getRootPath } from ' @nextcloud/files/dav'
157162import { translate as t } from ' @nextcloud/l10n'
158163import { join , dirname , normalize } from ' path'
159164import { showError , showSuccess , showWarning } from ' @nextcloud/dialogs'
@@ -181,7 +186,6 @@ import ViewGridIcon from 'vue-material-design-icons/ViewGrid.vue'
181186import { action as sidebarAction } from ' ../actions/sidebarAction.ts'
182187import { getSummaryFor } from ' ../utils/fileUtils.ts'
183188import { humanizeWebDAVError } from ' ../utils/davUtils.ts'
184- import { useFileListHeaders } from ' ../composables/useFileListHeaders.ts'
185189import { useFileListWidth } from ' ../composables/useFileListWidth.ts'
186190import { useFilesStore } from ' ../store/files.ts'
187191import { useFiltersStore } from ' ../store/filters.ts'
@@ -194,7 +198,6 @@ import { useUserConfigStore } from '../store/userconfig.ts'
194198import { useViewConfigStore } from ' ../store/viewConfig.ts'
195199import BreadCrumbs from ' ../components/BreadCrumbs.vue'
196200import DragAndDropNotice from ' ../components/DragAndDropNotice.vue'
197- import FilesListHeader from ' ../components/FilesListHeader.vue'
198201import FilesListVirtual from ' ../components/FilesListVirtual.vue'
199202import filesSortingMixin from ' ../mixins/filesSorting.ts'
200203import logger from ' ../logger.ts'
@@ -207,7 +210,6 @@ export default defineComponent({
207210 components: {
208211 BreadCrumbs ,
209212 DragAndDropNotice ,
210- FilesListHeader ,
211213 FilesListVirtual ,
212214 LinkIcon ,
213215 ListViewIcon ,
@@ -256,7 +258,6 @@ export default defineComponent({
256258 directory ,
257259 fileId ,
258260 fileListWidth ,
259- headers: useFileListHeaders (),
260261 t ,
261262
262263 filesStore ,
@@ -321,12 +322,23 @@ export default defineComponent({
321322 /**
322323 * The current folder.
323324 */
324- currentFolder(): Folder | undefined {
325+ currentFolder(): Folder {
326+ // TMP folder to use until we have the first valid folder
327+ // fetched and cached. This allow us to mount the FilesListVirtual
328+ // at all time and avoid unmount/mount and undesired rendering issues.
329+ const dummyFolder = new Folder ({
330+ id: 0 ,
331+ source: getRemoteURL () + getRootPath (),
332+ root: getRootPath (),
333+ owner: getCurrentUser ()?.uid || null ,
334+ permissions: Permission .NONE ,
335+ })
336+
325337 if (! this .currentView ?.id ) {
326- return
338+ return dummyFolder
327339 }
328340
329- return this .filesStore .getDirectoryByPath (this .currentView .id , this .directory )
341+ return this .filesStore .getDirectoryByPath (this .currentView .id , this .directory ) || dummyFolder
330342 },
331343
332344 dirContents(): Node [] {
@@ -338,7 +350,7 @@ export default defineComponent({
338350 /**
339351 * The current directory contents.
340352 */
341- dirContentsSorted() {
353+ dirContentsSorted(): INode [] {
342354 if (! this .currentView ) {
343355 return []
344356 }
@@ -574,9 +586,20 @@ export default defineComponent({
574586
575587 if (! currentView ) {
576588 logger .debug (' The current view doesn\' t exists or is not ready.' , { currentView })
589+
590+ // If we still haven't a valid view, let's wait for the page to load
591+ // then try again. Else redirect to the default view
592+ window .addEventListener (' DOMContentLoaded' , () => {
593+ if (! this .currentView ) {
594+ logger .warn (' No current view after DOMContentLoaded, redirecting to the default view' )
595+ window .OCP .Files .Router .goToRoute (null , { view: ' files' })
596+ }
597+ }, { once: true })
577598 return
578599 }
579600
601+ logger .debug (' Fetching contents for directory' , { dir , currentView })
602+
580603 // If we have a cancellable promise ongoing, cancel it
581604 if (this .promise && ' cancel' in this .promise ) {
582605 this .promise .cancel ()
0 commit comments