diff --git a/src/gui/src/UI/Dashboard/TabFiles.js b/src/gui/src/UI/Dashboard/TabFiles.js index 33f8da040..e9e7a8367 100644 --- a/src/gui/src/UI/Dashboard/TabFiles.js +++ b/src/gui/src/UI/Dashboard/TabFiles.js @@ -2799,7 +2799,7 @@ const TabFiles = { this.classList.toggle('active', pressed); this.setAttribute('aria-pressed', String(pressed)); }); - await this.renderDirectory(window.shared_path); + await this.renderDirectory(window.shared_path, { filterChange: true }); if ( this.currentPath === window.shared_path ) { this.$el_window.find('.files-tab .files').scrollTop(0); } @@ -2816,6 +2816,7 @@ const TabFiles = { * @param {boolean} [options.skipUrlUpdate] - If true, don't update browser URL * @param {boolean} [options.skipNavHistory] - If true, don't add to navigation history * @param {boolean} [options.force] - Supersede a navigation that is still loading + * @param {boolean} [options.filterChange] - A people filter change: if Shared can't be listed, filter the rows already loaded * @returns {Promise} */ async renderDirectory (target, options = {}) { @@ -2880,10 +2881,17 @@ const TabFiles = { directoryContents = isSharedView ? await this.listSharedView() : await window.puter.fs.readdir(readdirArg); + if ( isSharedView ) this.sharedRows = directoryContents; } catch ( err ) { console.error('Failed to read directory:', err); - readFailed = true; - directoryContents = []; + // Filtering needs no server: a filter change still applies to + // the rows already loaded. + if ( isSharedView && options.filterChange && this.sharedRows ) { + directoryContents = this.sharedRows; + } else { + readFailed = true; + directoryContents = []; + } } if ( seq !== this.renderSeq ) return; if ( ! directoryContents ) { diff --git a/src/gui/src/UI/Dashboard/TabFiles.test.js b/src/gui/src/UI/Dashboard/TabFiles.test.js index 302397cf0..6af1b7099 100644 --- a/src/gui/src/UI/Dashboard/TabFiles.test.js +++ b/src/gui/src/UI/Dashboard/TabFiles.test.js @@ -663,6 +663,33 @@ describe('TabFiles people filter across navigation', () => { expect(listed()).toEqual(['a', 'b']); }); + it('still filters when Shared fails to list again', async () => { + await TabFiles.renderDirectory(SHARED); + vi.spyOn(console, 'error').mockImplementation(() => {}); + TabFiles.listSharedView.mockImplementation(async () => { + throw new Error('offline'); + }); + + await TabFiles.setSharerFilter('bob'); + + expect(listed()).toEqual(['b']); + expect($('.sharer-chip.active').attr('data-sharer')).toBe('bob'); + expect($('.files').text()).not.toContain("couldn't be opened"); + }); + + it('still says a Shared it was asked to open could not be listed', async () => { + await TabFiles.renderDirectory(SHARED); + await TabFiles.renderDirectory('/alice/Documents'); + vi.spyOn(console, 'error').mockImplementation(() => {}); + TabFiles.listSharedView.mockImplementation(async () => { + throw new Error('offline'); + }); + + await TabFiles.renderDirectory(SHARED); + + expect($('.files').text()).toContain("couldn't be opened"); + }); + it("clears once the user goes back to their own files", async () => { await TabFiles.renderDirectory(SHARED); await TabFiles.setSharerFilter('bob');