diff --git a/src/app/Scenes/Activity/components/PartnerOfferCreatedNotification.tsx b/src/app/Scenes/Activity/components/PartnerOfferCreatedNotification.tsx
index a6fde83f6ad..2dc5fd06ca7 100644
--- a/src/app/Scenes/Activity/components/PartnerOfferCreatedNotification.tsx
+++ b/src/app/Scenes/Activity/components/PartnerOfferCreatedNotification.tsx
@@ -41,9 +41,9 @@ export const PartnerOfferCreatedNotification: React.FC
- {subtitle}
+ {!!subtitle && {subtitle}}
diff --git a/src/app/Scenes/Activity/components/__tests__/PartnerOfferCreatedNotification.tests.tsx b/src/app/Scenes/Activity/components/__tests__/PartnerOfferCreatedNotification.tests.tsx
index 57b1d722a8d..5d3dcd10c60 100644
--- a/src/app/Scenes/Activity/components/__tests__/PartnerOfferCreatedNotification.tests.tsx
+++ b/src/app/Scenes/Activity/components/__tests__/PartnerOfferCreatedNotification.tests.tsx
@@ -60,7 +60,7 @@ describe("PartnerOfferCreatedNotification", () => {
expect(screen.getByText("$405,000")).toBeOnTheScreen()
expect(screen.getByText(/List price:\s*\$450,000\s*/)).toBeOnTheScreen()
expect(screen.getByText('"This is a note from the gallery"')).toBeOnTheScreen()
- expect(screen.getByText("Review the offer before it expires")).toBeOnTheScreen()
+ expect(screen.queryByText("Review the offer before it expires")).not.toBeOnTheScreen()
expect(screen.queryByText("Review the offer on your saved artwork")).not.toBeOnTheScreen()
expect(screen.queryByText("Manage Saves")).not.toBeOnTheScreen()
})
diff --git a/src/app/Scenes/Inbox/Components/Conversations/__tests__/ConversationCTA.tests.tsx b/src/app/Scenes/Inbox/Components/Conversations/__tests__/ConversationCTA.tests.tsx
index fd7edd92117..06737d57075 100644
--- a/src/app/Scenes/Inbox/Components/Conversations/__tests__/ConversationCTA.tests.tsx
+++ b/src/app/Scenes/Inbox/Components/Conversations/__tests__/ConversationCTA.tests.tsx
@@ -95,52 +95,70 @@ describe("conversation about an artwork with inquiry checkout enabled", () => {
expect(screen.UNSAFE_queryAllByType(OpenInquiryModalButton)).toHaveLength(1)
})
- describe("with an active partner offer", () => {
- const futureISO = () => new Date(Date.now() + 60 * 60 * 1000).toISOString()
-
- const partnerOfferResolvers = {
- Conversation: () => ({
- items: [{ item: { __typename: "Artwork" }, liveArtwork: { __typename: "Artwork" } }],
- activeOrders: { edges: [] },
- }),
- // Ensures both `items.item` and `liveArtwork` resolve to the same artwork id
- // that the offer below references.
- Artwork: () => ({ internalID: "123", href: "/artwork/foo", isOfferableFromInquiry: true }),
- Me: () => ({
- partnerOffersConnection: {
- edges: [
- {
- node: {
- internalID: "partner-offer-id",
- artworkId: "123",
- endAt: futureISO(),
- isAvailable: true,
- priceWithDiscount: { display: "US$450" },
+ // The `partnerOffersConnection` query fetches both `BULK` (sent via the
+ // partner dashboard's send-offer tool) and `PERSONALIZED` offer types.
+ // Eigen can't distinguish between the two once fetched, so both should
+ // behave identically here.
+ describe.each([["a personalized partner offer"], ["a bulk offer (send offer)"]])(
+ "with %s",
+ () => {
+ const futureISO = () => new Date(Date.now() + 60 * 60 * 1000).toISOString()
+ const pastISO = () => new Date(Date.now() - 60 * 60 * 1000).toISOString()
+
+ const buildPartnerOfferResolvers = (offerOverrides: Record = {}) => ({
+ Conversation: () => ({
+ items: [{ item: { __typename: "Artwork" }, liveArtwork: { __typename: "Artwork" } }],
+ activeOrders: { edges: [] },
+ }),
+ // Ensures both `items.item` and `liveArtwork` resolve to the same artwork id
+ // that the offer below references.
+ Artwork: () => ({ internalID: "123", href: "/artwork/foo", isOfferableFromInquiry: true }),
+ Me: () => ({
+ partnerOffersConnection: {
+ edges: [
+ {
+ node: {
+ internalID: "partner-offer-id",
+ artworkId: "123",
+ endAt: futureISO(),
+ isAvailable: true,
+ priceWithDiscount: { display: "US$450" },
+ ...offerOverrides,
+ },
},
- },
- ],
- },
- }),
- }
+ ],
+ },
+ }),
+ })
- it("replaces the inquiry buttons with the offer banner when the flag is on", () => {
- __globalStoreTestUtils__?.injectFeatureFlags({ AREnableConversationPartnerOffers: true })
+ it("replaces the inquiry buttons with the offer banner when the flag is on", () => {
+ __globalStoreTestUtils__?.injectFeatureFlags({ AREnableConversationPartnerOffers: true })
- renderWithRelay(partnerOfferResolvers)
+ renderWithRelay(buildPartnerOfferResolvers())
- // The dedicated offer banner replaces the inquiry transaction buttons.
- expect(screen.UNSAFE_queryAllByType(OpenInquiryModalButton)).toHaveLength(0)
- expect(screen.UNSAFE_queryAllByType(ConversationPartnerOfferCTA)).toHaveLength(1)
- })
+ // The dedicated offer banner replaces the inquiry transaction buttons.
+ expect(screen.UNSAFE_queryAllByType(OpenInquiryModalButton)).toHaveLength(0)
+ expect(screen.UNSAFE_queryAllByType(ConversationPartnerOfferCTA)).toHaveLength(1)
+ })
- it("still renders the inquiry buttons when the flag is off", () => {
- __globalStoreTestUtils__?.injectFeatureFlags({ AREnableConversationPartnerOffers: false })
+ it("still renders the inquiry buttons when the flag is off", () => {
+ __globalStoreTestUtils__?.injectFeatureFlags({ AREnableConversationPartnerOffers: false })
- renderWithRelay(partnerOfferResolvers)
+ renderWithRelay(buildPartnerOfferResolvers())
- expect(screen.UNSAFE_queryAllByType(OpenInquiryModalButton)).toHaveLength(1)
- })
- })
+ expect(screen.UNSAFE_queryAllByType(OpenInquiryModalButton)).toHaveLength(1)
+ })
+
+ it("falls back to the inquiry buttons once the offer expires", () => {
+ __globalStoreTestUtils__?.injectFeatureFlags({ AREnableConversationPartnerOffers: true })
+
+ renderWithRelay(buildPartnerOfferResolvers({ endAt: pastISO() }))
+
+ expect(screen.UNSAFE_queryAllByType(ConversationPartnerOfferCTA)).toHaveLength(0)
+ expect(screen.UNSAFE_queryAllByType(OpenInquiryModalButton)).toHaveLength(1)
+ })
+ }
+ )
it("renders the payment failed message if the payment failed", () => {
renderWithOrders({ lastTransactionFailed: true, mode: "OFFER" })
diff --git a/src/app/Scenes/Inbox/Components/Conversations/__tests__/ConversationPartnerOfferCTA.tests.tsx b/src/app/Scenes/Inbox/Components/Conversations/__tests__/ConversationPartnerOfferCTA.tests.tsx
index f97ff1197ae..e79a69c399a 100644
--- a/src/app/Scenes/Inbox/Components/Conversations/__tests__/ConversationPartnerOfferCTA.tests.tsx
+++ b/src/app/Scenes/Inbox/Components/Conversations/__tests__/ConversationPartnerOfferCTA.tests.tsx
@@ -65,20 +65,37 @@ describe("ConversationPartnerOfferCTA", () => {
__globalStoreTestUtils__?.injectFeatureFlags({ AREnableConversationPartnerOffers: true })
})
- it("renders the offer banner, tracks it, and navigates to the artwork with the partner offer id", () => {
- renderWithRelay(offerResolvers())
-
- expect(screen.getByText("Offer received for $450")).toBeTruthy()
-
- expect(mockTrackEvent).toHaveBeenCalledWith({
- action: "partnerOfferInConversationViewed",
- context_owner_id: "conversation-id",
- context_owner_type: "conversation",
- })
-
- fireEvent.press(screen.getByTestId("partnerOfferActionLink"))
- expect(navigate).toHaveBeenCalledWith("/artwork/some-artwork?partner_offer_id=partner-offer-id")
- })
+ // The `partnerOffersConnection` query fetches both `BULK` (sent via the
+ // partner dashboard's send-offer tool) and `PERSONALIZED` offer types.
+ // Eigen can't distinguish between the two once fetched, so the banner
+ // should render identically for both.
+ describe.each([["a personalized partner offer"], ["a bulk offer (send offer)"]])(
+ "with %s",
+ () => {
+ it("renders the offer banner, tracks it, and navigates to the artwork with the partner offer id", () => {
+ renderWithRelay(offerResolvers())
+
+ expect(screen.getByText("Offer received for $450")).toBeTruthy()
+
+ expect(mockTrackEvent).toHaveBeenCalledWith({
+ action: "partnerOfferInConversationViewed",
+ context_owner_id: "conversation-id",
+ context_owner_type: "conversation",
+ })
+
+ fireEvent.press(screen.getByTestId("partnerOfferActionLink"))
+ expect(navigate).toHaveBeenCalledWith(
+ "/artwork/some-artwork?partner_offer_id=partner-offer-id"
+ )
+ })
+
+ it("renders nothing when the offer has expired", () => {
+ renderWithRelay(offerResolvers({ endAt: pastISO() }))
+
+ expect(screen.queryByTestId("partnerOfferActionLink")).toBeNull()
+ })
+ }
+ )
it("falls back to a generic title when there is no discounted price", () => {
renderWithRelay(offerResolvers({ priceWithDiscount: null }))
@@ -103,12 +120,6 @@ describe("ConversationPartnerOfferCTA", () => {
expect(screen.queryByTestId("partnerOfferActionLink")).toBeNull()
})
- it("renders nothing when the offer has expired", () => {
- renderWithRelay(offerResolvers({ endAt: pastISO() }))
-
- expect(screen.queryByTestId("partnerOfferActionLink")).toBeNull()
- })
-
it("renders nothing when the offer is unavailable", () => {
renderWithRelay(offerResolvers({ isAvailable: false }))
diff --git a/src/app/Scenes/Inbox/hooks/usePartnerOffer.ts b/src/app/Scenes/Inbox/hooks/usePartnerOffer.ts
index 03fe46d320f..28b0052e71e 100644
--- a/src/app/Scenes/Inbox/hooks/usePartnerOffer.ts
+++ b/src/app/Scenes/Inbox/hooks/usePartnerOffer.ts
@@ -26,7 +26,7 @@ export const usePartnerOffer = ({ me, artworkId }: UsePartnerOfferProps) => {
const fragment = graphql`
fragment usePartnerOffer_me on Me {
- partnerOffersConnection(first: 100, offerType: [PERSONALIZED]) {
+ partnerOffersConnection(first: 100, offerType: [BULK, PERSONALIZED]) {
edges {
node {
...ConversationPartnerOfferCTA_partnerOffers