diff --git a/package.json b/package.json index ae65767d0c8..910773153a8 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ }, "dependencies": { "@artsy/cohesion": "4.154.0", - "@artsy/palette-mobile": "13.0.16", + "@artsy/palette-mobile": "13.0.18", "@artsy/to-title-case": "1.1.0", "@expo/react-native-action-sheet": "4.0.1", "@gorhom/bottom-sheet": "4.5.1", diff --git a/src/app/Components/Artist/ArtistArtworks/ArtistArtworks.tsx b/src/app/Components/Artist/ArtistArtworks/ArtistArtworks.tsx index fce64112a1a..2db65d7eb20 100644 --- a/src/app/Components/Artist/ArtistArtworks/ArtistArtworks.tsx +++ b/src/app/Components/Artist/ArtistArtworks/ArtistArtworks.tsx @@ -12,6 +12,7 @@ import { useScreenDimensions, useSpace, } from "@artsy/palette-mobile" +import { MasonryFlashListRef } from "@shopify/flash-list" import { ArtistArtworks_artist$data } from "__generated__/ArtistArtworks_artist.graphql" import { ArtistArtworksFilterHeader } from "app/Components/Artist/ArtistArtworks/ArtistArtworksFilterHeader" import { CreateSavedSearchModal } from "app/Components/Artist/ArtistArtworks/CreateSavedSearchModal" @@ -36,7 +37,7 @@ import { ON_END_REACHED_THRESHOLD_MASONRY, } from "app/utils/masonryHelpers" import { Schema } from "app/utils/track" -import React, { useCallback, useEffect, useMemo, useState } from "react" +import React, { useCallback, useEffect, useMemo, useRef, useState } from "react" import { RelayPaginationProp, createPaginationContainer, graphql } from "react-relay" import { useTracking } from "react-tracking" @@ -45,12 +46,14 @@ interface ArtworksGridProps extends InfiniteScrollGridProps { searchCriteria: SearchCriteriaAttributes | null relay: RelayPaginationProp predefinedFilters?: FilterArray + scrollToArtworksGrid: boolean } const ArtworksGrid: React.FC = ({ artist, relay, predefinedFilters, + scrollToArtworksGrid, searchCriteria, ...props }) => { @@ -64,6 +67,9 @@ const ArtworksGrid: React.FC = ({ const showCreateAlertAtEndOfList = useFeatureFlag("ARShowCreateAlertInArtistArtworksListFooter") const enableAlertsFilters = useFeatureFlag("AREnableAlertsFilters") const artworks = useMemo(() => extractNodes(artist.artworks), [artist.artworks]) + + const gridRef = useRef>(null) + const appliedFilters = ArtworksFiltersStore.useStoreState((state) => state.appliedFilters) const { navigateToPageableRoute } = useNavigateToPageableRoute({ items: artworks }) @@ -100,6 +106,13 @@ const ArtworksGrid: React.FC = ({ setInitialFilterStateAction(filters) }, []) + useEffect(() => { + if (scrollToArtworksGrid) { + setTimeout(() => { + gridRef.current?.scrollToOffset({ offset: 0, animated: true }) + }, 1000) + } + }) const { savedSearchEntity, attributes } = useCreateSavedSearchModalFilters({ entityId: artist.internalID!, entityName: artist.name ?? "", @@ -232,6 +245,7 @@ const ArtworksGrid: React.FC = ({ numColumns={NUM_COLUMNS_MASONRY} estimatedItemSize={ESTIMATED_MASONRY_ITEM_SIZE} keyboardShouldPersistTaps="handled" + innerRef={gridRef} ListEmptyComponent={ { paramValue: "-published_at", }, ], + scrollToArtworksGrid: true, + searchCriteriaID: undefined, }, }) }) @@ -137,6 +139,7 @@ describe("ActivityItem", () => { expect(navigate).toHaveBeenCalledWith(alertTargetUrl, { passProps: { + scrollToArtworksGrid: true, searchCriteriaID: "searchCriteriaId", predefinedFilters: [ { diff --git a/src/app/Scenes/Activity/utils/navigateToActivityItem.ts b/src/app/Scenes/Activity/utils/navigateToActivityItem.ts index 76137012b0a..83de9e66e13 100644 --- a/src/app/Scenes/Activity/utils/navigateToActivityItem.ts +++ b/src/app/Scenes/Activity/utils/navigateToActivityItem.ts @@ -1,5 +1,6 @@ import { FilterArray } from "app/Components/ArtworkFilter/ArtworkFilterHelpers" import { ORDERED_ARTWORK_SORTS } from "app/Components/ArtworkFilter/Filters/SortOptions" +import { matchRoute } from "app/routes" import { navigate } from "app/system/navigation/navigate" import { last } from "lodash" import { parse as parseQueryString } from "query-string" @@ -13,10 +14,16 @@ export const navigateToActivityItem = (targetHref: string) => { (sortEntity) => sortEntity.paramValue === "-published_at" )! + const passProps: any = { + predefinedFilters: [sortFilterItem] as FilterArray, + searchCriteriaID: parsed.search_criteria_id, + } + + if ((matchRoute(targetHref) as any).module === "Artist") { + passProps.scrollToArtworksGrid = true + } + navigate(targetHref, { - passProps: { - predefinedFilters: [sortFilterItem] as FilterArray, - searchCriteriaID: parsed.search_criteria_id, - }, + passProps, }) } diff --git a/src/app/Scenes/Artist/Artist.tsx b/src/app/Scenes/Artist/Artist.tsx index 6207acd97f4..14546a3174e 100644 --- a/src/app/Scenes/Artist/Artist.tsx +++ b/src/app/Scenes/Artist/Artist.tsx @@ -57,6 +57,7 @@ interface ArtistProps { initialTab?: string me: ArtistAboveTheFoldQuery["response"]["me"] predefinedFilters?: FilterArray + scrollToArtworksGrid: boolean searchCriteria: SearchCriteriaAttributes | null } @@ -69,6 +70,7 @@ export const Artist: React.FC = (props) => { initialTab = INITIAL_TAB, me, predefinedFilters, + scrollToArtworksGrid, searchCriteria, } = props @@ -142,6 +144,7 @@ export const Artist: React.FC = (props) => { artist={artistAboveTheFold} searchCriteria={searchCriteria} predefinedFilters={predefinedFilters} + scrollToArtworksGrid={scrollToArtworksGrid} /> @@ -175,13 +178,14 @@ export const Artist: React.FC = (props) => { } interface ArtistQueryRendererProps { + artistID: string + categories?: string[] environment?: RelayModernEnvironment initialTab?: string - searchCriteriaID?: string - search_criteria_id?: string - artistID: string predefinedFilters?: FilterArray - categories?: string[] + scrollToArtworksGrid?: boolean + search_criteria_id?: string + searchCriteriaID?: string sizes?: string[] } @@ -215,12 +219,13 @@ export const defaultArtistVariables = () => ({ export const ArtistQueryRenderer: React.FC = (props) => { const { artistID, + categories, environment, initialTab, - searchCriteriaID, - search_criteria_id, predefinedFilters, - categories, + search_criteria_id, + scrollToArtworksGrid = false, + searchCriteriaID, sizes, } = props @@ -290,6 +295,7 @@ export const ArtistQueryRenderer: React.FC = (props) = categories: categories ?? [], sizes: sizes ?? [], })} + scrollToArtworksGrid={scrollToArtworksGrid} /> ) }, diff --git a/src/app/Scenes/Search/components/AutosuggestSearchResult.tests.tsx b/src/app/Scenes/Search/components/AutosuggestSearchResult.tests.tsx index 0641f37cea2..de4a9b09ead 100644 --- a/src/app/Scenes/Search/components/AutosuggestSearchResult.tests.tsx +++ b/src/app/Scenes/Search/components/AutosuggestSearchResult.tests.tsx @@ -1,3 +1,4 @@ +import { Touchable } from "@artsy/palette-mobile" import { fireEvent } from "@testing-library/react-native" import { SearchContext } from "app/Scenes/Search/SearchContext" import { GlobalStore, GlobalStoreProvider } from "app/store/GlobalStore" @@ -5,7 +6,6 @@ import { EntityType, navigate, navigateToEntity, SlugType } from "app/system/nav import { CatchErrors } from "app/utils/CatchErrors" import { extractText } from "app/utils/tests/extractText" import { renderWithWrappers, renderWithWrappersLEGACY } from "app/utils/tests/renderWithWrappers" -import { Touchable } from "@artsy/palette-mobile" import { Pressable } from "react-native" import { act } from "react-test-renderer" import { AutosuggestSearchResult } from "./AutosuggestSearchResult" @@ -92,7 +92,7 @@ describe(AutosuggestSearchResult, () => { tree.root.findByType(Touchable).props.onPress() await new Promise((r) => setTimeout(r, 50)) expect(inputBlurMock).toHaveBeenCalled() - expect(navigate).toHaveBeenCalledWith(result.href, { passProps: { initialTab: "Artworks" } }) + expect(navigate).toHaveBeenCalledWith(`${result.href}`) }) it(`highlights a part of the string even when the string has diacritics but the highlight doesn't`, async () => { @@ -253,7 +253,7 @@ describe(AutosuggestSearchResult, () => { tree.root.findAllByType(Pressable)[0].props.onPress() }) await new Promise((r) => setTimeout(r, 50)) - expect(navigate).toHaveBeenCalledWith("/artist/anto-carte", { + expect(navigate).toHaveBeenCalledWith("/artist/anto-carte/artworks", { passProps: { initialTab: "Artworks" }, }) @@ -261,7 +261,7 @@ describe(AutosuggestSearchResult, () => { tree.root.findAllByType(Pressable)[1].props.onPress() }) await new Promise((r) => setTimeout(r, 50)) - expect(navigate).toHaveBeenCalledWith("/artist/anto-carte", { + expect(navigate).toHaveBeenCalledWith("/artist/anto-carte/auction-results", { passProps: { initialTab: "Insights" }, }) }) diff --git a/src/app/Scenes/Search/components/AutosuggestSearchResult.tsx b/src/app/Scenes/Search/components/AutosuggestSearchResult.tsx index ddb54ccbb6b..7b112e257d3 100644 --- a/src/app/Scenes/Search/components/AutosuggestSearchResult.tsx +++ b/src/app/Scenes/Search/components/AutosuggestSearchResult.tsx @@ -29,7 +29,11 @@ export type TrackResultPress = (result: AutosuggestResult, itemIndex?: number) = type ArtistTabs = "Insights" | "Artworks" -type HandleResultPress = (passProps?: { artistTab: ArtistTabs }) => void +type PassedProps = { + initialTab: ArtistTabs +} + +type HandleResultPress = (passProps?: PassedProps) => void const getResultType = (result: AutosuggestResult) => { if (result.displayType) { @@ -77,7 +81,7 @@ export const AutosuggestSearchResult: React.FC<{ inputRef.current?.blur() // need to wait a tick to push next view otherwise the input won't blur ¯\_(ツ)_/¯ setTimeout(() => { - navigateToResult(result, passProps?.artistTab) + navigateToResult(result, passProps) if (updateRecentSearchesOnTap) { GlobalStore.actions.search.addRecentSearch({ type: "AUTOSUGGEST_RESULT_TAPPED", @@ -162,7 +166,7 @@ export const AutosuggestSearchResult: React.FC<{ highlightEnabled Icon={ArtworkIcon} rounded - onPress={() => onPress({ artistTab: "Artworks" })} + onPress={() => onPress({ initialTab: "Artworks" })} block > Artworks @@ -172,7 +176,7 @@ export const AutosuggestSearchResult: React.FC<{ highlightEnabled Icon={AuctionIcon} rounded - onPress={() => onPress({ artistTab: "Insights" })} + onPress={() => onPress({ initialTab: "Insights" })} block > Auction Results @@ -189,13 +193,19 @@ export const AutosuggestSearchResult: React.FC<{ * about the entity type to render the correct placeholder/skeleton loader * @param result */ -function navigateToResult(result: AutosuggestResult, artistTab: ArtistTabs = "Artworks") { +function navigateToResult(result: AutosuggestResult, props?: PassedProps) { if (result.displayType === "Gallery" || result.displayType === "Institution") { navigateToPartner(result.href!) } else if (result.displayType === "Fair") { navigateToEntity(result.href!, EntityType.Fair, SlugType.ProfileID) } else if (result.__typename === "Artist") { - navigate(result.href!, { passProps: { initialTab: artistTab } }) + if (props?.initialTab === "Insights") { + navigate(`${result.href!}/auction-results`, { passProps: props }) + } + if (props?.initialTab === "Artworks") { + navigate(`${result.href!}/artworks`, { passProps: props }) + } + navigate(result.href!) } else { navigate(result.href!) } diff --git a/src/app/routes.ts b/src/app/routes.ts index 6c536c781d7..8cda0879e99 100644 --- a/src/app/routes.ts +++ b/src/app/routes.ts @@ -138,6 +138,10 @@ function getDomainMap(): Record { ...params, initialTab: "Insights", })), + addRoute("/artist/:artistID/artworks", "Artist", (params) => ({ + ...params, + initialTab: "Insights", + })), addRoute("/artist/:artistID/shows", "ArtistShows"), // Routes `/artist/:artistID/*` and `"/:profile_id_ignored/artist/:artistID"` diff --git a/yarn.lock b/yarn.lock index 62fe57c6067..69728cc702b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24,10 +24,10 @@ dependencies: core-js "3" -"@artsy/palette-mobile@13.0.16": - version "13.0.16" - resolved "https://registry.yarnpkg.com/@artsy/palette-mobile/-/palette-mobile-13.0.16.tgz#7bc4adeba350ac81e0baee2f27809e55f4ad8778" - integrity sha512-fZeXaRFK6BrKq+iXRF+enD8+BATmUJV5ME2DwgT/x9bKNnocfuGPGGwVVLXO2vYetWDRXkntVDuNbx89HM4//A== +"@artsy/palette-mobile@13.0.18": + version "13.0.18" + resolved "https://registry.yarnpkg.com/@artsy/palette-mobile/-/palette-mobile-13.0.18.tgz#401e8d7650214969c2f45f55dcf7f03f211017a7" + integrity sha512-zgECF6QcMO46Ymoc/01QfW7Q6ZwK9eYF6LVkrBb7yFBtkqhMeFJjS/FnrBchGiyQQVha9MWRmoidoreEbtbDiA== dependencies: "@artsy/palette-tokens" "^5.0.0" "@shopify/flash-list" "^1.5.0"