# 0.31.0 (/docs/changelog/0-31-0)



<Callout type="info" title="No native changes to the platform">
  Every platform change here is JavaScript. No native module was added or
  altered, so apps pick this up with a build at `0.31.0` — no
  [store release](/docs/ship/app-releases) is required for the platform itself.

  The new integration is the exception, and only if you adopt it:
  `@gauntlet/easyrewardz` peers on `@react-native-clipboard/clipboard`, which
  is not in the app template. Most apps carrying a loyalty integration already
  have it; adding it to one that does not is a native change for that app.
</Callout>

## Breaking [#breaking]

**Collection lookups can return `null`.** Four `ICatalogProvider` methods
widened their return type:

| Method                              | Was                                 | Now                                       |
| ----------------------------------- | ----------------------------------- | ----------------------------------------- |
| `getCollectionByHandle`             | `Promise<ICollection>`              | `Promise<ICollection \| null>`            |
| `getCollectionById`                 | `Promise<ICollection>`              | `Promise<ICollection \| null>`            |
| `getCollectionWithProductsByHandle` | `Promise<{ collection, products }>` | `Promise<CollectionWithProducts \| null>` |
| `getCollectionWithProductsById`     | `Promise<{ collection, products }>` | `Promise<CollectionWithProducts \| null>` |

Call sites have to handle `null`, and a custom catalog provider has to widen its
own return types to match. The shape the last two resolve to is now exported as
`CollectionWithProducts` from `@gauntlet/types`, rather than written inline.

**`getCollectionWithProducts*` take filter arguments before the cursor.** The
signature changed from `(id, cursor?)` to
`(id, query?, price?, sort?, cursor?, productsFirst?)`. An existing
two-argument call now passes the cursor where the query belongs. TypeScript
flags it, so nothing fails silently, but every call site needs looking at.

## New [#new]

**`@gauntlet/easyrewardz` — EasyRewardz loyalty, published for the first time.**
Customer snapshot, points balance, the points ledger and gift vouchers. It is
read-only: points are shown, never redeemed. Requests are proxied through
Appbrew's backend, which holds the EasyRewardz credentials, so the app carries
no keys and talks to no EasyRewardz endpoint directly. A failure comes back as
an error rather than as a zero balance or an empty list, so the blocks take
their error path instead of rendering a plausible lie. Register it with
`registerEasyrewardzBlocks` and configure it under the `easyrewardz` key.

**Nector referral claim.** `useNectorReferralCode` and
`extractNectorReferralCode` read a referral code off the deep link that opened
the app, and `ReferralClaimForm` and `NectorReferralPrompt` collect and submit
it. The form claims over mobile or email, or picks the channel itself.

**A rating distribution graph on Nector reviews.** The review summary can show
the familiar five-bar breakdown of how many reviews sit at each star rating.
Opt in per block with `showNectorGraph`; the write-review button moves into the
summary when it is on.

**Flag options on the phone input.** `PhoneConfigOptions` adds
`countryCodelayout`, `flagSize`, `flagWithEmoji` and `flagWithButton`. The flag
now renders when the country code is laid out first, which it previously did
not.

## Changed [#changed]

**Move to wishlist from the cart.** `cart-list-v2` takes a
`moveToWishlistSelector` block of `MoveToWishlistOptions`: whether the control
toggles, whether it raises a toast, whether the line stays in the cart on add
and on remove, and which icon marks each state. The added, removed and error
toasts carry their own copy and an optional link.

**The rewards program is tracked per customer.** The slice now records which
customer a program belongs to. Two lookups that finish out of order can no
longer leave one shopper looking at another's balance, and the status stops
flashing through `loading` when the program in hand is already the right one.

**`RewardsProvider.getRewardsProgram` takes an optional lead id and tier.** Both
are optional, so an existing provider satisfies the interface unchanged.

## Fixed [#fixed]

* **A collection that no longer exists no longer crashes the screen.** The
  lookup returns `null` and warns, instead of casting an absent response and
  failing further down. This is the change behind the breaking note above.

* **Paging a collection that was evicted mid-flight writes nothing.** If the
  collection entry is reset, remounted or evicted while a page is in the air,
  the result belongs to a context nobody is rendering, and is now discarded
  rather than merged into whatever replaced it.

* **The OTP sheet dismisses itself after a successful sign-in** instead of
  staying up over the signed-in screen.

* **Nector credits review rewards**, and rewards data is refetched per customer
  rather than served warm from whoever was signed in before.

## Upgrading [#upgrading]

Move every `@gauntlet/*` entry to `0.31.0` together with the Appbrew-published
`@app-brew/*` packages, which version with the platform, then rebuild. No
`pod install` or Gradle change is needed for this release unless you are adding
`@gauntlet/easyrewardz` to an app that does not already have
`@react-native-clipboard/clipboard`. Check your collection call sites against
the breaking notes above before you ship. Full workflow:
[Upgrading @gauntlet packages](/docs/ship/upgrading).
