diff --git a/package.json b/package.json index ddec9224caa..efa25355d65 100644 --- a/package.json +++ b/package.json @@ -152,6 +152,7 @@ "ordinal": "1.0.3", "query-string": "4.3.4", "react": "18.2.0", + "react-error-boundary": "4.0.13", "react-fps": "1.0.6", "react-native": "0.73.9", "react-native-blob-util": "0.19.9", diff --git a/src/app/Components/WorksForYouArtworks.tsx b/src/app/Components/WorksForYouArtworks.tsx index 2ce91b88c81..dc772dcc0c5 100644 --- a/src/app/Components/WorksForYouArtworks.tsx +++ b/src/app/Components/WorksForYouArtworks.tsx @@ -122,8 +122,8 @@ interface WorksForYouArtworksQRProps { onlyAtAuction?: boolean } -export const WorksForYouArtworksQR: React.FC = withSuspense( - ({ version, onlyAtAuction = false, maxWorksPerArtist = 3 }) => { +export const WorksForYouArtworksQR: React.FC = withSuspense({ + Component: ({ version, onlyAtAuction = false, maxWorksPerArtist = 3 }) => { const data = useLazyLoadQuery( newWorksForYouArtworksQuery, { @@ -145,5 +145,6 @@ export const WorksForYouArtworksQR: React.FC = withS return }, - () => -) + LoadingFallback: () => , + ErrorFallback: () => Nothing yet. Please check back later., +}) diff --git a/src/app/Scenes/Activity/ActivityItemScreen.tsx b/src/app/Scenes/Activity/ActivityItemScreen.tsx index 83bd7a920c9..a49a53ff7c5 100644 --- a/src/app/Scenes/Activity/ActivityItemScreen.tsx +++ b/src/app/Scenes/Activity/ActivityItemScreen.tsx @@ -29,8 +29,8 @@ interface ActivityItemScreenQueryRendererProps { } export const ActivityItemScreenQueryRenderer: FC = - withSuspense( - ({ notificationID }) => { + withSuspense({ + Component: ({ notificationID }) => { const data = useLazyLoadQuery(ActivityItemQuery, { internalID: notificationID, }) @@ -65,8 +65,11 @@ export const ActivityItemScreenQueryRenderer: FC - ) + LoadingFallback: () => , + ErrorFallback: (fallbackProps) => { + return + }, + }) const ActivityItemQuery = graphql` query ActivityItemScreenQuery($internalID: String!) { diff --git a/src/app/Scenes/Activity/components/ActivityErrorScreen.tsx b/src/app/Scenes/Activity/components/ActivityErrorScreen.tsx index 6febb88f70f..45110564850 100644 --- a/src/app/Scenes/Activity/components/ActivityErrorScreen.tsx +++ b/src/app/Scenes/Activity/components/ActivityErrorScreen.tsx @@ -1,16 +1,16 @@ -import { Screen } from "@artsy/palette-mobile" -import { LoadFailureView } from "app/Components/LoadFailureView" +import { Screen, SimpleMessage } from "@artsy/palette-mobile" import { goBack } from "app/system/navigation/navigate" interface ActivityErrorScreenProps { headerTitle: string + error?: Error } export const ActivityErrorScreen: React.FC = ({ headerTitle }) => { return ( - + Something went wrong. Please check back later. ) } diff --git a/src/app/Scenes/Artwork/Components/ArtworkError.tsx b/src/app/Scenes/Artwork/Components/ArtworkError.tsx index 6318cb54e03..0ab8ad3b843 100644 --- a/src/app/Scenes/Artwork/Components/ArtworkError.tsx +++ b/src/app/Scenes/Artwork/Components/ArtworkError.tsx @@ -3,6 +3,7 @@ import { Flex, Join, Spacer, Spinner, Text } from "@artsy/palette-mobile" import { ArtworkErrorQuery } from "__generated__/ArtworkErrorQuery.graphql" import { ArtworkErrorRecentlyViewed_homePage$key } from "__generated__/ArtworkErrorRecentlyViewed_homePage.graphql" import { FancyModalHeader } from "app/Components/FancyModal/FancyModalHeader" +import { LoadFailureView } from "app/Components/LoadFailureView" import { ArtworkModuleRailFragmentContainer } from "app/Scenes/Home/Components/ArtworkModuleRail" import { ArtworkRecommendationsRail } from "app/Scenes/Home/Components/ArtworkRecommendationsRail" import { NewWorksForYouRail } from "app/Scenes/Home/Components/NewWorksForYouRail" @@ -75,8 +76,8 @@ export const ArtworkError: React.FC = ({ homePage, me, viewer ) } -export const ArtworkErrorScreen: React.FC<{}> = withSuspense( - () => { +export const ArtworkErrorScreen: React.FC<{}> = withSuspense({ + Component: () => { const worksForYouRecommendationsModel = useExperimentVariant( RECOMMENDATION_MODEL_EXPERIMENT_NAME ) @@ -100,12 +101,23 @@ export const ArtworkErrorScreen: React.FC<{}> = withSuspense( } return }, - () => ( + LoadingFallback: () => ( - ) -) + ), + ErrorFallback: (fallbackProps) => { + return ( + + ) + }, +}) const recentlyViewedFragment = graphql` fragment ArtworkErrorRecentlyViewed_homePage on HomePage { diff --git a/src/app/Scenes/Artwork/Components/BrowseSimilarWorks/BrowseSimilarWorks.tsx b/src/app/Scenes/Artwork/Components/BrowseSimilarWorks/BrowseSimilarWorks.tsx index 61d865f674a..115514bcc0c 100644 --- a/src/app/Scenes/Artwork/Components/BrowseSimilarWorks/BrowseSimilarWorks.tsx +++ b/src/app/Scenes/Artwork/Components/BrowseSimilarWorks/BrowseSimilarWorks.tsx @@ -50,17 +50,26 @@ const BrowseSimilarWorks: React.FC<{ artwork: BrowseSimilarWorks_artwork$key }> const artworkAlert = computeArtworkAlertProps(artwork) + if ( + !artworkAlert || + !artworkAlert.entity || + !artworkAlert.attributes || + !artworkAlert.aggregations + ) { + return null + } + const params: BrowseSimilarWorksProps = { - aggregations: artworkAlert.aggregations!, - attributes: artworkAlert.attributes!, - entity: artworkAlert.entity!, + aggregations: artworkAlert.aggregations, + attributes: artworkAlert.attributes, + entity: artworkAlert.entity, } return } -export const BrowseSimilarWorksQueryRenderer: React.FC<{ artworkID: string }> = withSuspense( - (props) => { +export const BrowseSimilarWorksQueryRenderer: React.FC<{ artworkID: string }> = withSuspense({ + Component: (props) => { const data = useLazyLoadQuery(SimilarWorksQuery, { artworkID: props.artworkID, }) @@ -69,10 +78,13 @@ export const BrowseSimilarWorksQueryRenderer: React.FC<{ artworkID: string }> = return } - return + return + }, + LoadingFallback: BrowseSimilarWorksPlaceholder, + ErrorFallback: () => { + return }, - BrowseSimilarWorksPlaceholder -) +}) const similarWorksFragment = graphql` fragment BrowseSimilarWorks_artwork on Artwork { diff --git a/src/app/Scenes/Artwork/Components/BrowseSimilarWorks/BrowseSimilarWorksContent.tsx b/src/app/Scenes/Artwork/Components/BrowseSimilarWorks/BrowseSimilarWorksContent.tsx index 710e079a681..04fe786d643 100644 --- a/src/app/Scenes/Artwork/Components/BrowseSimilarWorks/BrowseSimilarWorksContent.tsx +++ b/src/app/Scenes/Artwork/Components/BrowseSimilarWorks/BrowseSimilarWorksContent.tsx @@ -87,8 +87,8 @@ const SimilarArtworksPlaceholder: React.FC = () => { return } -const SimilarArtworksContainer: React.FC<{ attributes: SearchCriteriaAttributes }> = withSuspense( - ({ attributes }) => { +const SimilarArtworksContainer: React.FC<{ attributes: SearchCriteriaAttributes }> = withSuspense({ + Component: ({ attributes }) => { const screen = useScreenDimensions() const { space } = useTheme() @@ -125,5 +125,12 @@ const SimilarArtworksContainer: React.FC<{ attributes: SearchCriteriaAttributes ) }, - SimilarArtworksPlaceholder -) + LoadingFallback: SimilarArtworksPlaceholder, + ErrorFallback: () => { + return ( + + There aren’t any works available that meet the criteria at this time. + + ) + }, +}) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionActivity.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionActivity.tsx index d999bb344d0..1325bce1e96 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionActivity.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionActivity.tsx @@ -19,7 +19,7 @@ import { import { useHomeViewTracking } from "app/Scenes/HomeView/useHomeViewTracking" import { navigate } from "app/system/navigation/navigate" import { extractNodes } from "app/utils/extractNodes" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { useMemoizedRandom } from "app/utils/placeholders" import { times } from "lodash" import { FlatList } from "react-native" @@ -192,8 +192,8 @@ const HomeViewSectionActivityPlaceholder: React.FC = (flexProps) => { ) } -export const HomeViewSectionActivityQueryRenderer: React.FC = withSuspense( - ({ sectionID, index, ...flexProps }) => { +export const HomeViewSectionActivityQueryRenderer: React.FC = withSuspense({ + Component: ({ sectionID, index, ...flexProps }) => { const data = useLazyLoadQuery(homeViewSectionActivityQuery, { id: sectionID, }) @@ -204,5 +204,6 @@ export const HomeViewSectionActivityQueryRenderer: React.FC return }, - HomeViewSectionActivityPlaceholder -) + LoadingFallback: HomeViewSectionActivityPlaceholder, + ErrorFallback: NoFallback, +}) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionArticles.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionArticles.tsx index 0805e41033f..e62c2b6f48f 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionArticles.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionArticles.tsx @@ -17,7 +17,7 @@ import { HOME_VIEW_SECTIONS_SEPARATOR_HEIGHT } from "app/Scenes/HomeView/HomeVie import { SectionSharedProps } from "app/Scenes/HomeView/Sections/Section" import { useHomeViewTracking } from "app/Scenes/HomeView/useHomeViewTracking" import { navigate } from "app/system/navigation/navigate" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { useMemoizedRandom } from "app/utils/placeholders" import { times } from "lodash" import { graphql, useFragment, useLazyLoadQuery } from "react-relay" @@ -147,8 +147,8 @@ const HomeViewSectionArticlesPlaceholder: React.FC = (flexProps) => { ) } -export const HomeViewSectionArticlesQueryRenderer: React.FC = withSuspense( - ({ sectionID, index, ...flexProps }) => { +export const HomeViewSectionArticlesQueryRenderer: React.FC = withSuspense({ + Component: ({ sectionID, index, ...flexProps }) => { const data = useLazyLoadQuery( homeViewSectionArticlesQuery, { @@ -167,5 +167,6 @@ export const HomeViewSectionArticlesQueryRenderer: React.FC return }, - HomeViewSectionArticlesPlaceholder -) + LoadingFallback: HomeViewSectionArticlesPlaceholder, + ErrorFallback: NoFallback, +}) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionArticlesCards.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionArticlesCards.tsx index 43a58265f45..ee0b2338c03 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionArticlesCards.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionArticlesCards.tsx @@ -20,7 +20,7 @@ import { SectionSharedProps } from "app/Scenes/HomeView/Sections/Section" import { useHomeViewTracking } from "app/Scenes/HomeView/useHomeViewTracking" import { navigate } from "app/system/navigation/navigate" import { extractNodes } from "app/utils/extractNodes" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { ExtractNodeType } from "app/utils/relayHelpers" import { times } from "lodash" import { graphql, useFragment, useLazyLoadQuery } from "react-relay" @@ -181,26 +181,33 @@ const homeViewSectionArticlesCardsQuery = graphql` ` export const HomeViewSectionArticlesCardsQueryRenderer: React.FC = withSuspense( - ({ sectionID, index, ...flexProps }) => { - const data = useLazyLoadQuery( - homeViewSectionArticlesCardsQuery, - { - id: sectionID, - }, - { - networkCacheConfig: { - force: false, + { + Component: ({ sectionID, index, ...flexProps }) => { + const data = useLazyLoadQuery( + homeViewSectionArticlesCardsQuery, + { + id: sectionID, }, - } - ) + { + networkCacheConfig: { + force: false, + }, + } + ) - if (!data.homeView.section) { - return null - } + if (!data.homeView.section) { + return null + } - return ( - - ) - }, - HomeViewSectionArticlesCardsPlaceholder + return ( + + ) + }, + LoadingFallback: HomeViewSectionArticlesCardsPlaceholder, + ErrorFallback: NoFallback, + } ) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionArtists.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionArtists.tsx index 9275e1804ef..439ab206d7b 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionArtists.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionArtists.tsx @@ -28,7 +28,7 @@ import { import { useHomeViewTracking } from "app/Scenes/HomeView/useHomeViewTracking" import { navigate } from "app/system/navigation/navigate" import { extractNodes } from "app/utils/extractNodes" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { useMemoizedRandom } from "app/utils/placeholders" import { ExtractNodeType } from "app/utils/relayHelpers" import { times } from "lodash" @@ -252,8 +252,8 @@ const HomeViewSectionArtistsPlaceholder: React.FC = (flexProps) => { ) } -export const HomeViewSectionArtistsQueryRenderer: React.FC = withSuspense( - ({ sectionID, index, ...flexProps }) => { +export const HomeViewSectionArtistsQueryRenderer: React.FC = withSuspense({ + Component: ({ sectionID, index, ...flexProps }) => { const data = useLazyLoadQuery(homeViewSectionArtistsQuery, { id: sectionID, }) @@ -270,5 +270,6 @@ export const HomeViewSectionArtistsQueryRenderer: React.FC = /> ) }, - HomeViewSectionArtistsPlaceholder -) + LoadingFallback: HomeViewSectionArtistsPlaceholder, + ErrorFallback: NoFallback, +}) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionArtworks.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionArtworks.tsx index bd338540448..1a1c7d198f4 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionArtworks.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionArtworks.tsx @@ -24,7 +24,7 @@ import { useHomeViewTracking } from "app/Scenes/HomeView/useHomeViewTracking" import { navigate } from "app/system/navigation/navigate" import { extractNodes } from "app/utils/extractNodes" import { useFeatureFlag } from "app/utils/hooks/useFeatureFlag" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { useMemoizedRandom } from "app/utils/placeholders" import { times } from "lodash" import { graphql, useFragment, useLazyLoadQuery } from "react-relay" @@ -195,8 +195,8 @@ const HomeViewSectionArtworksPlaceholder: React.FC = (flexProps) => { ) } -export const HomeViewSectionArtworksQueryRenderer: React.FC = withSuspense( - ({ sectionID, index, ...flexProps }) => { +export const HomeViewSectionArtworksQueryRenderer: React.FC = withSuspense({ + Component: ({ sectionID, index, ...flexProps }) => { const data = useLazyLoadQuery(homeViewSectionArtworksQuery, { id: sectionID, }) @@ -207,5 +207,6 @@ export const HomeViewSectionArtworksQueryRenderer: React.FC return }, - HomeViewSectionArtworksPlaceholder -) + LoadingFallback: HomeViewSectionArtworksPlaceholder, + ErrorFallback: NoFallback, +}) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionAuctionResults.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionAuctionResults.tsx index be0ae579f19..530406db56d 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionAuctionResults.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionAuctionResults.tsx @@ -26,7 +26,7 @@ import { import { useHomeViewTracking } from "app/Scenes/HomeView/useHomeViewTracking" import { navigate } from "app/system/navigation/navigate" import { extractNodes } from "app/utils/extractNodes" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { useMemoizedRandom } from "app/utils/placeholders" import { times } from "lodash" import { Dimensions, FlatList } from "react-native" @@ -211,19 +211,27 @@ const homeViewSectionAuctionResultsQuery = graphql` ` export const HomeViewSectionAuctionResultsQueryRenderer: React.FC = - withSuspense(({ sectionID, index, ...flexProps }) => { - const data = useLazyLoadQuery( - homeViewSectionAuctionResultsQuery, - { - id: sectionID, - } - ) + withSuspense({ + Component: ({ sectionID, index, ...flexProps }) => { + const data = useLazyLoadQuery( + homeViewSectionAuctionResultsQuery, + { + id: sectionID, + } + ) - if (!data.homeView.section) { - return null - } + if (!data.homeView.section) { + return null + } - return ( - - ) - }, HomeViewSectionAuctionResultsPlaceholder) + return ( + + ) + }, + LoadingFallback: HomeViewSectionAuctionResultsPlaceholder, + ErrorFallback: NoFallback, + }) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionDiscoverMarketingCollections.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionDiscoverMarketingCollections.tsx index efc3c9e8b2c..2daa4b2ec01 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionDiscoverMarketingCollections.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionDiscoverMarketingCollections.tsx @@ -6,7 +6,7 @@ import { SectionTitle } from "app/Components/SectionTitle" import { SectionSharedProps } from "app/Scenes/HomeView/Sections/Section" import { navigate } from "app/system/navigation/navigate" import { extractNodes } from "app/utils/extractNodes" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { times } from "lodash" import { FlatList, ScrollView } from "react-native" import { isTablet } from "react-native-device-info" @@ -133,23 +133,27 @@ const homeViewSectionDiscoverMarketingCollectionsQuery = graphql` ` export const HomeViewSectionDiscoverMarketingCollectionsQueryRenderer: React.FC = - withSuspense(({ sectionID, index, ...flexProps }) => { - const data = useLazyLoadQuery( - homeViewSectionDiscoverMarketingCollectionsQuery, - { - id: sectionID, - } - ) + withSuspense({ + Component: ({ sectionID, index, ...flexProps }) => { + const data = useLazyLoadQuery( + homeViewSectionDiscoverMarketingCollectionsQuery, + { + id: sectionID, + } + ) - if (!data.homeView.section) { - return null - } + if (!data.homeView.section) { + return null + } - return ( - - ) - }, HomeViewSectionDiscoverMarketingCollectionsPlaceholder) + return ( + + ) + }, + LoadingFallback: HomeViewSectionDiscoverMarketingCollectionsPlaceholder, + ErrorFallback: NoFallback, + }) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionFairs.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionFairs.tsx index 0dce5f08a51..7f758ca4175 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionFairs.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionFairs.tsx @@ -16,7 +16,7 @@ import { import { useHomeViewTracking } from "app/Scenes/HomeView/useHomeViewTracking" import { navigate } from "app/system/navigation/navigate" import { extractNodes } from "app/utils/extractNodes" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { useMemoizedRandom } from "app/utils/placeholders" import { times } from "lodash" import { graphql, useFragment, useLazyLoadQuery } from "react-relay" @@ -189,8 +189,8 @@ const homeViewSectionFairsQuery = graphql` } ` -export const HomeViewSectionFairsQueryRenderer: React.FC = withSuspense( - ({ sectionID, index, ...flexProps }) => { +export const HomeViewSectionFairsQueryRenderer: React.FC = withSuspense({ + Component: ({ sectionID, index, ...flexProps }) => { const data = useLazyLoadQuery( homeViewSectionFairsQuery, { @@ -209,5 +209,6 @@ export const HomeViewSectionFairsQueryRenderer: React.FC = w return }, - HomeViewSectionFairsPlaceholder -) + LoadingFallback: HomeViewSectionFairsPlaceholder, + ErrorFallback: NoFallback, +}) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionFeaturedCollection.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionFeaturedCollection.tsx index 2f7f5d7aad4..7213a256f9a 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionFeaturedCollection.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionFeaturedCollection.tsx @@ -25,7 +25,7 @@ import { useHomeViewTracking } from "app/Scenes/HomeView/useHomeViewTracking" import { navigate } from "app/system/navigation/navigate" import { extractNodes } from "app/utils/extractNodes" import { useFeatureFlag } from "app/utils/hooks/useFeatureFlag" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { TouchableOpacity, useWindowDimensions } from "react-native" import { graphql, useFragment, useLazyLoadQuery } from "react-relay" @@ -217,28 +217,32 @@ const homeViewSectionFeaturedCollectionQuery = graphql` ` export const HomeViewSectionFeaturedCollectionQueryRenderer: React.FC = - withSuspense(({ sectionID, index, ...flexProps }) => { - const data = useLazyLoadQuery( - homeViewSectionFeaturedCollectionQuery, - { - id: sectionID, - }, - { - networkCacheConfig: { - force: false, + withSuspense({ + Component: ({ sectionID, index, ...flexProps }) => { + const data = useLazyLoadQuery( + homeViewSectionFeaturedCollectionQuery, + { + id: sectionID, }, - } - ) + { + networkCacheConfig: { + force: false, + }, + } + ) - if (!data.homeView.section) { - return null - } + if (!data.homeView.section) { + return null + } - return ( - - ) - }, HomeViewSectionFeaturedCollectionPlaceholder) + return ( + + ) + }, + LoadingFallback: HomeViewSectionFeaturedCollectionPlaceholder, + ErrorFallback: NoFallback, + }) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionGalleries.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionGalleries.tsx index bb8ae59c7de..de9ed90c287 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionGalleries.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionGalleries.tsx @@ -15,7 +15,7 @@ import { HomeViewSectionSentinel } from "app/Scenes/HomeView/Components/HomeView import { SectionSharedProps } from "app/Scenes/HomeView/Sections/Section" import { useHomeViewTracking } from "app/Scenes/HomeView/useHomeViewTracking" import { navigate } from "app/system/navigation/navigate" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { isTablet } from "react-native-device-info" import FastImage from "react-native-fast-image" import LinearGradient from "react-native-linear-gradient" @@ -156,8 +156,8 @@ const homeViewSectionGalleriesQuery = graphql` } ` -export const HomeViewSectionGalleriesQueryRenderer: React.FC = withSuspense( - ({ sectionID, index, ...flexProps }) => { +export const HomeViewSectionGalleriesQueryRenderer: React.FC = withSuspense({ + Component: ({ sectionID, index, ...flexProps }) => { const data = useLazyLoadQuery( homeViewSectionGalleriesQuery, { @@ -176,5 +176,6 @@ export const HomeViewSectionGalleriesQueryRenderer: React.FC return }, - HomeViewSectionGalleriesPlaceholder -) + LoadingFallback: HomeViewSectionGalleriesPlaceholder, + ErrorFallback: NoFallback, +}) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionHeroUnits.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionHeroUnits.tsx index f87b0b5c6f4..6f16eface8e 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionHeroUnits.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionHeroUnits.tsx @@ -13,7 +13,7 @@ import { import { useHomeViewTracking } from "app/Scenes/HomeView/useHomeViewTracking" import { extractNodes } from "app/utils/extractNodes" import { useScreenDimensions } from "app/utils/hooks" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { isNumber } from "lodash" import { useRef, useState } from "react" import { FlatList, ViewabilityConfig, ViewToken } from "react-native" @@ -141,8 +141,8 @@ const homeViewSectionHeroUnitsQuery = graphql` } ` -export const HomeViewSectionHeroUnitsQueryRenderer: React.FC = withSuspense( - ({ sectionID, index, ...flexProps }) => { +export const HomeViewSectionHeroUnitsQueryRenderer: React.FC = withSuspense({ + Component: ({ sectionID, index, ...flexProps }) => { const data = useLazyLoadQuery( homeViewSectionHeroUnitsQuery, { @@ -161,5 +161,6 @@ export const HomeViewSectionHeroUnitsQueryRenderer: React.FC return }, - HomeViewSectionHeroUnitsPlaceholder -) + LoadingFallback: HomeViewSectionHeroUnitsPlaceholder, + ErrorFallback: NoFallback, +}) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionMarketingCollections.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionMarketingCollections.tsx index 8ce0ba5c914..28cfc6e9148 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionMarketingCollections.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionMarketingCollections.tsx @@ -34,7 +34,7 @@ import { import { useHomeViewTracking } from "app/Scenes/HomeView/useHomeViewTracking" import { navigate } from "app/system/navigation/navigate" import { extractNodes } from "app/utils/extractNodes" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { useMemoizedRandom } from "app/utils/placeholders" import { ExtractNodeType } from "app/utils/relayHelpers" import { times } from "lodash" @@ -229,28 +229,32 @@ const homeViewSectionMarketingCollectionsQuery = graphql` ` export const HomeViewSectionMarketingCollectionsQueryRenderer: React.FC = - withSuspense(({ sectionID, index, ...flexProps }) => { - const data = useLazyLoadQuery( - homeViewSectionMarketingCollectionsQuery, - { - id: sectionID, - }, - { - networkCacheConfig: { - force: false, + withSuspense({ + Component: ({ sectionID, index, ...flexProps }) => { + const data = useLazyLoadQuery( + homeViewSectionMarketingCollectionsQuery, + { + id: sectionID, }, - } - ) + { + networkCacheConfig: { + force: false, + }, + } + ) - if (!data.homeView.section) { - return null - } + if (!data.homeView.section) { + return null + } - return ( - - ) - }, HomeViewSectionMarketingCollectionsPlaceholder) + return ( + + ) + }, + LoadingFallback: HomeViewSectionMarketingCollectionsPlaceholder, + ErrorFallback: NoFallback, + }) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionSales.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionSales.tsx index 66ae52c9552..3b3c9a2d4ce 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionSales.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionSales.tsx @@ -25,7 +25,7 @@ import { import { useHomeViewTracking } from "app/Scenes/HomeView/useHomeViewTracking" import { navigate } from "app/system/navigation/navigate" import { extractNodes } from "app/utils/extractNodes" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { useMemoizedRandom } from "app/utils/placeholders" import { times } from "lodash" import { useRef } from "react" @@ -211,8 +211,8 @@ const homeViewSectionSalesQuery = graphql` } ` -export const HomeViewSectionSalesQueryRenderer: React.FC = withSuspense( - ({ sectionID, index, ...flexProps }) => { +export const HomeViewSectionSalesQueryRenderer: React.FC = withSuspense({ + Component: ({ sectionID, index, ...flexProps }) => { const data = useLazyLoadQuery( homeViewSectionSalesQuery, { @@ -231,5 +231,6 @@ export const HomeViewSectionSalesQueryRenderer: React.FC = w return }, - HomeViewSectionSalesPlaceholder -) + LoadingFallback: HomeViewSectionSalesPlaceholder, + ErrorFallback: NoFallback, +}) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionShows.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionShows.tsx index 7ab811466bf..cb21fd23cc9 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionShows.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionShows.tsx @@ -7,7 +7,7 @@ import { HomeViewSectionSentinel } from "app/Scenes/HomeView/Components/HomeView import { SectionSharedProps } from "app/Scenes/HomeView/Sections/Section" import { useHomeViewTracking } from "app/Scenes/HomeView/useHomeViewTracking" import { useFeatureFlag } from "app/utils/hooks/useFeatureFlag" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { graphql, useFragment, useLazyLoadQuery } from "react-relay" interface HomeViewSectionShowsProps { @@ -78,8 +78,8 @@ const homeViewSectionShowsQuery = graphql` } ` -export const HomeViewSectionShowsQueryRenderer: React.FC = withSuspense( - ({ sectionID, index, ...flexProps }) => { +export const HomeViewSectionShowsQueryRenderer: React.FC = withSuspense({ + Component: ({ sectionID, index, ...flexProps }) => { const data = useLazyLoadQuery( homeViewSectionShowsQuery, { @@ -98,5 +98,6 @@ export const HomeViewSectionShowsQueryRenderer: React.FC = w return }, - HomeViewSectionShowsPlaceholder -) + LoadingFallback: HomeViewSectionShowsPlaceholder, + ErrorFallback: NoFallback, +}) diff --git a/src/app/Scenes/HomeView/Sections/HomeViewSectionViewingRooms.tsx b/src/app/Scenes/HomeView/Sections/HomeViewSectionViewingRooms.tsx index 37ee0c33f04..1362bafb5ed 100644 --- a/src/app/Scenes/HomeView/Sections/HomeViewSectionViewingRooms.tsx +++ b/src/app/Scenes/HomeView/Sections/HomeViewSectionViewingRooms.tsx @@ -20,7 +20,7 @@ import { ViewingRoomsRailPlaceholder, } from "app/Scenes/ViewingRoom/Components/ViewingRoomsHomeRail" import { navigate } from "app/system/navigation/navigate" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { useMemoizedRandom } from "app/utils/placeholders" import { times } from "lodash" import { Suspense } from "react" @@ -145,8 +145,8 @@ const homeViewSectionViewingRoomsQuery = graphql` } ` -export const HomeViewSectionViewingRoomsQueryRenderer: React.FC = withSuspense( - ({ sectionID, index, ...flexProps }) => { +export const HomeViewSectionViewingRoomsQueryRenderer: React.FC = withSuspense({ + Component: ({ sectionID, index, ...flexProps }) => { const data = useLazyLoadQuery( homeViewSectionViewingRoomsQuery, { @@ -167,5 +167,6 @@ export const HomeViewSectionViewingRoomsQueryRenderer: React.FC ) }, - HomeViewSectionArtworksPlaceholder -) + LoadingFallback: HomeViewSectionArtworksPlaceholder, + ErrorFallback: NoFallback, +}) diff --git a/src/app/Scenes/HomeViewSectionScreen/HomeViewSectionScreen.tsx b/src/app/Scenes/HomeViewSectionScreen/HomeViewSectionScreen.tsx index c8912e0d1d4..e8cfbe1ca4b 100644 --- a/src/app/Scenes/HomeViewSectionScreen/HomeViewSectionScreen.tsx +++ b/src/app/Scenes/HomeViewSectionScreen/HomeViewSectionScreen.tsx @@ -5,6 +5,7 @@ import { HomeViewSectionScreenQuery, HomeViewSectionScreenQuery$data, } from "__generated__/HomeViewSectionScreenQuery.graphql" +import { LoadFailureView } from "app/Components/LoadFailureView" import { useHomeViewTracking } from "app/Scenes/HomeView/useHomeViewTracking" import { HomeViewSectionScreenContent } from "app/Scenes/HomeViewSectionScreen/HomeViewSectionScreenContent" import { HomeViewSectionScreenPlaceholder } from "app/Scenes/HomeViewSectionScreen/HomeViewSectionScreenPlaceholder" @@ -64,8 +65,8 @@ interface HomeViewSectionScreenQueryRendererProps { sectionType: string } -export const HomeViewSectionScreenQueryRenderer = withSuspense( - (props: HomeViewSectionScreenQueryRendererProps) => { +export const HomeViewSectionScreenQueryRenderer = withSuspense({ + Component: (props: HomeViewSectionScreenQueryRendererProps) => { const data = useLazyLoadQuery(HOME_SECTION_SCREEN_QUERY, { id: props.sectionID, }) @@ -76,5 +77,13 @@ export const HomeViewSectionScreenQueryRenderer = withSuspense( return }, - HomeViewSectionScreenPlaceholder -) + LoadingFallback: HomeViewSectionScreenPlaceholder, + ErrorFallback: (fallbackProps) => ( + + ), +}) diff --git a/src/app/Scenes/MyCollection/Screens/ArtworkForm/Components/MyCollectionArtworkFormDeleteArtworkModal.tsx b/src/app/Scenes/MyCollection/Screens/ArtworkForm/Components/MyCollectionArtworkFormDeleteArtworkModal.tsx index 2c5ff92057b..36efd702829 100644 --- a/src/app/Scenes/MyCollection/Screens/ArtworkForm/Components/MyCollectionArtworkFormDeleteArtworkModal.tsx +++ b/src/app/Scenes/MyCollection/Screens/ArtworkForm/Components/MyCollectionArtworkFormDeleteArtworkModal.tsx @@ -8,7 +8,7 @@ import { Text, } from "@artsy/palette-mobile" import { MyCollectionArtworkFormDeleteArtworkModalQuery } from "__generated__/MyCollectionArtworkFormDeleteArtworkModalQuery.graphql" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, SpinnerFallback, withSuspense } from "app/utils/hooks/withSuspense" import { useState } from "react" import { Modal } from "react-native" import { graphql, useLazyLoadQuery } from "react-relay" @@ -34,73 +34,77 @@ const myCollectionArtworkFormDeleteArtworkModalQuery = graphql` ` export const MyCollectionArtworkFormDeleteArtworkModal: React.FC = - withSuspense(({ visible, hideModal, deleteArtwork, artistID }) => { - const data = useLazyLoadQuery( - myCollectionArtworkFormDeleteArtworkModalQuery, - { - artistID: artistID, - } - ) + withSuspense({ + Component: ({ visible, hideModal, deleteArtwork, artistID }) => { + const data = useLazyLoadQuery( + myCollectionArtworkFormDeleteArtworkModalQuery, + { + artistID: artistID, + } + ) - const [shouldDeleteArtist, setShouldDeleteArtist] = useState(false) + const [shouldDeleteArtist, setShouldDeleteArtist] = useState(false) - // If the query fails, disable artist deletion. It can be done separetely from the home screen - const isLastArtwork = (data.me?.myCollectionConnection?.totalCount || 10) === 1 + // If the query fails, disable artist deletion. It can be done separetely from the home screen + const isLastArtwork = (data.me?.myCollectionConnection?.totalCount || 10) === 1 - return ( - - - Delete this artwork? - - This artwork will be removed from My Collection. - - setShouldDeleteArtist(!shouldDeleteArtist)} - disabled={!isLastArtwork} - justifyContent="center" - flex={undefined} - checked={shouldDeleteArtist} - accessibilityLabel={`Remove ${data.artist?.name} from the artists you collect`} - accessibilityState={{ checked: shouldDeleteArtist }} - accessibilityHint="If you remove the artist from your collection, they will no longer appear in your profile as a collected artist." - > - - Remove {data.artist?.name} from the artists you collect - - + return ( + + + Delete this artwork? + + This artwork will be removed from My Collection. + + setShouldDeleteArtist(!shouldDeleteArtist)} + disabled={!isLastArtwork} + justifyContent="center" + flex={undefined} + checked={shouldDeleteArtist} + accessibilityLabel={`Remove ${data.artist?.name} from the artists you collect`} + accessibilityState={{ checked: shouldDeleteArtist }} + accessibilityHint="If you remove the artist from your collection, they will no longer appear in your profile as a collected artist." + > + + Remove {data.artist?.name} from the artists you collect + + - {!isLastArtwork && ( - <> - - } - /> - - )} - - - - - - - ) + {!isLastArtwork && ( + <> + + } + /> + + )} + + + + + + + ) + }, + LoadingFallback: SpinnerFallback, + ErrorFallback: NoFallback, }) diff --git a/src/app/Scenes/MyCollection/Screens/ArtworkForm/Screens/MyCollectionArtworkEdit.tsx b/src/app/Scenes/MyCollection/Screens/ArtworkForm/Screens/MyCollectionArtworkEdit.tsx index c7a98ae4c66..ee14787cc8c 100644 --- a/src/app/Scenes/MyCollection/Screens/ArtworkForm/Screens/MyCollectionArtworkEdit.tsx +++ b/src/app/Scenes/MyCollection/Screens/ArtworkForm/Screens/MyCollectionArtworkEdit.tsx @@ -1,6 +1,7 @@ import { MyCollectionArtworkEditQuery } from "__generated__/MyCollectionArtworkEditQuery.graphql" +import { LoadFailureView } from "app/Components/LoadFailureView" import { MyCollectionArtworkFormScreen } from "app/Scenes/MyCollection/Screens/ArtworkForm/MyCollectionArtworkForm" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { SpinnerFallback, withSuspense } from "app/utils/hooks/withSuspense" import { graphql, useLazyLoadQuery } from "react-relay" interface MyCollectionArtworkEditProps { @@ -54,12 +55,24 @@ export const myCollectionArtworkEditQuery = graphql` } ` -export const MyCollectionArtworkEditQueryRenderer = withSuspense( - ({ artworkID }: MyCollectionArtworkEditProps) => { +export const MyCollectionArtworkEditQueryRenderer = withSuspense({ + Component: ({ artworkID }: MyCollectionArtworkEditProps) => { const data = useLazyLoadQuery(myCollectionArtworkEditQuery, { artworkId: artworkID, }) return - } -) + }, + LoadingFallback: SpinnerFallback, + ErrorFallback: (fallbackProps) => { + return ( + + ) + }, +}) diff --git a/src/app/Scenes/MyCollection/Screens/CollectedArtistsPrivacy/MyCollectionCollectedArtistsPrivacy.tsx b/src/app/Scenes/MyCollection/Screens/CollectedArtistsPrivacy/MyCollectionCollectedArtistsPrivacy.tsx index d7cf5b5e924..f2b3bb62c14 100644 --- a/src/app/Scenes/MyCollection/Screens/CollectedArtistsPrivacy/MyCollectionCollectedArtistsPrivacy.tsx +++ b/src/app/Scenes/MyCollection/Screens/CollectedArtistsPrivacy/MyCollectionCollectedArtistsPrivacy.tsx @@ -4,6 +4,7 @@ import { MyCollectionCollectedArtistsPrivacyQuery$data, } from "__generated__/MyCollectionCollectedArtistsPrivacyQuery.graphql" import { ArtistListItemPlaceholder } from "app/Components/ArtistListItem" +import { LoadFailureView } from "app/Components/LoadFailureView" import { useToast } from "app/Components/Toast/toastHook" import { HeaderComponent, @@ -28,10 +29,14 @@ interface MyCollectionCollectedArtistsPrivacyProps { export const MyCollectionCollectedArtistsPrivacy: React.FC< MyCollectionCollectedArtistsPrivacyProps > = ({ me }) => { + if (!me) { + return null + } + return ( - + @@ -89,19 +94,39 @@ const ShareSettingsScreenPlaceholder: React.FC<{}> = () => ( ) -export const MyCollectionCollectedArtistsPrivacyQueryRenderer: React.FC<{}> = withSuspense(() => { - const data = useLazyLoadQuery( - myCollectionCollectedArtistsPrivacyQuery, - {}, - { fetchPolicy: "store-and-network" } - ) +export const MyCollectionCollectedArtistsPrivacyQueryRenderer: React.FC<{}> = withSuspense({ + Component: () => { + const data = useLazyLoadQuery( + myCollectionCollectedArtistsPrivacyQuery, + {}, + { fetchPolicy: "store-and-network" } + ) - return ( - - - - ) -}, ShareSettingsScreenPlaceholder) + console.warn("MyCollectionArtistsPrivacyQueryRenderer") + + if (!data.me) { + return null + } + + return ( + + + + ) + }, + LoadingFallback: ShareSettingsScreenPlaceholder, + ErrorFallback: (fallbackProps) => { + return ( + + ) + }, +}) const myCollectionCollectedArtistsPrivacyQuery = graphql` query MyCollectionCollectedArtistsPrivacyQuery { diff --git a/src/app/Scenes/MyProfile/MyProfileHeader.tsx b/src/app/Scenes/MyProfile/MyProfileHeader.tsx index 91950089e2e..a55e8d8bfd6 100644 --- a/src/app/Scenes/MyProfile/MyProfileHeader.tsx +++ b/src/app/Scenes/MyProfile/MyProfileHeader.tsx @@ -19,6 +19,7 @@ import { SkeletonText, Button, Image, + SimpleMessage, } from "@artsy/palette-mobile" import { MyProfileHeaderQuery } from "__generated__/MyProfileHeaderQuery.graphql" import { MyProfileHeader_me$key } from "__generated__/MyProfileHeader_me.graphql" @@ -286,21 +287,30 @@ const myProfileHeaderQuery = graphql` } ` -export const MyProfileHeaderQueryRenderer = withSuspense((props) => { - const data = useLazyLoadQuery( - myProfileHeaderQuery, - {}, - { - fetchPolicy: "network-only", - networkCacheConfig: { - force: true, - }, +export const MyProfileHeaderQueryRenderer = withSuspense({ + Component: (props) => { + const data = useLazyLoadQuery( + myProfileHeaderQuery, + {}, + { + fetchPolicy: "network-only", + networkCacheConfig: { + force: true, + }, + } + ) + + if (!data.me) { + return Failed to load profile. Please check back later. } - ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return -}, MyProfileHeaderPlaceholder) + return + }, + LoadingFallback: MyProfileHeaderPlaceholder, + ErrorFallback: () => { + return Failed to load profile. Please check back later. + }, +}) const tracks = { tappedCompleteMyProfile: ({ id }: { id: string }): TappedCompleteYourProfile => ({ diff --git a/src/app/Scenes/NewWorksFromGalleriesYouFollow/Components/NewWorksFromGalleriesYouFollow.tsx b/src/app/Scenes/NewWorksFromGalleriesYouFollow/Components/NewWorksFromGalleriesYouFollow.tsx index 8e352e1f43e..7b7cb25f44c 100644 --- a/src/app/Scenes/NewWorksFromGalleriesYouFollow/Components/NewWorksFromGalleriesYouFollow.tsx +++ b/src/app/Scenes/NewWorksFromGalleriesYouFollow/Components/NewWorksFromGalleriesYouFollow.tsx @@ -84,6 +84,12 @@ const artworkConnectionFragment = graphql` } ` -export const NewWorksFromGalleriesYouFollowQR: React.FC = withSuspense(() => { - return -}, NewWorksFromGalleriesYouFollowPlaceholder) +export const NewWorksFromGalleriesYouFollowQR: React.FC = withSuspense({ + Component: () => { + return + }, + LoadingFallback: NewWorksFromGalleriesYouFollowPlaceholder, + ErrorFallback: () => { + return Nothing yet. Please check back later. + }, +}) diff --git a/src/app/Scenes/RecentlyViewed/Components/RecentlyViewedArtworks.tsx b/src/app/Scenes/RecentlyViewed/Components/RecentlyViewedArtworks.tsx index 367b47160b7..a0b56d6da37 100644 --- a/src/app/Scenes/RecentlyViewed/Components/RecentlyViewedArtworks.tsx +++ b/src/app/Scenes/RecentlyViewed/Components/RecentlyViewedArtworks.tsx @@ -85,6 +85,12 @@ const artworkConnectionFragment = graphql` } ` -export const RecentlyViewedArtworksQR: React.FC = withSuspense(() => { - return -}, RecentlyViewedPlaceholder) +export const RecentlyViewedArtworksQR: React.FC = withSuspense({ + Component: () => { + return + }, + LoadingFallback: RecentlyViewedPlaceholder, + ErrorFallback: () => { + return Nothing yet. Please check back later. + }, +}) diff --git a/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterArtistSeries.tsx b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterArtistSeries.tsx index ae30c239c20..a5ba4bb9d4a 100644 --- a/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterArtistSeries.tsx +++ b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterArtistSeries.tsx @@ -16,7 +16,7 @@ import { useSavedSearchFilter, useSearchCriteriaAttributes, } from "app/Scenes/SavedSearchAlert/helpers" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { compact } from "lodash" import { useState } from "react" import { TouchableOpacity } from "react-native" @@ -125,18 +125,22 @@ const savedSearchFilterPriceRangeQuery = graphql` } ` -export const SavedSearchFilterArtistSeriesQR: React.FC<{}> = withSuspense(() => { - const artistID = SavedSearchStore.useStoreState((state) => state.entity.artists[0].id) - const data = useLazyLoadQuery( - savedSearchFilterPriceRangeQuery, - { - artistID: artistID, - } - ) +export const SavedSearchFilterArtistSeriesQR: React.FC<{}> = withSuspense({ + Component: () => { + const artistID = SavedSearchStore.useStoreState((state) => state.entity.artists[0].id) + const data = useLazyLoadQuery( + savedSearchFilterPriceRangeQuery, + { + artistID: artistID, + } + ) - if (!data.artist) { - return null - } + if (!data.artist) { + return null + } - return -}, Placeholder) + return + }, + LoadingFallback: Placeholder, + ErrorFallback: NoFallback, +}) diff --git a/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterPriceRange.tests.tsx b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterPriceRange.tests.tsx index 2392e43dd23..7bbe704c5c1 100644 --- a/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterPriceRange.tests.tsx +++ b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterPriceRange.tests.tsx @@ -25,7 +25,7 @@ describe("SavedSearchFilterPriceRange", () => { }), }) - await waitForElementToBeRemoved(() => screen.getByTestId("loading-skeleton")) + await waitForElementToBeRemoved(() => screen.queryByTestId("loading-skeleton")) expect(screen.getByLabelText("Minimum Price Range Input")).toHaveProp("value", "200") expect(screen.getByLabelText("Maximum Price Range Input")).toHaveProp("value", "3000") @@ -47,7 +47,7 @@ describe("SavedSearchFilterPriceRange", () => { }), }) - await waitForElementToBeRemoved(() => screen.getByTestId("loading-skeleton")) + await waitForElementToBeRemoved(() => screen.queryByTestId("loading-skeleton")) expect(screen.getByLabelText("Minimum Price Range Input")).toHaveProp("value", "200") expect(screen.getByLabelText("Maximum Price Range Input")).toHaveProp("value", "3000") diff --git a/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterPriceRange.tsx b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterPriceRange.tsx index 9b85670c8d2..327f5d59957 100644 --- a/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterPriceRange.tsx +++ b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterPriceRange.tsx @@ -15,7 +15,7 @@ import { PriceRange } from "app/Components/PriceRange/types" import { getBarsFromAggregations } from "app/Components/PriceRange/utils" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" import { useSearchCriteriaAttributes } from "app/Scenes/SavedSearchAlert/helpers" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { useEffect, useState } from "react" import { graphql, useLazyLoadQuery } from "react-relay" import useDebounce from "react-use/lib/useDebounce" @@ -122,18 +122,22 @@ const savedSearchFilterPriceRangeQuery = graphql` } ` -export const SavedSearchFilterPriceRangeQR: React.FC<{}> = withSuspense(() => { - const artistID = SavedSearchStore.useStoreState((state) => state.entity.artists[0].id) - const data = useLazyLoadQuery( - savedSearchFilterPriceRangeQuery, - { - artistID: artistID, - } - ) +export const SavedSearchFilterPriceRangeQR: React.FC<{}> = withSuspense({ + Component: () => { + const artistID = SavedSearchStore.useStoreState((state) => state.entity.artists[0].id) + const data = useLazyLoadQuery( + savedSearchFilterPriceRangeQuery, + { + artistID: artistID, + } + ) - if (!data.artist) { - return null - } + if (!data.artist) { + return null + } - return -}, Placeholder) + return + }, + LoadingFallback: Placeholder, + ErrorFallback: NoFallback, +}) diff --git a/src/app/Scenes/SavedSearchAlert/screens/AlertPriceRangeScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AlertPriceRangeScreen.tsx index 53ef8f9a16a..9f5e6eb5c87 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AlertPriceRangeScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AlertPriceRangeScreen.tsx @@ -13,7 +13,7 @@ import { getBarsFromAggregations } from "app/Components/PriceRange/utils" import { CreateSavedSearchAlertNavigationStack } from "app/Scenes/SavedSearchAlert/SavedSearchAlertModel" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" import { GlobalStore } from "app/store/GlobalStore" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { NoFallback, withSuspense } from "app/utils/hooks/withSuspense" import { useState } from "react" import { graphql, useLazyLoadQuery } from "react-relay" @@ -109,11 +109,15 @@ const Placeholder: React.FC<{}> = () => ( ) export const AlertPriceRangeScreenQueryRenderer: React.FC = - withSuspense((props) => { - const artistID = SavedSearchStore.useStoreState((state) => state.entity.artists[0].id) - const data = useLazyLoadQuery(alertPriceRangeScreenQuery, { - artistID: artistID, - }) + withSuspense({ + Component: (props) => { + const artistID = SavedSearchStore.useStoreState((state) => state.entity.artists[0].id) + const data = useLazyLoadQuery(alertPriceRangeScreenQuery, { + artistID: artistID, + }) - return - }, Placeholder) + return + }, + LoadingFallback: Placeholder, + ErrorFallback: NoFallback, + }) diff --git a/src/app/Scenes/SavedSearchAlert/screens/ConfirmationScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/ConfirmationScreen.tsx index d0eac2c6072..c1b8dfd3c9e 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/ConfirmationScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/ConfirmationScreen.tsx @@ -123,8 +123,8 @@ const MatchingArtworksPlaceholder: React.FC = () => { ) } -const MatchingArtworksContainer: React.FC<{ closeModal?: () => void }> = withSuspense( - ({ closeModal }) => { +const MatchingArtworksContainer: React.FC<{ closeModal?: () => void }> = withSuspense({ + Component: ({ closeModal }) => { // TODO: instead of using artworksConnection and passing attributes from the store, use `alert.artworksConnection` field instead. const attributes = SavedSearchStore.useStoreState((state) => state.attributes) const currentArtworkID = SavedSearchStore.useStoreState((state) => state.currentArtworkID) @@ -140,8 +140,15 @@ const MatchingArtworksContainer: React.FC<{ closeModal?: () => void }> = withSus return }, - MatchingArtworksPlaceholder -) + LoadingFallback: MatchingArtworksPlaceholder, + ErrorFallback: () => { + return ( + + There aren't any works available that meet the criteria at this time. + + ) + }, +}) const matchingArtworksQuery = graphql` query ConfirmationScreenMatchingArtworksQuery($input: FilterArtworksInput, $first: Int) { diff --git a/src/app/Scenes/SellWithArtsy/ArtworkForm/SubmitArtworkFormEdit.tsx b/src/app/Scenes/SellWithArtsy/ArtworkForm/SubmitArtworkFormEdit.tsx index 51554555227..cf54ca7ebf9 100644 --- a/src/app/Scenes/SellWithArtsy/ArtworkForm/SubmitArtworkFormEdit.tsx +++ b/src/app/Scenes/SellWithArtsy/ArtworkForm/SubmitArtworkFormEdit.tsx @@ -1,35 +1,50 @@ import { SubmitArtworkFormEditQuery } from "__generated__/SubmitArtworkFormEditQuery.graphql" +import { LoadFailureView } from "app/Components/LoadFailureView" import { RetryErrorBoundary } from "app/Components/RetryErrorBoundary" import { SubmitArtworkForm, SubmitArtworkProps, } from "app/Scenes/SellWithArtsy/ArtworkForm/SubmitArtworkForm" import { getInitialSubmissionValues } from "app/Scenes/SellWithArtsy/ArtworkForm/Utils/getInitialSubmissionValues" -import { withSuspense } from "app/utils/hooks/withSuspense" +import { SpinnerFallback, withSuspense } from "app/utils/hooks/withSuspense" import { graphql, useLazyLoadQuery } from "react-relay" -export const SubmitArtworkFormEdit: React.FC = withSuspense((props) => { - const data = useLazyLoadQuery( - submitArtworkFormEditQuery, - { - id: props.externalID, - }, - { fetchPolicy: "network-only" } - ) +export const SubmitArtworkFormEdit: React.FC = withSuspense({ + Component: (props) => { + const data = useLazyLoadQuery( + submitArtworkFormEditQuery, + { + id: props.externalID, + }, + { fetchPolicy: "network-only" } + ) - return ( - <> - {!!data?.submission && ( - - )} - - ) + return ( + <> + {!!data?.submission && ( + + )} + + ) + }, + LoadingFallback: SpinnerFallback, + ErrorFallback: (fallbackProps) => { + return ( + + ) + }, }) export const SubmitArtworkFormEditContainer: React.FC = (props) => { diff --git a/src/app/utils/hooks/withSuspense.tsx b/src/app/utils/hooks/withSuspense.tsx index dcc3c9aa863..a8ae390db2e 100644 --- a/src/app/utils/hooks/withSuspense.tsx +++ b/src/app/utils/hooks/withSuspense.tsx @@ -1,26 +1,87 @@ import { Flex, Spinner } from "@artsy/palette-mobile" +import { captureException } from "@sentry/react-native" import { ProvidePlaceholderContext } from "app/utils/placeholders" -import { Suspense } from "react" - -export const withSuspense = - ( - Component: React.FC, - Fallback: React.FC = () => ( - - - - ) - ) => - (props: any) => { +import { ReactElement, Suspense } from "react" +import { ErrorBoundary, FallbackProps } from "react-error-boundary" + +/** + * A symbol used to indicate that no error fallback should be rendered. + */ +export const NoFallback = Symbol("NoFallback") + +/** + * A symbol used to indicate that the default Spinner fallback should be used. + */ +export const SpinnerFallback = Symbol("SpinnerFallback") + +interface WithSuspenseOptions { + /** + * The component to be rendered within the suspense boundary. + */ + Component: React.FC + + /** + * The component to display while the content is loading. + * Pass `SpinnerFallback` to use the default loading spinner, in most cases you should use a skeleton loader instead. + */ + LoadingFallback: React.FC | typeof SpinnerFallback + + /** + * The component to display if an error occurs. + * Pass `NoFallback` to skip rendering any error fallback, only use this for subcomponents that don't need to render anything, e.g. a rail. + * Make sure to test your error component and make sure nav is available to navigate away from the error. + */ + ErrorFallback: ((props: FallbackProps) => ReactElement | null) | typeof NoFallback +} + +const DefaultLoadingFallback: React.FC = () => ( + + + +) + +/** + * A higher-order component that wraps the given component in an ErrorBoundary and Suspense. + * Allows specifying fallback components for both loading and error states. + * + * @param {WithSuspenseOptions} options - Configuration options for the HOC. + * @param {React.FC} options.Component - The component to render within the suspense boundary. + * @param {React.FC | typeof SpinnerFallback} options.LoadingFallback - The component to display while loading (or `SpinnerFallback` for default). + * @param {((props: FallbackProps) => ReactElement | null) | typeof NoFallback} options.ErrorFallback - The component to display if an error occurs (or `NoFallback` to skip). + * @returns {React.FC} The wrapped component with suspense and error handling. + */ +export const withSuspense = ({ + Component, + LoadingFallback, + ErrorFallback, +}: WithSuspenseOptions) => { + const LoadingFallbackComponent = + LoadingFallback === SpinnerFallback ? DefaultLoadingFallback : LoadingFallback + + return (props: any) => { + // we display the fallback component if error or we defensively hide the component return ( - - - - } + { + if (ErrorFallback === NoFallback) { + // No fallback means render nothing when an error occurs + return null + } + return ErrorFallback ? ErrorFallback(error) : null + }} + // onError captures the exception and sends it to Sentry + onError={(error) => captureException(error)} > - - + + + + } + > + + + ) } +} diff --git a/test-patches/whatwg-fetch+3.0.0.patch b/test-patches/whatwg-fetch+3.0.0.patch new file mode 100644 index 00000000000..65c9fe62f95 --- /dev/null +++ b/test-patches/whatwg-fetch+3.0.0.patch @@ -0,0 +1,77 @@ +diff --git a/node_modules/whatwg-fetch/dist/fetch.umd.js b/node_modules/whatwg-fetch/dist/fetch.umd.js +index f9b44fd..331f863 100644 +--- a/node_modules/whatwg-fetch/dist/fetch.umd.js ++++ b/node_modules/whatwg-fetch/dist/fetch.umd.js +@@ -444,6 +444,28 @@ + exports.DOMException.prototype.constructor = exports.DOMException; + } + ++ const hostRejectionMap = { ++ "cdn-settings.segment.com": false, ++ "localhost": false, ++ "echo.artsy.net": false, ++ "metaphysics-staging.artsy.net": false, ++ "unleashprx-staging.artsy.net": false, ++ "staging.artsy.net": false, ++ "live-staging.artsy.net": false, ++ "clients3.google.com": false, ++ "volley-staging.artsy.net": false, ++ "stagingapi.artsy.net": false, ++ "api.segment.io": false, ++ "localhost:8081": false, ++ "click.artsy.net": false ++ }; ++ ++ ++ const metaphysicsRejectionTests = { ++ query: "query HomeAboveTheFoldQuery", ++ saleID: "artsy-auction-street-art-7", // Key-value pair to match in the body ++ }; ++ + function fetch(input, init) { + return new Promise(function(resolve, reject) { + var request = new Request(input, init); +@@ -452,6 +474,43 @@ + return reject(new exports.DOMException('Aborted', 'AbortError')) + } + ++ // Extract the host from the request URL ++ const requestUrl = new URL(request.url); ++ const requestHost = requestUrl.host; ++ ++ // Check if the host exists in the hostRejectionMap ++ if (!Object.prototype.hasOwnProperty.call(hostRejectionMap, requestHost)) { ++ console.log(`FETCH: Host not in supported list: ${requestHost}`); ++ } else if (requestHost.includes("metaphysics")) { ++ // If it's a request to Metaphysics, inspect the body content ++ try { ++ // Ensure that the body exists and is a JSON string ++ if (init.body) { ++ const body = JSON.parse(init.body); ++ ++ console.log("FETCH: Metaphysics request body", body); ++ ++ // Check if the body matches the query string in rejection tests ++ const shouldRejectQuery = metaphysicsRejectionTests.query && body.query.includes(metaphysicsRejectionTests.query); ++ ++ // Check for variable-based rejection ++ const shouldRejectVariables = Object.entries(metaphysicsRejectionTests).some(([key, value]) => { ++ return key !== 'query' && body.variables && body.variables[key] === value; ++ }); ++ ++ if (shouldRejectQuery || shouldRejectVariables) { ++ console.warn("FETCH: Network request failed due to specific GraphQL query conditions"); ++ return reject(new Error('Network request failed due to specific GraphQL query conditions')); ++ } ++ } ++ } catch (e) { ++ console.log("Failed to parse request body for inspection", e); ++ } ++ } else if (hostRejectionMap[requestHost]) { ++ // If toggle is true, simulate a failure ++ return reject(new TypeError('Network request failed')); ++ } ++ + var xhr = new XMLHttpRequest(); + + function abortXhr() { diff --git a/yarn.lock b/yarn.lock index c6b68afeea8..ceb92344b00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12489,6 +12489,13 @@ react-dom@17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" +react-error-boundary@4.0.13: + version "4.0.13" + resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-4.0.13.tgz#80386b7b27b1131c5fbb7368b8c0d983354c7947" + integrity sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ== + dependencies: + "@babel/runtime" "^7.12.5" + react-error-boundary@^3.1.0: version "3.1.3" resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.3.tgz#276bfa05de8ac17b863587c9e0647522c25e2a0b"