From e19037a8075f0b47e9e417300783b9a4e354bcd4 Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Mon, 15 Apr 2024 12:58:57 -0400 Subject: [PATCH] [PAY-2597] Test migration of a single endpoint from apiclient -> SDK (#8091) --- package-lock.json | 6 + packages/common/package.json | 1 + packages/common/src/api/topArtists.ts | 25 ++- .../src/audius-query/AudiusQueryContext.ts | 2 + packages/common/src/models/ImageSizes.ts | 24 +++ packages/common/src/models/PlaylistLibrary.ts | 11 ++ packages/common/src/models/User.ts | 102 +++++++++-- .../services/remote-config/feature-flags.ts | 2 + packages/common/src/store/effects.ts | 82 ++++++++- .../src/store/ui/related-artists/sagas.ts | 30 +++- packages/common/src/utils/index.ts | 1 + .../common/src/utils/sdkMigrationUtils.ts | 170 ++++++++++++++++++ .../mobile/src/app/AudiusQueryProvider.tsx | 7 + .../USDCPurchaseBuyerNotification.tsx | 2 +- .../USDCPurchaseSellerNotification.tsx | 2 +- packages/web/src/app/AudiusQueryProvider.tsx | 7 + 16 files changed, 440 insertions(+), 34 deletions(-) create mode 100644 packages/common/src/utils/sdkMigrationUtils.ts diff --git a/package-lock.json b/package-lock.json index 6a1dcd5dabc..daa8b11135e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57309,6 +57309,11 @@ "dev": true, "license": "MIT" }, + "node_modules/deep-object-diff": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.9.tgz", + "integrity": "sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==" + }, "node_modules/deepmerge": { "version": "4.3.1", "license": "MIT", @@ -109640,6 +109645,7 @@ "async-retry": "1.3.3", "bn.js": "5.1.0", "dayjs": "1.10.7", + "deep-object-diff": "1.1.9", "formik": "2.4.1", "fxa-common-password-list": "0.0.2", "hashids": "2.2.1", diff --git a/packages/common/package.json b/packages/common/package.json index 32cde05d115..8323a77ee4d 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -48,6 +48,7 @@ "async-retry": "1.3.3", "bn.js": "5.1.0", "dayjs": "1.10.7", + "deep-object-diff": "1.1.9", "formik": "2.4.1", "fxa-common-password-list": "0.0.2", "hashids": "2.2.1", diff --git a/packages/common/src/api/topArtists.ts b/packages/common/src/api/topArtists.ts index 5631838c219..dccd75dd97d 100644 --- a/packages/common/src/api/topArtists.ts +++ b/packages/common/src/api/topArtists.ts @@ -1,6 +1,7 @@ import { uniq } from 'lodash' import { createApi } from '~/audius-query' +import { userMetadataListFromSDK } from '~/models' import { ID } from '~/models/Identifiers' import { Kind } from '~/models/Kind' @@ -18,12 +19,26 @@ const topArtistsApi = createApi({ getTopArtistsInGenre: { async fetch(args: GetTopArtistsForGenreArgs, context) { const { genre, limit, offset } = args - const { apiClient } = context + const { apiClient, audiusSdk, checkSDKMigration } = context + const sdk = await audiusSdk() - return await apiClient.getTopArtistGenres({ - genres: [genre], - limit, - offset + return await checkSDKMigration({ + legacy: apiClient.getTopArtistGenres({ + genres: [genre], + limit, + offset + }), + migrated: async () => + userMetadataListFromSDK( + ( + await sdk.full.users.getTopUsersInGenre({ + genre: [genre], + limit, + offset + }) + ).data + ), + endpointName: 'getTopArtistsInGenre' }) }, options: { idArgKey: 'genre', kind: Kind.USERS, schemaKey: 'users' } diff --git a/packages/common/src/audius-query/AudiusQueryContext.ts b/packages/common/src/audius-query/AudiusQueryContext.ts index 619d316c79b..75ca06c1619 100644 --- a/packages/common/src/audius-query/AudiusQueryContext.ts +++ b/packages/common/src/audius-query/AudiusQueryContext.ts @@ -5,6 +5,7 @@ import type { Dispatch } from 'redux' import type { AudiusAPIClient } from '~/services/audius-api-client' import { AudiusBackend, Env, RemoteConfigInstance } from '~/services/index' +import { SDKMigrationChecker } from '~/utils/sdkMigrationUtils' import { ReportToSentryArgs } from '../models' @@ -14,6 +15,7 @@ export type AudiusQueryContextType = { audiusBackend: AudiusBackend dispatch: Dispatch reportToSentry: (args: ReportToSentryArgs) => void + checkSDKMigration: SDKMigrationChecker env: Env fetch: typeof fetch remoteConfigInstance: RemoteConfigInstance diff --git a/packages/common/src/models/ImageSizes.ts b/packages/common/src/models/ImageSizes.ts index 2880f6201fd..b45964cb1f1 100644 --- a/packages/common/src/models/ImageSizes.ts +++ b/packages/common/src/models/ImageSizes.ts @@ -1,3 +1,5 @@ +import { full } from '@audius/sdk' + export enum DefaultSizes { // Used as a catch-all fallback when no other size data is available. OVERRIDE = 'OVERRIDE' @@ -31,3 +33,25 @@ export type ProfilePictureSizesCids = export type ProfilePictureSizes = ImageSizesObject export type CoverPhotoSizesCids = ImageSizesObjectWithoutOverride export type CoverPhotoSizes = ImageSizesObject + +export const coverPhotoSizesCIDsFromSDK = ( + input: full.CoverPhoto +): CoverPhotoSizesCids => { + return [WidthSizes.SIZE_640, WidthSizes.SIZE_2000].reduce((out, size) => { + out[size] = input[size] ?? null + return out + }, {}) +} + +export const profilePictureSizesCIDsFromSDK = ( + input: full.ProfilePicture +): ProfilePictureSizesCids => { + return [ + SquareSizes.SIZE_1000_BY_1000, + SquareSizes.SIZE_150_BY_150, + SquareSizes.SIZE_480_BY_480 + ].reduce((out, size) => { + out[size] = input[size] ?? null + return out + }, {}) +} diff --git a/packages/common/src/models/PlaylistLibrary.ts b/packages/common/src/models/PlaylistLibrary.ts index 4843bd7a5d6..01070f44912 100644 --- a/packages/common/src/models/PlaylistLibrary.ts +++ b/packages/common/src/models/PlaylistLibrary.ts @@ -1,3 +1,5 @@ +import { full } from '@audius/sdk' + import { SmartCollectionVariant } from '~/models/SmartCollectionVariant' import { ID } from './Identifiers' @@ -43,3 +45,12 @@ export type PlaylistLibraryItem = export type PlaylistLibrary = { contents: (PlaylistLibraryFolder | PlaylistLibraryIdentifier)[] } + +export const playlistLibraryFromSDK = ( + input?: full.PlaylistLibrary +): PlaylistLibrary | undefined => { + if (!input) return undefined + return { + contents: input.contents as PlaylistLibraryItem[] + } +} diff --git a/packages/common/src/models/User.ts b/packages/common/src/models/User.ts index a6d046a8d96..d45a46c2027 100644 --- a/packages/common/src/models/User.ts +++ b/packages/common/src/models/User.ts @@ -1,3 +1,7 @@ +import { full } from '@audius/sdk' +import { omit } from 'lodash' +import snakecaseKeys from 'snakecase-keys' + import { Collectible, CollectiblesMetadata } from '~/models/Collectible' import { Color } from '~/models/Color' import { CID, ID } from '~/models/Identifiers' @@ -5,11 +9,17 @@ import { CoverPhotoSizes, CoverPhotoSizesCids, ProfilePictureSizes, - ProfilePictureSizesCids + ProfilePictureSizesCids, + coverPhotoSizesCIDsFromSDK, + profilePictureSizesCIDsFromSDK } from '~/models/ImageSizes' -import { PlaylistLibrary } from '~/models/PlaylistLibrary' +import { + PlaylistLibrary, + playlistLibraryFromSDK +} from '~/models/PlaylistLibrary' import { SolanaWalletAddress, StringWei, WalletAddress } from '~/models/Wallet' -import { Nullable } from '~/utils/typeUtils' +import { decodeHashId } from '~/utils/hashIds' +import { Nullable, removeNullable } from '~/utils/typeUtils' import { Timestamped } from './Timestamped' import { UserEvent } from './UserEvent' @@ -20,37 +30,37 @@ export type UserMetadata = { artist_pick_track_id: Nullable bio: Nullable blocknumber: number + collectibleList?: Collectible[] + collectibles?: CollectiblesMetadata + collectiblesOrderUnset?: boolean + cover_photo_cids?: Nullable + cover_photo_sizes: Nullable cover_photo: Nullable creator_node_endpoint: Nullable current_user_followee_follow_count: number does_current_user_follow: boolean does_current_user_subscribe?: boolean + erc_wallet: WalletAddress followee_count: number follower_count: number - supporter_count: number - supporting_count: number - handle: string handle_lc: string + handle: string + has_collectibles: boolean is_deactivated: boolean is_verified: boolean location: Nullable + metadata_multihash: Nullable name: string playlist_count: number + profile_picture_cids?: Nullable + profile_picture_sizes: Nullable profile_picture: Nullable repost_count: number - track_count: number - cover_photo_sizes: Nullable - cover_photo_cids?: Nullable - profile_picture_sizes: Nullable - profile_picture_cids?: Nullable - metadata_multihash: Nullable - erc_wallet: WalletAddress - spl_wallet: Nullable - has_collectibles: boolean - collectibles?: CollectiblesMetadata - collectiblesOrderUnset?: boolean - collectibleList?: Collectible[] solanaCollectibleList?: Collectible[] + spl_wallet: Nullable + supporter_count: number + supporting_count: number + track_count: number // Only present on the "current" account track_save_count?: number @@ -69,7 +79,7 @@ export type UserMetadata = { associated_wallets?: Nullable associated_sol_wallets?: Nullable associated_wallets_balance?: Nullable - playlist_library?: PlaylistLibrary + playlist_library?: Nullable userBank?: SolanaWalletAddress local?: boolean events?: UserEvent @@ -100,3 +110,57 @@ export type UserMultihash = Pick< User, 'metadata_multihash' | 'creator_node_endpoint' > + +/** Converts a SDK `full.UserFull` response to a UserMetadata. Note: Will _not_ include the "current user" fields as those aren't returned by the Users API */ +export const userMetadataFromSDK = ( + input: full.UserFull +): UserMetadata | undefined => { + const user = snakecaseKeys(input) + const decodedUserId = decodeHashId(user.id) + if (!decodedUserId) { + return undefined + } + + const newUser: UserMetadata = { + // Fields from API that are omitted in this model + ...omit(user, ['id', 'cover_photo_legacy', 'profile_picture_legacy']), + + // Conversions + artist_pick_track_id: user.artist_pick_track_id + ? decodeHashId(user.artist_pick_track_id) + : null, + + // Nested Types + playlist_library: playlistLibraryFromSDK(user.playlist_library) ?? null, + cover_photo_cids: user.cover_photo_cids + ? coverPhotoSizesCIDsFromSDK(user.cover_photo_cids) + : null, + profile_picture_cids: user.profile_picture_cids + ? profilePictureSizesCIDsFromSDK(user.profile_picture_cids) + : null, + + // Re-types + balance: user.balance as StringWei, + associated_wallets_balance: user.associated_wallets_balance as StringWei, + total_balance: user.total_balance as StringWei, + user_id: decodedUserId, + spl_wallet: user.spl_wallet as SolanaWalletAddress, + + // Legacy Overrides + cover_photo: user.cover_photo_legacy ?? null, + profile_picture: user.profile_picture_legacy ?? null, + + // Required Nullable fields + bio: user.bio ?? null, + cover_photo_sizes: user.cover_photo_sizes ?? null, + creator_node_endpoint: user.creator_node_endpoint ?? null, + location: user.location ?? null, + metadata_multihash: user.metadata_multihash ?? null, + profile_picture_sizes: user.profile_picture_sizes ?? null + } + + return newUser +} + +export const userMetadataListFromSDK = (input?: full.UserFull[]) => + input ? input.map((d) => userMetadataFromSDK(d)).filter(removeNullable) : [] diff --git a/packages/common/src/services/remote-config/feature-flags.ts b/packages/common/src/services/remote-config/feature-flags.ts index b6e09aec7b1..77e61301487 100644 --- a/packages/common/src/services/remote-config/feature-flags.ts +++ b/packages/common/src/services/remote-config/feature-flags.ts @@ -58,6 +58,7 @@ export enum FeatureFlags { TIKTOK_NATIVE_AUTH = 'tiktok_native_auth', PREMIUM_ALBUMS_ENABLED = 'premium_albums_enabled', REWARDS_COOLDOWN = 'rewards_cooldown', + SDK_MIGRATION_SHADOWING = 'sdk_migration_shadowing', USE_SDK_TIPS = 'use_sdk_tips', USE_SDK_REWARDS = 'use_sdk_rewards', DISCOVERY_TIP_REACTIONS = 'discovery_tip_reactions' @@ -134,6 +135,7 @@ export const flagDefaults: FlagDefaults = { [FeatureFlags.TIKTOK_NATIVE_AUTH]: true, [FeatureFlags.PREMIUM_ALBUMS_ENABLED]: false, [FeatureFlags.REWARDS_COOLDOWN]: false, + [FeatureFlags.SDK_MIGRATION_SHADOWING]: false, [FeatureFlags.USE_SDK_TIPS]: false, [FeatureFlags.USE_SDK_REWARDS]: false, [FeatureFlags.DISCOVERY_TIP_REACTIONS]: false diff --git a/packages/common/src/store/effects.ts b/packages/common/src/store/effects.ts index c0b9d7d2fbd..7e31387d7b8 100644 --- a/packages/common/src/store/effects.ts +++ b/packages/common/src/store/effects.ts @@ -1,5 +1,17 @@ -import { GetContextEffect } from 'redux-saga/effects' -import { getContext as getContextBase, SagaGenerator } from 'typed-redux-saga' +import { AllEffect, CallEffect, GetContextEffect } from 'redux-saga/effects' +import { + all, + call, + getContext as getContextBase, + SagaGenerator +} from 'typed-redux-saga' + +import { ErrorLevel } from '~/models/ErrorReporting' +import { FeatureFlags } from '~/services' +import { + compareSDKResponse, + SDKMigrationFailedError +} from '~/utils/sdkMigrationUtils' import { CommonStoreContext } from './storeContext' @@ -7,3 +19,69 @@ export const getContext = ( prop: Prop ): SagaGenerator => getContextBase(prop) + +/** Helper generator that returns a fully-awaited AudiusSDK instance */ +export function* getSDK() { + const audiusSdk = yield* getContext('audiusSdk') + return yield* call(audiusSdk) +} + +/** This effect is used to shadow a migration without affecting the return value. + * It will run two effects in parallel to fetch the legacy and migrated responses, + * compare the results, log the diff, and then return the legacy value. Errors thrown + * by the effect for the migrated response will be caught to avoid bugs in the migrated + * code from causing errors. + */ +export function* checkSDKMigration({ + legacy: legacyCall, + migrated: migratedCall, + endpointName +}: { + legacy: SagaGenerator> + migrated: SagaGenerator> + endpointName: string +}) { + const getFeatureEnabled = yield* getContext('getFeatureEnabled') + const reportToSentry = yield* getContext('reportToSentry') + + if (!getFeatureEnabled(FeatureFlags.SDK_MIGRATION_SHADOWING)) { + return yield* legacyCall + } + + const [legacy, migrated] = yield* all([ + legacyCall, + call(function* settle() { + try { + return yield* migratedCall + } catch (e) { + return e instanceof Error ? e : new Error(`${e}`) + } + }) + ]) as SagaGenerator>> + + try { + compareSDKResponse({ legacy, migrated }, endpointName) + } catch (e) { + const error = + e instanceof SDKMigrationFailedError + ? e + : new SDKMigrationFailedError({ + endpointName, + innerMessage: `Unknown error: ${e}`, + legacyValue: legacy, + migratedValue: migrated + }) + console.warn('SDK Migration failed', error) + yield* call(reportToSentry, { + error, + level: ErrorLevel.Warning, + additionalInfo: { + diff: JSON.stringify(error.diff, null, 2), + legacyValue: JSON.stringify(error.legacyValue, null, 2), + migratedValue: JSON.stringify(error.migratedValue, null, 2) + }, + tags: { endpointName: error.endpointName } + }) + } + return legacy +} diff --git a/packages/common/src/store/ui/related-artists/sagas.ts b/packages/common/src/store/ui/related-artists/sagas.ts index 152c06478b1..e8e5d6702f7 100644 --- a/packages/common/src/store/ui/related-artists/sagas.ts +++ b/packages/common/src/store/ui/related-artists/sagas.ts @@ -2,11 +2,12 @@ import { PayloadAction } from '@reduxjs/toolkit' import { shuffle } from 'lodash' import { call, put, select, takeEvery } from 'typed-redux-saga' -import { ID, UserMetadata } from '~/models' +import { Id } from '~/api' +import { ID, UserMetadata, userMetadataListFromSDK } from '~/models' import { DoubleKeys } from '~/services/remote-config' import { accountSelectors } from '~/store/account' import { processAndCacheUsers } from '~/store/cache' -import { getContext } from '~/store/effects' +import { checkSDKMigration, getContext, getSDK } from '~/store/effects' import { waitForRead } from '~/utils/sagaHelpers' import { removeNullable } from '~/utils/typeUtils' @@ -36,7 +37,7 @@ export function* fetchRelatedArtists(action: PayloadAction<{ artistId: ID }>) { let suggestedFollows = relatedArtists .filter((user) => !user.does_current_user_follow) .slice(0, 5) - if (suggestedFollows.length === 0) { + if (suggestedFollows.length !== 0) { const showTopArtistRecommendationsPercent = remoteConfigInstance.getRemoteVar( DoubleKeys.SHOW_ARTIST_RECOMMENDATIONS_FALLBACK_PERCENT @@ -66,11 +67,28 @@ export function* fetchRelatedArtists(action: PayloadAction<{ artistId: ID }>) { function* fetchTopArtists() { yield* waitForRead() const apiClient = yield* getContext('apiClient') + const currentUserId = yield* select(getUserId) - const topArtists = yield* call([apiClient, apiClient.getTopArtists], { - currentUserId, - limit: 50 + + const topArtists = yield* checkSDKMigration({ + endpointName: 'getTopArtists', + legacy: call([apiClient, apiClient.getTopArtists], { + currentUserId, + limit: 50 + }), + migrated: call(function* () { + const sdk = yield* getSDK() + const { data } = yield* call( + [sdk.full.users, sdk.full.users.getTopUsers], + { + limit: 50, + userId: Id.parse(currentUserId) + } + ) + return userMetadataListFromSDK(data) + }) }) + const filteredArtists = topArtists.filter( (user) => !user.does_current_user_follow && !user.is_deactivated ) diff --git a/packages/common/src/utils/index.ts b/packages/common/src/utils/index.ts index c13f7c0ec70..054bbcc17b1 100644 --- a/packages/common/src/utils/index.ts +++ b/packages/common/src/utils/index.ts @@ -40,3 +40,4 @@ export * from './email' export * from './commonPasswordCheck' export * from './restrictedHandles' export * from './contentTypeUtils' +export * from './sdkMigrationUtils' diff --git a/packages/common/src/utils/sdkMigrationUtils.ts b/packages/common/src/utils/sdkMigrationUtils.ts new file mode 100644 index 00000000000..ba8aa013352 --- /dev/null +++ b/packages/common/src/utils/sdkMigrationUtils.ts @@ -0,0 +1,170 @@ +import { detailedDiff } from 'deep-object-diff' +import { isEmpty } from 'lodash' + +import { ErrorLevel, ReportToSentryArgs } from '~/models/ErrorReporting' +import { FeatureFlags, RemoteConfigInstance } from '~/services/remote-config' + +export type CheckSDKMigrationArgs = { + legacy: T + migrated?: T | Error +} + +export class SDKMigrationFailedError extends Error { + public endpointName: string + public innerMessage: string + public legacyValue: unknown + public migratedValue: unknown + public diff?: object + + constructor({ + endpointName, + innerMessage, + legacyValue, + migratedValue, + diff + }: { + endpointName: string + innerMessage: string + legacyValue?: unknown + migratedValue?: unknown + diff?: object + }) { + super(`Diff ${endpointName} failed: ${innerMessage}`) + this.name = 'SDKMigrationFailedError' + this.endpointName = endpointName + this.innerMessage = innerMessage + this.legacyValue = legacyValue + this.migratedValue = migratedValue + this.diff = diff + } +} + +/** Compares a legacy and migrated response, which must be the same shape. For + * literal values, will do a strict equals. For objects, will do a deep diff. + * Throws `SDKMigrationFailedError` if there is a difference between the two responses. + */ +export const compareSDKResponse = ( + { legacy, migrated }: CheckSDKMigrationArgs, + endpointName: string +) => { + // Migrated is an error, skip the diff + if (migrated instanceof Error) { + throw new SDKMigrationFailedError({ + endpointName, + innerMessage: 'Migrated response was error', + legacyValue: legacy, + migratedValue: migrated + }) + } + // Both object-like, perform deep diff + if (typeof legacy === 'object' && typeof migrated === 'object') { + const diff = detailedDiff(legacy, migrated) + if ( + !isEmpty(diff.added) || + !isEmpty(diff.deleted) || + !isEmpty(diff.updated) + ) { + throw new SDKMigrationFailedError({ + diff, + endpointName, + innerMessage: 'Legacy and migrated values differ', + legacyValue: legacy, + migratedValue: migrated + }) + } + } + // Not object like, perform strict equals + else if (legacy !== migrated) { + throw new SDKMigrationFailedError({ + endpointName, + innerMessage: 'Legacy and migrated values not strictly equal', + legacyValue: legacy, + migratedValue: migrated + }) + } + console.debug(`SDK Migration succeeded for ${endpointName}`) +} + +const safeAwait = async (promiseOrFn: Promise | (() => Promise)) => { + try { + return await (typeof promiseOrFn === 'function' + ? promiseOrFn() + : promiseOrFn) + } catch (e) { + return e instanceof Error ? e : new Error(`${e}`) + } +} + +export type SDKMigrationChecker = (config: { + legacy: Promise | (() => Promise) + migrated: Promise | (() => Promise) + endpointName: string +}) => Promise + +export const createMigrationChecker = ({ + remoteConfigInstance, + reportToSentry +}: { + remoteConfigInstance: RemoteConfigInstance + reportToSentry: (args: ReportToSentryArgs) => void +}): SDKMigrationChecker => { + /** This helper is used to shadow a migration without affecting the return value. + * It will run two calls in parallel to fetch the legacy and migrated responses, + * compare the results, log the diff, and then return the legacy value. Errors thrown + * by the call for the migrated response will be caught to avoid bugs in the migrated + * code from causing errors. + */ + const checkSDKMigration = async ({ + legacy: legacyCall, + migrated: migratedCall, + endpointName + }: { + legacy: Promise | (() => Promise) + migrated: Promise | (() => Promise) + endpointName: string + }) => { + const legacyPromise = + typeof legacyCall === 'function' ? legacyCall() : legacyCall + if ( + !remoteConfigInstance.getFeatureEnabled( + FeatureFlags.SDK_MIGRATION_SHADOWING + ) + ) { + return legacyPromise + } + + const [legacy, migrated] = await Promise.all([ + legacyPromise, + safeAwait(migratedCall) + ]) + + try { + compareSDKResponse({ legacy, migrated }, endpointName) + } catch (e) { + const error = + e instanceof SDKMigrationFailedError + ? e + : new SDKMigrationFailedError({ + endpointName, + innerMessage: `Unknown error: ${e}`, + legacyValue: legacy, + migratedValue: migrated + }) + console.warn('SDK Migration failed', error) + reportToSentry({ + error, + level: ErrorLevel.Warning, + additionalInfo: { + diff: JSON.stringify(error.diff, null, 2), + legacyValue: JSON.stringify(error.legacyValue, null, 2), + migratedValue: JSON.stringify(error.migratedValue, null, 2) + }, + tags: { endpointName: error.endpointName } + }) + } + + return legacy + } + + return checkSDKMigration +} diff --git a/packages/mobile/src/app/AudiusQueryProvider.tsx b/packages/mobile/src/app/AudiusQueryProvider.tsx index 1e017278c71..83a863dac25 100644 --- a/packages/mobile/src/app/AudiusQueryProvider.tsx +++ b/packages/mobile/src/app/AudiusQueryProvider.tsx @@ -1,6 +1,7 @@ import type { ReactNode } from 'react' import { AudiusQueryContext } from '@audius/common/audius-query' +import { createMigrationChecker } from '@audius/common/utils' import { env } from 'app/env' import { apiClient } from 'app/services/audius-api-client' @@ -14,10 +15,16 @@ type AudiusQueryProviderProps = { children: ReactNode } +const checkSDKMigration = createMigrationChecker({ + remoteConfigInstance, + reportToSentry +}) + export const audiusQueryContext = { apiClient, audiusBackend: audiusBackendInstance, audiusSdk, + checkSDKMigration, dispatch: store.dispatch, reportToSentry, env, diff --git a/packages/mobile/src/screens/notifications-screen/Notifications/USDCPurchaseBuyerNotification.tsx b/packages/mobile/src/screens/notifications-screen/Notifications/USDCPurchaseBuyerNotification.tsx index 45a787da240..bdc1f1b5f10 100644 --- a/packages/mobile/src/screens/notifications-screen/Notifications/USDCPurchaseBuyerNotification.tsx +++ b/packages/mobile/src/screens/notifications-screen/Notifications/USDCPurchaseBuyerNotification.tsx @@ -71,7 +71,7 @@ export const USDCPurchaseBuyerNotification = ({ ) return { shareText: content ? shareText : '', analytics } }, - [content] + [content, entityType] ) const handlePress = useCallback(() => { diff --git a/packages/mobile/src/screens/notifications-screen/Notifications/USDCPurchaseSellerNotification.tsx b/packages/mobile/src/screens/notifications-screen/Notifications/USDCPurchaseSellerNotification.tsx index 29b4573f7bb..d1127580b51 100644 --- a/packages/mobile/src/screens/notifications-screen/Notifications/USDCPurchaseSellerNotification.tsx +++ b/packages/mobile/src/screens/notifications-screen/Notifications/USDCPurchaseSellerNotification.tsx @@ -10,12 +10,12 @@ import type { import { notificationsSelectors } from '@audius/common/store' import { stringUSDCToBN, formatUSDCWeiToUSDString } from '@audius/common/utils' import type { Nullable } from '@audius/common/utils' +import { capitalize } from 'lodash' import { useSelector } from 'react-redux' import { IconCart } from '@audius/harmony-native' import { useNotificationNavigation } from 'app/hooks/useNotificationNavigation' -import { capitalize } from 'lodash' import { EntityLink, NotificationHeader, diff --git a/packages/web/src/app/AudiusQueryProvider.tsx b/packages/web/src/app/AudiusQueryProvider.tsx index 12f86517d46..c615ede4f2e 100644 --- a/packages/web/src/app/AudiusQueryProvider.tsx +++ b/packages/web/src/app/AudiusQueryProvider.tsx @@ -1,6 +1,7 @@ import { ReactNode } from 'react' import { AudiusQueryContext } from '@audius/common/audius-query' +import { createMigrationChecker } from '@audius/common/utils' import { useDispatch } from 'react-redux' import { apiClient } from 'services/audius-api-client' @@ -14,6 +15,11 @@ type AudiusQueryProviderProps = { children: ReactNode } +const checkSDKMigration = createMigrationChecker({ + remoteConfigInstance, + reportToSentry +}) + export const AudiusQueryProvider = (props: AudiusQueryProviderProps) => { const { children } = props const dispatch = useDispatch() @@ -23,6 +29,7 @@ export const AudiusQueryProvider = (props: AudiusQueryProviderProps) => { apiClient, audiusBackend: audiusBackendInstance, audiusSdk, + checkSDKMigration, dispatch, reportToSentry, env,