From 0d610336e07f4ce314196e9fae947c83fe6d96d0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Apr 2026 22:06:41 +0000 Subject: [PATCH 1/2] Auto-follow artist when purchasing their artist coin When a user successfully buys an artist coin via the Jupiter swap flow, look up the coin's owner and follow that artist if the user doesn't already follow them. Tagged as a new FollowSource.ARTIST_COIN_PURCHASE for analytics. https://claude.ai/code/session_01E5VEqdyb3VyG6cicnNRzgd --- .../src/api/tan-query/jupiter/useSwapCoins.ts | 82 +++++++++++++++++++ packages/common/src/models/Analytics.ts | 3 +- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/packages/common/src/api/tan-query/jupiter/useSwapCoins.ts b/packages/common/src/api/tan-query/jupiter/useSwapCoins.ts index 857007ea29a..a82f64ac25e 100644 --- a/packages/common/src/api/tan-query/jupiter/useSwapCoins.ts +++ b/packages/common/src/api/tan-query/jupiter/useSwapCoins.ts @@ -1,5 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query' +import { useDispatch } from 'react-redux' +import { coinFromSdk } from '~/adapters/coin' import { getUserCoinQueryKey, getUserQueryKey, @@ -12,10 +14,15 @@ import { import { QUERY_KEYS } from '~/api/tan-query/queryKeys' import type { QueryContextType } from '~/api/tan-query/utils/QueryContext' import { Feature } from '~/models' +import { FollowSource } from '~/models/Analytics' import type { User } from '~/models/User' import { JupiterQuoteResult } from '~/services/Jupiter' +import { NON_ARTIST_COIN_MINTS } from '~/store/ui/shared/tokenConstants' +import { getArtistCoinQueryFn } from '../coins/useArtistCoin' import { useTradeableCoins } from '../coins/useTradeableCoins' +import { useFollowUser } from '../users/useFollowUser' +import { entityCacheOptions } from '../utils/entityCacheOptions' import { SwapOrchestrator } from './orchestrator' import { @@ -213,6 +220,70 @@ export const optimisticallyUpdateSwapBalances = ( }) } +/** + * Auto-follows the owning artist of a coin after a successful purchase. + * Skips if the output mint is not an artist coin, the user already follows + * the artist, or the coin's owner can't be resolved. + */ +const autoFollowArtistOnCoinPurchase = async ({ + outputMint, + queryClient, + audiusSdk, + dispatch, + followUser, + reportToSentry +}: { + outputMint: string + queryClient: ReturnType + audiusSdk: QueryContextType['audiusSdk'] + dispatch: ReturnType + followUser: ReturnType['mutate'] + reportToSentry: QueryContextType['reportToSentry'] +}) => { + if (!outputMint || NON_ARTIST_COIN_MINTS.includes(outputMint)) { + return + } + + try { + const coin = await queryClient.fetchQuery({ + queryKey: getArtistCoinQueryKey(outputMint), + queryFn: async () => { + const sdk = await audiusSdk() + const rawCoin = await getArtistCoinQueryFn( + outputMint, + queryClient, + sdk, + dispatch + ) + return coinFromSdk(rawCoin) + }, + ...entityCacheOptions + }) + + if (!coin?.ownerId) { + return + } + + // Skip if the current user already follows the artist + const artistUser = queryClient.getQueryData(getUserQueryKey(coin.ownerId)) + if (artistUser?.does_current_user_follow) { + return + } + + followUser({ + followeeUserId: coin.ownerId, + source: FollowSource.ARTIST_COIN_PURCHASE + }) + } catch (error) { + reportToSentry({ + name: 'AutoFollowArtistOnCoinPurchaseError', + error: error as Error, + feature: Feature.TanQuery, + additionalInfo: { outputMint } + }) + } +} + /** * Hook for executing coin swaps using Jupiter. * Swaps any supported SPL token (or SOL) for another. @@ -223,6 +294,8 @@ export const useSwapCoins = () => { useQueryContext() const { data: user } = useCurrentAccountUser() const { coins } = useTradeableCoins() + const dispatch = useDispatch() + const { mutate: followUser } = useFollowUser() return useMutation({ mutationFn: async (params): Promise => { @@ -304,6 +377,15 @@ export const useSwapCoins = () => { }, onSuccess: (result, params) => { optimisticallyUpdateSwapBalances(params, result, queryClient, user, env) + // Auto-follow the artist whose coin was just purchased + autoFollowArtistOnCoinPurchase({ + outputMint: params.outputMint, + queryClient, + audiusSdk, + dispatch, + followUser, + reportToSentry + }) }, onMutate: () => { return { status: SwapStatus.SENDING_TRANSACTION } diff --git a/packages/common/src/models/Analytics.ts b/packages/common/src/models/Analytics.ts index 5588bc3b1e2..6f0a3c8b89c 100644 --- a/packages/common/src/models/Analytics.ts +++ b/packages/common/src/models/Analytics.ts @@ -969,7 +969,8 @@ export enum FollowSource { EMPTY_FEED = 'empty feed', HOW_TO_UNLOCK_TRACK_PAGE = 'how to unlock track page', HOW_TO_UNLOCK_MODAL = 'how to unlock modal', - SIGN_UP = 'sign up' + SIGN_UP = 'sign up', + ARTIST_COIN_PURCHASE = 'artist coin purchase' } type Share = { From 2e53b84857bc4fcfceb6ed5eeca5a48636219fe0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Apr 2026 22:09:43 +0000 Subject: [PATCH 2/2] Add changeset for auto-follow artist on coin purchase https://claude.ai/code/session_01E5VEqdyb3VyG6cicnNRzgd --- .changeset/auto-follow-artist-coin-purchase.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/auto-follow-artist-coin-purchase.md diff --git a/.changeset/auto-follow-artist-coin-purchase.md b/.changeset/auto-follow-artist-coin-purchase.md new file mode 100644 index 00000000000..9f151cb1c8e --- /dev/null +++ b/.changeset/auto-follow-artist-coin-purchase.md @@ -0,0 +1,7 @@ +--- +"@audius/common": patch +"@audius/mobile": patch +"@audius/web": patch +--- + +Automatically follow an artist when a user successfully purchases their artist coin