diff --git a/src/Searching.ts b/src/Searching.ts index f9b2830e85..d507bd10ef 100644 --- a/src/Searching.ts +++ b/src/Searching.ts @@ -718,4 +718,8 @@ export interface SearchInfo { * The total count of matching results as returned by the backend. */ count?: number; + /** + * Describe the error if any occured. + */ + error?: Error; } diff --git a/src/components/structures/RoomSearchView.tsx b/src/components/structures/RoomSearchView.tsx index cd03a0140a..4aadabd723 100644 --- a/src/components/structures/RoomSearchView.tsx +++ b/src/components/structures/RoomSearchView.tsx @@ -21,8 +21,6 @@ import { _t } from "../../languageHandler"; import { haveRendererForEvent } from "../../events/EventTileFactory"; import SearchResultTile from "../views/rooms/SearchResultTile"; import { searchPagination, SearchScope } from "../../Searching"; -import Modal from "../../Modal"; -import ErrorDialog from "../views/dialogs/ErrorDialog"; import type ResizeNotifier from "../../utils/ResizeNotifier"; import MatrixClientContext from "../../contexts/MatrixClientContext"; import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks"; @@ -45,7 +43,7 @@ interface Props { abortController?: AbortController; resizeNotifier: ResizeNotifier; className: string; - onUpdate(inProgress: boolean, results: ISearchResults | null): void; + onUpdate(inProgress: boolean, results: ISearchResults | null, error: Error | null): void; } // XXX: todo: merge overlapping results somehow? @@ -70,7 +68,7 @@ export const RoomSearchView = forwardRef( const handleSearchResult = useCallback( (searchPromise: Promise): Promise => { - onUpdate(true, null); + onUpdate(true, null, null); return searchPromise.then( async (results): Promise => { @@ -116,7 +114,7 @@ export const RoomSearchView = forwardRef( setHighlights(highlights); setResults({ ...results }); // copy to force a refresh - onUpdate(false, results); + onUpdate(false, results, null); return false; }, (error) => { @@ -125,11 +123,7 @@ export const RoomSearchView = forwardRef( return false; } logger.error("Search failed", error); - Modal.createDialog(ErrorDialog, { - title: _t("error_dialog|search_failed|title"), - description: error?.message ?? _t("error_dialog|search_failed|server_unavailable"), - }); - onUpdate(false, null); + onUpdate(false, null, error); return false; }, ); diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 3426ef7e97..7ad4abe15d 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -1716,11 +1716,12 @@ export class RoomView extends React.Component { this.onSearch(this.state.search?.term ?? "", scope); }; - private onSearchUpdate = (inProgress: boolean, searchResults: ISearchResults | null): void => { + private onSearchUpdate = (inProgress: boolean, searchResults: ISearchResults | null, error: Error | null): void => { this.setState({ search: { ...this.state.search!, count: searchResults?.count, + error: error ?? undefined, inProgress, }, }); diff --git a/src/components/views/rooms/RoomSearchAuxPanel.tsx b/src/components/views/rooms/RoomSearchAuxPanel.tsx index fa0a24abe9..be821c7de1 100644 --- a/src/components/views/rooms/RoomSearchAuxPanel.tsx +++ b/src/components/views/rooms/RoomSearchAuxPanel.tsx @@ -40,6 +40,8 @@ const RoomSearchAuxPanel: React.FC = ({ searchInfo, isRoomEncrypted, onSe { count: searchInfo.count }, { query: () => {searchInfo.term} }, ) + ) : searchInfo?.error !== undefined ? ( + searchInfo?.error.message ) : ( )} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 202951e6a6..37a3a0718e 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1133,11 +1133,7 @@ "title": "Unable to copy room link" }, "error_loading_user_profile": "Could not load user profile", - "forget_room_failed": "Failed to forget room %(errCode)s", - "search_failed": { - "server_unavailable": "Server may be unavailable, overloaded, or search timed out :(", - "title": "Search failed" - } + "forget_room_failed": "Failed to forget room %(errCode)s" }, "error_user_not_logged_in": "User is not logged in", "event_preview": { diff --git a/test/unit-tests/components/structures/RoomSearchView-test.tsx b/test/unit-tests/components/structures/RoomSearchView-test.tsx index e52da13be6..300a4c1ab3 100644 --- a/test/unit-tests/components/structures/RoomSearchView-test.tsx +++ b/test/unit-tests/components/structures/RoomSearchView-test.tsx @@ -247,7 +247,7 @@ describe("", () => { await screen.findByRole("progressbar"); await screen.findByText("Potato"); - expect(onUpdate).toHaveBeenCalledWith(false, expect.objectContaining({})); + expect(onUpdate).toHaveBeenCalledWith(false, expect.objectContaining({}), null); rerender( @@ -314,7 +314,8 @@ describe("", () => { }); }); - it("should show modal if error is encountered", async () => { + it("report error if one is encountered", async () => { + const onUpdate = jest.fn(); const deferred = defer(); render( @@ -326,14 +327,18 @@ describe("", () => { promise={deferred.promise} resizeNotifier={resizeNotifier} className="someClass" - onUpdate={jest.fn()} + onUpdate={onUpdate} /> , ); - deferred.reject(new Error("Some error")); + deferred.reject("Some error"); + try { + // Wait for RoomSearchView to process the promise + await deferred.promise; + } catch {} - await screen.findByText("Search failed"); - await screen.findByText("Some error"); + expect(onUpdate).toHaveBeenCalledWith(false, null, "Some error"); + expect(onUpdate).toHaveBeenCalledTimes(2); }); it("should combine search results when the query is present in multiple sucessive messages", async () => {