From b09ce36277214489dd492a7b77d959de904ea208 Mon Sep 17 00:00:00 2001 From: Omar H Date: Thu, 21 Nov 2024 21:00:55 -0500 Subject: [PATCH 01/13] do not throw error when status is 204 --- .../src/containers/ITwinGrid/useITwinFavorites.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts index 8d858ea..aa6e75a 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts @@ -52,9 +52,11 @@ export const useITwinFavorites = ( Accept: "application/vnd.bentley.itwin-platform.v1+json", }, }); - if (!result || result.status !== 200) { + + if (!result || (result.status !== 200 && result.status !== 204)) { throw new Error(`Failed to add iTwin ${iTwinId} to favorites`); } + setITwinFavorites((prev) => new Set([...prev, iTwinId])); } catch (error) { console.error(error); @@ -86,7 +88,7 @@ export const useITwinFavorites = ( }, }); - if (!result || result.status !== 200) { + if (!result || (result.status !== 200 && result.status !== 204)) { throw new Error(`Failed to remove iTwin ${iTwinId} to favorites`); } @@ -134,7 +136,7 @@ export const useITwinFavorites = ( `Failed to fetch iTwin favorites from ${url}.\nNo response.` ); } - if (result.status !== 200) { + if (result.status !== 200 && result.status !== 204) { throw new Error( `Failed to fetch iTwin favorites from ${url}.\nStatus: ${result.status}` ); From dc434625b485223b127e106dcdec5cd43d121dbb Mon Sep 17 00:00:00 2001 From: Omar H Date: Thu, 21 Nov 2024 21:01:28 -0500 Subject: [PATCH 02/13] Fix bug in ITwin favorites functionality --- .../fix-itwin-favorites_2024-11-22-02-01.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/@itwin/imodel-browser-react/fix-itwin-favorites_2024-11-22-02-01.json diff --git a/common/changes/@itwin/imodel-browser-react/fix-itwin-favorites_2024-11-22-02-01.json b/common/changes/@itwin/imodel-browser-react/fix-itwin-favorites_2024-11-22-02-01.json new file mode 100644 index 0000000..b8447d2 --- /dev/null +++ b/common/changes/@itwin/imodel-browser-react/fix-itwin-favorites_2024-11-22-02-01.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/imodel-browser-react", + "comment": "Fix bug in itwin favorites", + "type": "none" + } + ], + "packageName": "@itwin/imodel-browser-react" +} \ No newline at end of file From 603e8daaf81aa5e911fbaf6f649453c1c550f6e6 Mon Sep 17 00:00:00 2001 From: Omar H Date: Fri, 22 Nov 2024 10:54:03 -0500 Subject: [PATCH 03/13] Remove 204 status check in ITwin favorites fetch logic --- .../src/containers/ITwinGrid/useITwinFavorites.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts index aa6e75a..01baae3 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts @@ -136,7 +136,7 @@ export const useITwinFavorites = ( `Failed to fetch iTwin favorites from ${url}.\nNo response.` ); } - if (result.status !== 200 && result.status !== 204) { + if (result.status !== 200) { throw new Error( `Failed to fetch iTwin favorites from ${url}.\nStatus: ${result.status}` ); From 0c4c9e16377fc382ad333dce4acb0d8454c6764d Mon Sep 17 00:00:00 2001 From: Omar H Date: Fri, 22 Nov 2024 11:50:39 -0500 Subject: [PATCH 04/13] Handle AbortError in ITwin favorites fetching logic --- .../src/containers/ITwinGrid/useITwinFavorites.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts index 01baae3..83d58c7 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts @@ -158,7 +158,10 @@ export const useITwinFavorites = ( const favorites = await getITwinFavorites(abortSignal); setITwinFavorites(new Set(favorites.map((favorite) => favorite.id))); } catch (error) { - if (error === HOOK_ABORT_ERROR) { + if ( + error === HOOK_ABORT_ERROR || + (error instanceof Error && error.name === "AbortError") + ) { return; } console.error(error); From 4f8a36ef6bd85f7f6a4e8712783997275e1403d5 Mon Sep 17 00:00:00 2001 From: Omar H Date: Fri, 22 Nov 2024 11:52:08 -0500 Subject: [PATCH 05/13] Add label to favorites button in ITwinTile component --- .../imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx index d34c33e..4cbd228 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx @@ -98,6 +98,7 @@ export const ITwinTile = ({ } rightIcon={ { isFavorite ? await removeFromFavorites?.(iTwin.id) From 7b582c09e721f887a22a6a37e6800f80676a8961 Mon Sep 17 00:00:00 2001 From: Omar H Date: Fri, 22 Nov 2024 15:38:59 -0500 Subject: [PATCH 06/13] Add favorites functionality to ITwinGrid table view --- .../src/containers/ITwinGrid/ITwinGrid.tsx | 48 ++++++++++-------- .../ITwinGrid/useITwinTableConfig.tsx | 49 +++++++++++++++---- 2 files changed, 67 insertions(+), 30 deletions(-) diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx index 7db2e5f..b0ef647 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx @@ -33,6 +33,29 @@ export type IndividualITwinStateHook = ( } ) => Partial; +export interface ITwinGridStrings { + /** Displayed for table favorites header. */ + tableColumnFavorites: string; + /** Displayed for table name header. */ + tableColumnName: string; + /** Displayed for table description header. */ + tableColumnDescription: string; + /** Displayed for table lastModified header. */ + tableColumnLastModified: string; + /** Displayed on table while loading data. */ + tableLoadingData: string; + /** Badge text for trial iTwins */ + trialBadge: string; + /** Badge text for inactive iTwins */ + inactiveBadge: string; + /** Displayed after successful fetch, but no iTwins are returned. */ + noITwins: string; + /** Displayed when the component is mounted but the accessToken is empty. */ + noAuthentication: string; + /** Generic message displayed if an error occurs while fetching. */ + error: string; +} + export interface ITwinGridProps { /** Access token that requires the `itwins:read` scope. Provide a function that returns the token to prevent the token from expiring. */ accessToken?: string | (() => Promise) | undefined; @@ -54,26 +77,7 @@ export interface ITwinGridProps { /** Static props to apply over each tile, mainly used for tileProps, overrides ITwinGrid provided values */ tileOverrides?: Partial; /** Strings displayed by the browser */ - stringsOverrides?: { - /** Displayed for table name header. */ - tableColumnName?: string; - /** Displayed for table description header. */ - tableColumnDescription?: string; - /** Displayed for table lastModified header. */ - tableColumnLastModified?: string; - /** Displayed on table while loading data. */ - tableLoadingData?: string; - /** Badge text for trial iTwins */ - trialBadge?: string; - /** Badge text for inactive iTwins */ - inactiveBadge?: string; - /** Displayed after successful fetch, but no iTwins are returned. */ - noITwins?: string; - /** Displayed when the component is mounted but the accessToken is empty. */ - noAuthentication?: string; - /** Generic message displayed if an error occurs while fetching. */ - error?: string; - }; + stringsOverrides?: Partial; /** Object that configures different overrides for the API. * @property `data`: Array of iTwins used in the grid. * @property `serverEnvironmentPrefix`: Either qa or dev. @@ -113,6 +117,7 @@ export const ITwinGrid = ({ const strings = _mergeStrings( { + tableColumnFavorites: "", tableColumnName: "iTwin Number", tableColumnDescription: "iTwin Name", tableColumnLastModified: "Last Modified", @@ -147,6 +152,9 @@ export const ITwinGrid = ({ iTwinActions, onThumbnailClick, strings, + iTwinFavorites, + addITwinToFavorites, + removeITwinFromFavorites, }); const noResultsText = { diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinTableConfig.tsx b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinTableConfig.tsx index 36980c5..1a5a655 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinTableConfig.tsx +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinTableConfig.tsx @@ -2,8 +2,8 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { SvgMore } from "@itwin/itwinui-icons-react"; -import { DropdownMenu } from "@itwin/itwinui-react"; +import { SvgMore, SvgStar, SvgStarHollow } from "@itwin/itwinui-icons-react"; +import { DropdownMenu, IconButton } from "@itwin/itwinui-react"; import React from "react"; import { useMemo } from "react"; import { CellProps } from "react-table"; @@ -13,24 +13,24 @@ import { _buildManagedContextMenuOptions, ContextMenuBuilderItem, } from "../../utils/_buildMenuOptions"; +import { ITwinGridStrings } from "./ITwinGrid"; export interface useITwinTableConfigProps { iTwinActions: ContextMenuBuilderItem[] | undefined; onThumbnailClick: ((iTwin: ITwinFull) => void) | undefined; - strings: { - tableColumnName: string; - tableColumnDescription: string; - tableColumnLastModified: string; - noITwins: string; - noAuthentication: string; - error: string; - }; + strings: ITwinGridStrings; + iTwinFavorites: Set; + addITwinToFavorites: (iTwinId: string) => Promise; + removeITwinFromFavorites: (iTwinId: string) => Promise; } export const useITwinTableConfig = ({ iTwinActions, onThumbnailClick, strings, + iTwinFavorites, + addITwinToFavorites, + removeITwinFromFavorites, }: useITwinTableConfigProps) => { const onRowClick = (_: React.MouseEvent, row: any) => { const iTwin = row.original as ITwinFull; @@ -45,6 +45,31 @@ export const useITwinTableConfig = ({ { Header: "Table", columns: [ + { + id: "Favorite", + Header: strings.tableColumnFavorites, + accessor: "id", + width: 70, + Cell: (props: CellProps) => { + const isFavorite = iTwinFavorites.has(props.value); + return ( + { + e.stopPropagation(); + isFavorite + ? await removeITwinFromFavorites(props.value) + : await addITwinToFavorites(props.value); + }} + > + {isFavorite ? : } + + ); + }, + }, { id: "ITwinNumber", Header: strings.tableColumnName, @@ -106,8 +131,12 @@ export const useITwinTableConfig = ({ }, ], [ + addITwinToFavorites, iTwinActions, + iTwinFavorites, + removeITwinFromFavorites, strings.tableColumnDescription, + strings.tableColumnFavorites, strings.tableColumnLastModified, strings.tableColumnName, ] From f2df04bb28e46a530ce5bef1409a06290b2310c7 Mon Sep 17 00:00:00 2001 From: Omar H Date: Fri, 22 Nov 2024 15:40:17 -0500 Subject: [PATCH 07/13] Fix bug in ITwin favorites and add favorites to ITwinGrid table view --- .../fix-itwin-favorites_2024-11-22-02-01.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/changes/@itwin/imodel-browser-react/fix-itwin-favorites_2024-11-22-02-01.json b/common/changes/@itwin/imodel-browser-react/fix-itwin-favorites_2024-11-22-02-01.json index b8447d2..1368b55 100644 --- a/common/changes/@itwin/imodel-browser-react/fix-itwin-favorites_2024-11-22-02-01.json +++ b/common/changes/@itwin/imodel-browser-react/fix-itwin-favorites_2024-11-22-02-01.json @@ -2,7 +2,7 @@ "changes": [ { "packageName": "@itwin/imodel-browser-react", - "comment": "Fix bug in itwin favorites", + "comment": "Fix bug in itwin favorites and add favorites to iTwinGrid table view", "type": "none" } ], From d9e7e026d6bcdbf65a5c5048c9ca0d96084d1642 Mon Sep 17 00:00:00 2001 From: Omar H Date: Fri, 22 Nov 2024 16:00:25 -0500 Subject: [PATCH 08/13] Add unit tests for useITwinFavorites hook --- .../ITwinGrid/useITwinFavorites.test.ts | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.test.ts diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.test.ts b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.test.ts new file mode 100644 index 0000000..f45fd39 --- /dev/null +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.test.ts @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ + +import { renderHook } from "@testing-library/react-hooks"; + +import { useITwinFavorites } from "./useITwinFavorites"; + +export function mockFetch(data: any, status = 200) { + return jest.fn().mockImplementationOnce(async () => + Promise.resolve({ + status, + ok: true, + json: () => data, + }) + ); +} + +const accessToken = "test-access-token"; + +describe("useITwinFavorites", () => { + // Clear mocks before each test + beforeEach(() => { + jest.clearAllMocks(); + }); + + test("should initialize with empty Set", () => { + const { result } = renderHook(() => useITwinFavorites(accessToken)); + window.fetch = mockFetch({ iTwins: [] }); + + expect(result.current.iTwinFavorites).toBeInstanceOf(Set); + expect(result.current.iTwinFavorites.size).toBe(0); + }); + + test("should fetch favorites on mount", async () => { + const mockFavorites = [ + { id: "1", name: "iTwin1" }, + { id: "2", name: "iTwin2" }, + ]; + + window.fetch = mockFetch({ iTwins: mockFavorites }); + const { result, waitForNextUpdate } = renderHook(() => + useITwinFavorites(accessToken) + ); + + await waitForNextUpdate(); + expect(result.current.iTwinFavorites.has("1")).toBe(true); + expect(result.current.iTwinFavorites.has("2")).toBe(true); + expect(result.current.iTwinFavorites.size).toBe(2); + }); + + test("should handle empty response from API", async () => { + window.fetch = mockFetch({ iTwins: [] }); + + const { result, waitForNextUpdate } = renderHook(() => + useITwinFavorites(accessToken) + ); + + await waitForNextUpdate(); + expect(result.current.iTwinFavorites.size).toBe(0); + }); + + test("should add and remove an iTwin from favorites", async () => { + const { result, waitForNextUpdate } = renderHook(() => + useITwinFavorites(accessToken) + ); + const iTwinId = "test-itwin-id"; + window.fetch = mockFetch({}); + + await result.current.addITwinToFavorites(iTwinId); + await waitForNextUpdate(); + expect(result.current.iTwinFavorites.has(iTwinId)).toBe(true); + + await result.current.removeITwinFromFavorites(iTwinId); + await waitForNextUpdate(); + expect(result.current.iTwinFavorites.has(iTwinId)).toBe(false); + }); +}); From c909c2f08548677aca09719c7fe10f3ff1418b2d Mon Sep 17 00:00:00 2001 From: Omar H Date: Fri, 22 Nov 2024 16:53:33 -0500 Subject: [PATCH 09/13] Refactor useITwinFavorites tests to use act for state updates --- .../ITwinGrid/useITwinFavorites.test.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.test.ts b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.test.ts index f45fd39..d6fae0f 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.test.ts +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { renderHook } from "@testing-library/react-hooks"; +import { act } from "react"; import { useITwinFavorites } from "./useITwinFavorites"; @@ -62,18 +63,18 @@ describe("useITwinFavorites", () => { }); test("should add and remove an iTwin from favorites", async () => { - const { result, waitForNextUpdate } = renderHook(() => - useITwinFavorites(accessToken) - ); + const { result } = renderHook(() => useITwinFavorites(accessToken)); const iTwinId = "test-itwin-id"; - window.fetch = mockFetch({}); - - await result.current.addITwinToFavorites(iTwinId); - await waitForNextUpdate(); + await act(async () => { + window.fetch = mockFetch({}); + await result.current.addITwinToFavorites(iTwinId); + }); expect(result.current.iTwinFavorites.has(iTwinId)).toBe(true); - await result.current.removeITwinFromFavorites(iTwinId); - await waitForNextUpdate(); + await act(async () => { + window.fetch = mockFetch({}); + await result.current.removeITwinFromFavorites(iTwinId); + }); expect(result.current.iTwinFavorites.has(iTwinId)).toBe(false); }); }); From 0d17b387a611b89f9ddb674aeb59807866eb963f Mon Sep 17 00:00:00 2001 From: Omar H Date: Mon, 25 Nov 2024 13:32:15 -0500 Subject: [PATCH 10/13] Fix issue where itwin removed from favorites still showing after switching from favorites tab to other tab, then back to favorites tab quickly. --- .../src/containers/ITwinGrid/ITwinGrid.tsx | 11 +++++++++-- .../src/containers/ITwinGrid/useITwinData.ts | 11 +++++++++++ .../containers/ITwinGrid/useITwinFavorites.ts | 16 +++++++++++++++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx index b0ef647..e412e9f 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx @@ -112,8 +112,13 @@ export const ITwinGrid = ({ postProcessCallback, viewMode, }: ITwinGridProps) => { - const { iTwinFavorites, addITwinToFavorites, removeITwinFromFavorites } = - useITwinFavorites(accessToken, apiOverrides); + const { + iTwinFavorites, + addITwinToFavorites, + removeITwinFromFavorites, + useCache, + setUseCache, + } = useITwinFavorites(accessToken, apiOverrides); const strings = _mergeStrings( { @@ -140,6 +145,8 @@ export const ITwinGrid = ({ accessToken, apiOverrides, filterOptions, + useCache, + setUseCache, }); const iTwins = React.useMemo( diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinData.ts b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinData.ts index 390595a..f06740d 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinData.ts +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinData.ts @@ -20,6 +20,8 @@ export interface ProjectDataHookOptions { accessToken?: string | (() => Promise) | undefined; apiOverrides?: ApiOverrides; filterOptions?: ITwinFilterOptions; + useCache?: boolean; + setUseCache?: (value: boolean) => void; } const PAGE_SIZE = 100; @@ -30,6 +32,8 @@ export const useITwinData = ({ accessToken, apiOverrides, filterOptions, + useCache, + setUseCache, }: ProjectDataHookOptions) => { const data = apiOverrides?.data; const [projects, setProjects] = React.useState([]); @@ -103,6 +107,10 @@ export const useITwinData = ({ const options: RequestInit = { signal: abortController.signal, headers: { + "Cache-Control": + requestType === "favorites" && !useCache + ? "no-cache" + : "max-age=60", Authorization: typeof accessToken === "function" ? await accessToken() @@ -119,6 +127,7 @@ export const useITwinData = ({ throw new Error(errorText); }); setStatus(DataStatus.Complete); + requestType === "favorites" && setUseCache?.(true); if (result.iTwins.length !== PAGE_SIZE) { setMorePages(false); } @@ -148,6 +157,8 @@ export const useITwinData = ({ page, morePages, iTwinSubClass, + useCache, + setUseCache, ]); return { iTwins: filteredProjects, diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts index 83d58c7..fae6378 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts @@ -19,6 +19,8 @@ const HOOK_ABORT_ERROR = * - {Set} iTwinFavorites - A set of iTwin IDs that are marked as favorites. * - {function} addITwinToFavorites - A function to add an iTwin to favorites. * - {function} removeITwinFromFavorites - A function to remove an iTwin from favorites. + * - {boolean} useCache - A boolean indicating whether to use the cache. + * - {function} setUseCache - A function to set the useCache value. */ export const useITwinFavorites = ( accessToken: string | (() => Promise) | undefined, @@ -27,8 +29,11 @@ export const useITwinFavorites = ( iTwinFavorites: Set; addITwinToFavorites: (iTwinId: string) => Promise; removeITwinFromFavorites: (iTwinId: string) => Promise; + useCache: boolean; + setUseCache: (value: boolean) => void; } => { const [iTwinFavorites, setITwinFavorites] = useState(new Set()); + const [useCache, setUseCache] = useState(true); /** * Adds an iTwin to the favorites. @@ -58,6 +63,7 @@ export const useITwinFavorites = ( } setITwinFavorites((prev) => new Set([...prev, iTwinId])); + setUseCache(false); } catch (error) { console.error(error); } @@ -97,6 +103,7 @@ export const useITwinFavorites = ( newFavorites.delete(iTwinId); return newFavorites; }); + setUseCache(false); } catch (error) { console.error(error); } @@ -120,6 +127,7 @@ export const useITwinFavorites = ( )}/itwins/favorites?subClass=Project`; const result = await fetch(url, { headers: { + "Cache-Control": "no-cache", authorization: typeof accessToken === "function" ? await accessToken() @@ -174,7 +182,13 @@ export const useITwinFavorites = ( }; }, [getITwinFavorites]); - return { iTwinFavorites, addITwinToFavorites, removeITwinFromFavorites }; + return { + iTwinFavorites, + addITwinToFavorites, + removeITwinFromFavorites, + useCache, + setUseCache, + }; }; /** Response from https://developer.bentley.com/apis/iTwins/operations/get-my-favorite-itwins/ */ From 28fdb77707ef6eee7914582b2835d29d34222046 Mon Sep 17 00:00:00 2001 From: Omar H Date: Wed, 27 Nov 2024 14:03:34 -0500 Subject: [PATCH 11/13] change the wording for better code readability --- .../src/containers/ITwinGrid/ITwinGrid.tsx | 8 +++--- .../src/containers/ITwinGrid/ITwinTile.tsx | 4 ++- .../src/containers/ITwinGrid/useITwinData.ts | 18 ++++++------- .../containers/ITwinGrid/useITwinFavorites.ts | 26 +++++++++++-------- 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx index e412e9f..128fbb6 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx @@ -116,8 +116,8 @@ export const ITwinGrid = ({ iTwinFavorites, addITwinToFavorites, removeITwinFromFavorites, - useCache, - setUseCache, + shouldRefetchFavorites, + resetShouldRefetchFavorites, } = useITwinFavorites(accessToken, apiOverrides); const strings = _mergeStrings( @@ -145,8 +145,8 @@ export const ITwinGrid = ({ accessToken, apiOverrides, filterOptions, - useCache, - setUseCache, + shouldRefetchFavorites, + resetShouldRefetchFavorites, }); const iTwins = React.useMemo( diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx index 4cbd228..1f1a10b 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx @@ -98,7 +98,9 @@ export const ITwinTile = ({ } rightIcon={ { isFavorite ? await removeFromFavorites?.(iTwin.id) diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinData.ts b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinData.ts index f06740d..f0f6f4f 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinData.ts +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinData.ts @@ -20,8 +20,8 @@ export interface ProjectDataHookOptions { accessToken?: string | (() => Promise) | undefined; apiOverrides?: ApiOverrides; filterOptions?: ITwinFilterOptions; - useCache?: boolean; - setUseCache?: (value: boolean) => void; + shouldRefetchFavorites?: boolean; + resetShouldRefetchFavorites?: () => void; } const PAGE_SIZE = 100; @@ -32,8 +32,8 @@ export const useITwinData = ({ accessToken, apiOverrides, filterOptions, - useCache, - setUseCache, + shouldRefetchFavorites, + resetShouldRefetchFavorites, }: ProjectDataHookOptions) => { const data = apiOverrides?.data; const [projects, setProjects] = React.useState([]); @@ -108,9 +108,9 @@ export const useITwinData = ({ signal: abortController.signal, headers: { "Cache-Control": - requestType === "favorites" && !useCache + requestType === "favorites" && shouldRefetchFavorites ? "no-cache" - : "max-age=60", + : "", Authorization: typeof accessToken === "function" ? await accessToken() @@ -127,7 +127,7 @@ export const useITwinData = ({ throw new Error(errorText); }); setStatus(DataStatus.Complete); - requestType === "favorites" && setUseCache?.(true); + requestType === "favorites" && resetShouldRefetchFavorites?.(); if (result.iTwins.length !== PAGE_SIZE) { setMorePages(false); } @@ -157,8 +157,8 @@ export const useITwinData = ({ page, morePages, iTwinSubClass, - useCache, - setUseCache, + shouldRefetchFavorites, + resetShouldRefetchFavorites, ]); return { iTwins: filteredProjects, diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts index fae6378..3d25b3f 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinFavorites.ts @@ -19,8 +19,8 @@ const HOOK_ABORT_ERROR = * - {Set} iTwinFavorites - A set of iTwin IDs that are marked as favorites. * - {function} addITwinToFavorites - A function to add an iTwin to favorites. * - {function} removeITwinFromFavorites - A function to remove an iTwin from favorites. - * - {boolean} useCache - A boolean indicating whether to use the cache. - * - {function} setUseCache - A function to set the useCache value. + * - {boolean} shouldRefetchFavorites - A boolean indicating whether to refetch favorites when switching to the favorites tab. + * - {function} resetShouldRefetchFavorites - A function to reset shouldRefetchFavorites back to false. */ export const useITwinFavorites = ( accessToken: string | (() => Promise) | undefined, @@ -29,11 +29,11 @@ export const useITwinFavorites = ( iTwinFavorites: Set; addITwinToFavorites: (iTwinId: string) => Promise; removeITwinFromFavorites: (iTwinId: string) => Promise; - useCache: boolean; - setUseCache: (value: boolean) => void; + shouldRefetchFavorites: boolean; + resetShouldRefetchFavorites: () => void; } => { const [iTwinFavorites, setITwinFavorites] = useState(new Set()); - const [useCache, setUseCache] = useState(true); + const [shouldRefetchFavorites, setShouldRefetchFavorites] = useState(false); /** * Adds an iTwin to the favorites. @@ -63,7 +63,7 @@ export const useITwinFavorites = ( } setITwinFavorites((prev) => new Set([...prev, iTwinId])); - setUseCache(false); + setShouldRefetchFavorites(true); } catch (error) { console.error(error); } @@ -103,7 +103,7 @@ export const useITwinFavorites = ( newFavorites.delete(iTwinId); return newFavorites; }); - setUseCache(false); + setShouldRefetchFavorites(true); } catch (error) { console.error(error); } @@ -127,7 +127,7 @@ export const useITwinFavorites = ( )}/itwins/favorites?subClass=Project`; const result = await fetch(url, { headers: { - "Cache-Control": "no-cache", + "Cache-Control": shouldRefetchFavorites ? "no-cache" : "", authorization: typeof accessToken === "function" ? await accessToken() @@ -152,9 +152,13 @@ export const useITwinFavorites = ( const response: ITwinFavoritesResponse = await result.json(); return response.iTwins; }, - [accessToken, apiOverrides] + [accessToken, apiOverrides, shouldRefetchFavorites] ); + const resetShouldRefetchFavorites = useCallback(() => { + setShouldRefetchFavorites(false); + }, []); + useEffect(() => { const controller = new AbortController(); /** @@ -186,8 +190,8 @@ export const useITwinFavorites = ( iTwinFavorites, addITwinToFavorites, removeITwinFromFavorites, - useCache, - setUseCache, + shouldRefetchFavorites, + resetShouldRefetchFavorites, }; }; From 599fd83d08835c74382de74f78b9fbcf26ff3036 Mon Sep 17 00:00:00 2001 From: Omar H Date: Mon, 2 Dec 2024 13:11:17 -0500 Subject: [PATCH 12/13] Add accessible text for favorites actions in ITwinTile component --- .../src/containers/ITwinGrid/ITwinTile.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx index 1f1a10b..2241c71 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx @@ -29,6 +29,12 @@ export interface ITwinTileProps { trialBadge?: string; /** Badge text for inactive iTwins */ inactiveBadge?: string; + /** Accessible text for the hollow star icon to add the iTwin to favorites */ + addToFavorites?: string; + /** Accessible text for the full star icon to remove the iTwin from favorites */ + removeFromFavorites?: string; + /** Accessible text for the thumbnail icon to navigate to the iTwin */ + navigateToITwin?: string; }; /** Tile props that will be applied after normal use. (Will override ITwinTile if used) */ tileProps?: Partial; @@ -57,6 +63,9 @@ export const ITwinTile = ({ { trialBadge: "Trial", inactiveBadge: "Inactive", + addToFavorites: "Add to favorites", + removeFromFavorites: "Remove from favorites", + navigateToITwin: "Navigate to iTwin", }, stringsOverrides ); @@ -89,17 +98,18 @@ export const ITwinTile = ({ } moreOptions={moreOptions} thumbnail={ - onThumbnailClick?.(iTwin)} style={{ cursor: onThumbnailClick ? "pointer" : "auto" }} > - + } rightIcon={ { isFavorite From 8799d18aaa7504b2f61fee5f29443baf7c841640 Mon Sep 17 00:00:00 2001 From: Omar H Date: Tue, 3 Dec 2024 11:54:51 -0500 Subject: [PATCH 13/13] Add accessible text for favorites actions in ITwinGrid component --- .../imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx | 6 ++++++ .../src/containers/ITwinGrid/useITwinTableConfig.tsx | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx index 128fbb6..2510944 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx @@ -54,6 +54,10 @@ export interface ITwinGridStrings { noAuthentication: string; /** Generic message displayed if an error occurs while fetching. */ error: string; + /** Accessible text for the hollow star icon to add the iTwin to favorites */ + addToFavorites: string; + /** Accessible text for the full star icon to remove the iTwin from favorites */ + removeFromFavorites: string; } export interface ITwinGridProps { @@ -132,6 +136,8 @@ export const ITwinGrid = ({ noITwins: "No iTwin found.", noAuthentication: "No access token provided", error: "An error occurred", + addToFavorites: "Add to favorites", + removeFromFavorites: "Remove from favorites", }, stringsOverrides ); diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinTableConfig.tsx b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinTableConfig.tsx index 5b5368e..ad5d10e 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinTableConfig.tsx +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinTableConfig.tsx @@ -55,8 +55,10 @@ export const useITwinTableConfig = ({ return ( { e.stopPropagation();