From 20132791c887f707e6d4a942499978fe7b484a5d Mon Sep 17 00:00:00 2001 From: Saliou Diallo Date: Tue, 6 Feb 2024 07:30:31 -0500 Subject: [PATCH 1/6] Update nft gated signature response in dn --- .../queries/get_nft_gated_track_signatures.py | 80 +++++++++++++++---- .../src/queries/get_track_signature.py | 17 +--- 2 files changed, 69 insertions(+), 28 deletions(-) diff --git a/packages/discovery-provider/src/queries/get_nft_gated_track_signatures.py b/packages/discovery-provider/src/queries/get_nft_gated_track_signatures.py index ee49f8cd244..c5fd215cc21 100644 --- a/packages/discovery-provider/src/queries/get_nft_gated_track_signatures.py +++ b/packages/discovery-provider/src/queries/get_nft_gated_track_signatures.py @@ -114,6 +114,7 @@ def _get_eth_nft_gated_track_signatures( ): track_signature_map = {} track_cid_to_id_map = {} + track_id_to_original_cid_map = {} user_eth_wallets = list( map(Web3.to_checksum_address, eth_associated_wallets + [user_wallet]) @@ -137,6 +138,7 @@ def _get_eth_nft_gated_track_signatures( ) erc721_collection_track_map[contract_address].append(track.track_cid) track_cid_to_id_map[track.track_cid] = track.track_id + track_id_to_original_cid_map[track.track_id] = track.orig_file_cid erc1155_gated_tracks = list( filter( @@ -164,6 +166,7 @@ def _get_eth_nft_gated_track_signatures( contract_address ].union(track_token_id_set) track_cid_to_id_map[track.track_cid] = track.track_id + track_id_to_original_cid_map[track.track_id] = track.orig_file_cid with concurrent.futures.ThreadPoolExecutor() as executor: # Check ownership of nfts from erc721 collections from given contract addresses, @@ -184,9 +187,8 @@ def _get_eth_nft_gated_track_signatures( if future.result(): for track_cid in erc721_collection_track_map[contract_address]: track_id = track_cid_to_id_map[track_cid] - track_signature_map[ - track_id - ] = get_gated_content_signature_for_user_wallet( + original_cid = track_id_to_original_cid_map[track_id] + mp3_signature = get_gated_content_signature_for_user_wallet( { "track_id": track_id, "track_cid": track_cid, @@ -196,6 +198,22 @@ def _get_eth_nft_gated_track_signatures( "is_gated": True, } ) + original_signature = ( + get_gated_content_signature_for_user_wallet( + { + "track_id": track_id, + "track_cid": original_cid, + "type": "track", + "user_wallet": user_wallet, + "user_id": user_id, + "is_gated": True, + } + ) + ) + track_signature_map[track_id] = { + "mp3": mp3_signature, + "original": original_signature, + } except Exception as e: logger.error( f"Could not get future result for erc721 contract_address {contract_address}. Error: {e}" @@ -222,9 +240,8 @@ def _get_eth_nft_gated_track_signatures( if future.result(): for track_cid in erc1155_collection_track_map[contract_address]: track_id = track_cid_to_id_map[track_cid] - track_signature_map[ - track_id - ] = get_gated_content_signature_for_user_wallet( + original_cid = track_id_to_original_cid_map[track_id] + mp3_signature = get_gated_content_signature_for_user_wallet( { "track_id": track_id, "track_cid": track_cid, @@ -234,6 +251,22 @@ def _get_eth_nft_gated_track_signatures( "is_gated": True, } ) + original_signature = ( + get_gated_content_signature_for_user_wallet( + { + "track_id": track_id, + "track_cid": original_cid, + "type": "track", + "user_wallet": user_wallet, + "user_id": user_id, + "is_gated": True, + } + ) + ) + track_signature_map[track_id] = { + "mp3": mp3_signature, + "original": original_signature, + } except Exception as e: logger.error( f"Could not get future result for erc1155 contract_address {contract_address}. Error: {e}" @@ -394,6 +427,7 @@ def _get_sol_nft_gated_track_signatures( ): track_signature_map = {} track_cid_to_id_map = {} + track_id_to_original_cid_map = {} # Build a map of collection mint address -> track ids # so that only one chain call will be made for gated tracks @@ -403,6 +437,7 @@ def _get_sol_nft_gated_track_signatures( collection_mint_address = track.stream_conditions["nft_collection"]["address"] # type: ignore collection_track_map[collection_mint_address].append(track.track_cid) track_cid_to_id_map[track.track_cid] = track.track_id + track_id_to_original_cid_map[track.track_id] = track.orig_file_cid with concurrent.futures.ThreadPoolExecutor() as executor: # Check ownership of nfts from collections from given collection mint addresses, @@ -425,9 +460,8 @@ def _get_sol_nft_gated_track_signatures( if future.result(): for track_cid in collection_track_map[collection_mint_address]: track_id = track_cid_to_id_map[track_cid] - track_signature_map[ - track_id - ] = get_gated_content_signature_for_user_wallet( + original_cid = track_id_to_original_cid_map[track_id] + mp3_signature = get_gated_content_signature_for_user_wallet( { "track_id": track_id, "track_cid": cast(str, track_cid), @@ -437,6 +471,22 @@ def _get_sol_nft_gated_track_signatures( "is_gated": True, } ) + original_signature = ( + get_gated_content_signature_for_user_wallet( + { + "track_id": track_id, + "track_cid": cast(str, original_cid), + "type": "track", + "user_wallet": user_wallet, + "user_id": user_id, + "is_gated": True, + } + ) + ) + track_signature_map[track_id] = { + "mp3": mp3_signature, + "original": original_signature, + } except Exception as e: logger.error( f"Could not get future result for collection_mint_address {collection_mint_address}. Error: {e}" @@ -477,14 +527,14 @@ def get_nft_gated_track_signatures( nft_gated_tracks, ) ) - eth_nft_gated_track_signatures = _get_eth_nft_gated_track_signatures( + eth_nft_gated_track_signature_maps = _get_eth_nft_gated_track_signatures( user_wallet=user_wallet, eth_associated_wallets=associated_wallets["eth"], tracks=eth_nft_gated_tracks, track_token_id_map=track_token_id_map, user_id=user_id, ) - sol_nft_gated_track_signatures = _get_sol_nft_gated_track_signatures( + sol_nft_gated_track_signature_maps = _get_sol_nft_gated_track_signatures( user_wallet=user_wallet, sol_associated_wallets=associated_wallets["sol"], tracks=sol_nft_gated_tracks, @@ -492,10 +542,10 @@ def get_nft_gated_track_signatures( ) result = {} - for track_id, signature in eth_nft_gated_track_signatures.items(): - result[track_id] = signature - for track_id, signature in sol_nft_gated_track_signatures.items(): - result[track_id] = signature + for track_id, signature_map in eth_nft_gated_track_signature_maps.items(): + result[track_id] = signature_map + for track_id, signature_map in sol_nft_gated_track_signature_maps.items(): + result[track_id] = signature_map return result diff --git a/packages/discovery-provider/src/queries/get_track_signature.py b/packages/discovery-provider/src/queries/get_track_signature.py index 018bb084bee..625aa376a64 100644 --- a/packages/discovery-provider/src/queries/get_track_signature.py +++ b/packages/discovery-provider/src/queries/get_track_signature.py @@ -159,19 +159,10 @@ def get_track_download_signature(args: GetTrackDownloadSignature): authed_user_wallet = authed_user["user_wallet"].lower() if signature_user_wallet != authed_user_wallet: return None - # todo: use commented line below once logic is added to pass in correct signature - # for nft gated tracks i.e. for original cid or for mp3 cid. - # for now, we are returning the signature for mp3 cid - # ============== - # return {"signature": signature_obj, "cid": cid, "filename": filename} - # ============== - if not track.get("track_cid"): - return None - return { - "signature": signature_obj, - "cid": track.get("track_cid"), - "filename": filename, - } + # client must pass the correct nft access signature, i.e. + # the one with original cid if the user wants to download the original file, or + # the one with track cid if the user wants to download the transcoded file + return {"signature": signature_obj, "cid": cid, "filename": filename} # build a track instance from the track dict track_entity = Track( From 8b5d6bf178e31eb86509605b00a59c1e4a2046a2 Mon Sep 17 00:00:00 2001 From: Saliou Diallo Date: Tue, 6 Feb 2024 07:30:47 -0500 Subject: [PATCH 2/6] Lint dn --- .../queries/test_get_usdc_transactions_history.py | 10 ++-------- .../integration_tests/tasks/test_index_user_bank.py | 6 +++--- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/discovery-provider/integration_tests/queries/test_get_usdc_transactions_history.py b/packages/discovery-provider/integration_tests/queries/test_get_usdc_transactions_history.py index b4ebc63063c..5c9fdf47060 100644 --- a/packages/discovery-provider/integration_tests/queries/test_get_usdc_transactions_history.py +++ b/packages/discovery-provider/integration_tests/queries/test_get_usdc_transactions_history.py @@ -1,20 +1,14 @@ import pytest from integration_tests.utils import populate_mock_db -from src.models.users.usdc_transactions_history import ( - USDCTransactionMethod, -) +from src.models.users.usdc_transactions_history import USDCTransactionMethod from src.queries.get_usdc_transactions_history import ( GetUSDCTransactionsArgs, GetUSDCTransactionsCountArgs, get_usdc_transactions_history, get_usdc_transactions_history_count, ) - -from src.queries.query_helpers import ( - SortDirection, - TransactionSortMethod, -) +from src.queries.query_helpers import SortDirection, TransactionSortMethod from src.utils.db_session import get_db user1_id = 10 diff --git a/packages/discovery-provider/integration_tests/tasks/test_index_user_bank.py b/packages/discovery-provider/integration_tests/tasks/test_index_user_bank.py index 687e43d0004..1a7223b9223 100644 --- a/packages/discovery-provider/integration_tests/tasks/test_index_user_bank.py +++ b/packages/discovery-provider/integration_tests/tasks/test_index_user_bank.py @@ -8,9 +8,9 @@ EXTERNAL_ACCOUNT_ADDRESS, RECEIVER_ACCOUNT_WAUDIO_ADDRESS, RECIPIENT_USDC_USER_BANK_ADDRESS, + SENDER_ACCOUNT_WAUDIO_ADDRESS, SENDER_ROOT_WALLET_USDC_ACCOUNT_OWNER, SENDER_USDC_USER_BANK_ADDRESS, - SENDER_ACCOUNT_WAUDIO_ADDRESS, mock_failed_track_purchase_tx, mock_invalid_track_purchase_bad_splits_tx, mock_invalid_track_purchase_missing_splits_tx, @@ -20,11 +20,11 @@ mock_valid_create_usdc_token_account_tx, mock_valid_track_purchase_pay_extra_tx, mock_valid_track_purchase_tx, + mock_valid_transfer_prepare_withdrawal_tx, + mock_valid_transfer_withdrawal_tx, mock_valid_transfer_without_purchase_tx, mock_valid_waudio_transfer_between_user_banks, mock_valid_waudio_transfer_from_user_bank_to_external_address, - mock_valid_transfer_prepare_withdrawal_tx, - mock_valid_transfer_withdrawal_tx, ) from integration_tests.utils import populate_mock_db from src.challenges.challenge_event import ChallengeEvent From 766aa81fb3287fdd9d0cfe6374944419578eef37 Mon Sep 17 00:00:00 2001 From: Saliou Diallo Date: Tue, 6 Feb 2024 09:00:46 -0500 Subject: [PATCH 3/6] Update gated content sagas --- packages/common/src/models/Track.ts | 5 + .../src/services/audius-api-client/types.ts | 6 +- .../common/src/store/gated-content/sagas.ts | 194 +++++++++++++----- .../common/src/store/gated-content/slice.ts | 8 +- 4 files changed, 151 insertions(+), 62 deletions(-) diff --git a/packages/common/src/models/Track.ts b/packages/common/src/models/Track.ts index 12f8a692db6..41c48bd51b0 100644 --- a/packages/common/src/models/Track.ts +++ b/packages/common/src/models/Track.ts @@ -146,6 +146,11 @@ export type AccessSignature = { signature: string } +export type NFTAccessSignature = { + mp3: AccessSignature + original: AccessSignature +} + export type EthCollectionMap = { [slug: string]: { name: string diff --git a/packages/common/src/services/audius-api-client/types.ts b/packages/common/src/services/audius-api-client/types.ts index e9f1ca14749..cf5d8101ad2 100644 --- a/packages/common/src/services/audius-api-client/types.ts +++ b/packages/common/src/services/audius-api-client/types.ts @@ -16,9 +16,9 @@ import { Supporting, UserTip, AccessConditions, - AccessSignature, ID, - AccessPermissions + AccessPermissions, + NFTAccessSignature } from '../../models' import { License, Nullable } from '../../utils' @@ -289,5 +289,5 @@ export type GetTipsResponse = Omit & { } export type GetNFTGatedTrackSignaturesResponse = { - [id: ID]: AccessSignature + [id: ID]: NFTAccessSignature } diff --git a/packages/common/src/store/gated-content/sagas.ts b/packages/common/src/store/gated-content/sagas.ts index ddd6f929e15..23d0bc0bb66 100644 --- a/packages/common/src/store/gated-content/sagas.ts +++ b/packages/common/src/store/gated-content/sagas.ts @@ -14,13 +14,13 @@ import { ID, Kind, Name, - AccessSignature, GatedTrackStatus, Track, isContentCollectibleGated, isContentFollowGated, isContentTipGated, - isContentUSDCPurchaseGated + isContentUSDCPurchaseGated, + NFTAccessSignature } from '~/models' import { User } from '~/models/User' import { IntKeys } from '~/services/remote-config' @@ -132,15 +132,11 @@ function* getTokenIdMap({ // skip this track entry if it is not gated on an nft collection const { - is_stream_gated: isStreamGated, - stream_conditions: streamConditions + stream_conditions: streamConditions, + download_conditions: downloadConditions } = tracks[trackId] - if ( - !isStreamGated || - !streamConditions || - !isContentCollectibleGated(streamConditions) - ) - return + const conditions = streamConditions ?? downloadConditions + if (!conditions || !isContentCollectibleGated(conditions)) return // Set the token ids for ERC1155 nfts as the balanceOf contract method // which will be used to determine ownership requires the user's @@ -148,13 +144,10 @@ function* getTokenIdMap({ // todo: fix the string nft_collection to be an object // temporarily parse it into object here for now - let { nft_collection: nftCollection } = streamConditions + let { nft_collection: nftCollection } = conditions if (typeof nftCollection === 'string') { nftCollection = JSON.parse( - (streamConditions.nft_collection as unknown as string).replaceAll( - "'", - '"' - ) + (nftCollection as unknown as string).replaceAll("'", '"') ) } @@ -197,26 +190,41 @@ function* handleSpecialAccessTrackSubscriptions(tracks: Track[]) { track_id: trackId, owner_id: ownerId, stream_conditions: streamConditions, - access, - permalink + download_conditions: downloadConditions, + access } = track // Ignore updates that are nft access signature only, // i.e. make sure the above properties exist before proceeding. - if (!trackId || !ownerId || !streamConditions || !permalink) { + if (!trackId || !ownerId || !(streamConditions || downloadConditions)) { return false } - const hasNoStreamAccess = !access?.stream - const isFollowGated = isContentFollowGated(streamConditions) - const isTipGated = isContentTipGated(streamConditions) - const shouldHaveStreamAccess = - (isFollowGated && followeeIds.includes(ownerId)) || - (isTipGated && tippedUserIds.includes(ownerId)) - - if (hasNoStreamAccess && shouldHaveStreamAccess) { - statusMap[trackId] = 'UNLOCKING' - return true + if (streamConditions) { + const hasNoStreamAccess = !access?.stream + const isFollowGated = isContentFollowGated(streamConditions) + const isTipGated = isContentTipGated(streamConditions) + const shouldHaveStreamAccess = + (isFollowGated && followeeIds.includes(ownerId)) || + (isTipGated && tippedUserIds.includes(ownerId)) + + if (hasNoStreamAccess && shouldHaveStreamAccess) { + statusMap[trackId] = 'UNLOCKING' + // todo: if necessary, update some ui status to show that the track download is unlocking + return true + } + } else if (downloadConditions) { + const hasNoDownloadAccess = !access?.download + const isFollowGated = isContentFollowGated(downloadConditions) + const isTipGated = isContentTipGated(downloadConditions) + const shouldHaveDownloadAccess = + (isFollowGated && followeeIds.includes(ownerId)) || + (isTipGated && tippedUserIds.includes(ownerId)) + + if (hasNoDownloadAccess && shouldHaveDownloadAccess) { + // todo: if necessary, update some ui status to show that the track download is unlocking + return true + } } return false }) @@ -257,7 +265,7 @@ function* updateCollectibleGatedTracks(trackMap: { [id: ID]: string[] }) { let numTrackIdsWithSignature = 0 const nftGatedTrackSignatureMap: { - [id: ID]: Nullable + [id: ID]: Nullable } = { ...nftGatedTrackSignatureResponse } // Set null for tracks for which signatures did not get returned // to signal that an attempt was made but the user does not have access. @@ -303,12 +311,12 @@ function* updateCollectibleGatedTracks(trackMap: { [id: ID]: string[] }) { /** * This function runs when new tracks have been added to the cache or when eth or sol nfts are fetched. * It does a bunch of things (getting gradually larger and should now be broken up): - * - Updates the store with new stream signatures. + * - Updates the store with new stream and download signatures. * - Skips tracks whose signatures have already been previously obtained. * - Handles newly loading special access tracks that should have a signature but do not yet. * - Builds a map of nft-gated track ids (and potentially their respective nft token ids) to * make a request to DN which confirms that user owns the corresponding nft collections by - * returning corresponding stream signatures. + * returning corresponding stream and download signatures. */ function* updateGatedTrackAccess( action: @@ -369,14 +377,20 @@ function* updateGatedTrackAccess( }) const updatedNftAccessSignatureMap: { - [id: ID]: Nullable + [id: ID]: Nullable } = {} Object.keys(allTracks).forEach((trackId) => { const id = parseInt(trackId) if (skipped.has(id)) return - const { stream_conditions: streamConditions } = allTracks[trackId] - if (streamConditions?.nft_collection && !trackMap[id]) { + const { + stream_conditions: streamConditions, + download_conditions: downloadConditions + } = allTracks[trackId] + const isCollectibleGated = + isContentCollectibleGated(streamConditions) || + isContentCollectibleGated(downloadConditions) + if (isCollectibleGated && !trackMap[id]) { // Set null for collectible gated track signatures as // the user does not have nfts for those collections // and therefore does not have access. @@ -410,12 +424,24 @@ export function* pollGatedTrack({ remoteConfigInstance.getRemoteVar(IntKeys.GATED_TRACK_POLL_INTERVAL_MS) ?? DEFAULT_GATED_TRACK_POLL_INTERVAL_MS + // get initial track metadata to determine whether we are polling for stream or download access + const cachedTracks = yield* select(getTracks, { + ids: [trackId] + }) + const initialTrack = cachedTracks[trackId] + const initiallyHasNoStreamAccess = !initialTrack?.access.stream + const initiallyHasNoDownloadAccess = !initialTrack?.access.download + + // poll for access until it is granted while (true) { const track = yield* call([apiClient, 'getTrack'], { id: trackId, currentUserId }) - if (track?.access?.stream) { + const currentlyHasStreamAccess = !!track?.access?.stream + const currentlyHasDownloadAccess = !!track?.access?.download + + if (initiallyHasNoStreamAccess && currentlyHasStreamAccess) { yield* put( cacheActions.update(Kind.TRACKS, [ { @@ -427,6 +453,7 @@ export function* pollGatedTrack({ ]) ) yield* put(updateGatedTrackStatus({ trackId, status: 'UNLOCKED' })) + // todo: if necessary, update some ui status to show that the track download is unlocked yield* put(removeFolloweeId({ id: track.owner_id })) yield* put(removeTippedUserId({ id: track.owner_id })) @@ -453,7 +480,45 @@ export function* pollGatedTrack({ } }) } + break + } else if (initiallyHasNoDownloadAccess && currentlyHasDownloadAccess) { + yield* put( + cacheActions.update(Kind.TRACKS, [ + { + id: trackId, + metadata: { + access: track.access + } + } + ]) + ) + // todo: if necessary, update some ui status to show that the track download is unlocked + yield* put(removeFolloweeId({ id: track.owner_id })) + yield* put(removeTippedUserId({ id: track.owner_id })) + // Show confetti if track is unlocked from the how to unlock section on track page or modal + if (isSourceTrack) { + yield* put(showConfetti()) + } + + if (!track.download_conditions) { + return + } + const eventName = isContentUSDCPurchaseGated(track.download_conditions) + ? Name.USDC_PURCHASE_GATED_TRACK_UNLOCKED + : isContentFollowGated(track.download_conditions) + ? Name.FOLLOW_GATED_TRACK_UNLOCKED + : isContentTipGated(track.download_conditions) + ? Name.TIP_GATED_TRACK_UNLOCKED + : null + if (eventName) { + analytics.track({ + eventName, + properties: { + trackId + } + }) + } break } yield* delay(frequency) @@ -463,8 +528,8 @@ export function* pollGatedTrack({ /** * 1. Get follow or tip gated tracks of user * 2. Set those track statuses to 'UNLOCKING' - * 3. Poll for stream signatures for those tracks - * 4. When the signatures are returned, set those track statuses as 'UNLOCKED' + * 3. Poll for access for those tracks + * 4. When access is returned, set those track statuses as 'UNLOCKED' */ function* updateSpecialAccessTracks( trackOwnerId: ID, @@ -488,14 +553,25 @@ function* updateSpecialAccessTracks( Object.keys(cachedTracks).forEach((trackId) => { const id = parseInt(trackId) - const { owner_id: ownerId, stream_conditions: streamConditions } = - cachedTracks[id] - const isGated = + const { + owner_id: ownerId, + stream_conditions: streamConditions, + download_conditions: downloadConditions + } = cachedTracks[id] + const isTrackStreamGated = gate === 'follow' ? isContentFollowGated(streamConditions) : isContentTipGated(streamConditions) - if (isGated && ownerId === trackOwnerId) { + const isTrackDownloadGated = + gate === 'follow' + ? isContentFollowGated(downloadConditions) + : isContentTipGated(downloadConditions) + if (isTrackStreamGated && ownerId === trackOwnerId) { statusMap[id] = 'UNLOCKING' + // todo: if necessary, update some ui status to show that the track download is unlocking + tracksToPoll.add(id) + } else if (isTrackDownloadGated && ownerId === trackOwnerId) { + // todo: if necessary, update some ui status to show that the track download is unlocking tracksToPoll.add(id) } }) @@ -526,22 +602,30 @@ function* handleUnfollowUser( yield* put(removeFolloweeId({ id: action.userId })) const statusMap: { [id: ID]: GatedTrackStatus } = {} + const revokeAccessMap: { [id: ID]: 'stream' | 'download' } = {} const cachedTracks = yield* select(getTracks, {}) Object.keys(cachedTracks).forEach((trackId) => { const id = parseInt(trackId) - const { owner_id: ownerId, stream_conditions: streamConditions } = - cachedTracks[id] - const isFollowGated = isContentFollowGated(streamConditions) - if (isFollowGated && ownerId === action.userId) { + const { + owner_id: ownerId, + stream_conditions: streamConditions, + download_conditions: downloadConditions + } = cachedTracks[id] + const isStreamFollowGated = isContentFollowGated(streamConditions) + const isDownloadFollowGated = isContentFollowGated(downloadConditions) + if (isStreamFollowGated && ownerId === action.userId) { statusMap[id] = 'LOCKED' + // todo: if necessary, update some ui status to show that the track download is locked + revokeAccessMap[id] = 'stream' + } else if (isDownloadFollowGated && ownerId === action.userId) { + // todo: if necessary, update some ui status to show that the track download is locked + revokeAccessMap[id] = 'download' } }) yield* put(updateGatedTrackStatuses(statusMap)) - - const trackIds = Object.keys(statusMap).map((trackId) => parseInt(trackId)) - yield* put(revokeAccess({ trackIds })) + yield* put(revokeAccess({ revokeAccessMap })) } function* handleFollowUser( @@ -571,16 +655,16 @@ function* handleTipGatedTracks( * no longer accessible by the user. */ function* handleRevokeAccess(action: ReturnType) { - const cachedTracks = yield* select(getTracks, { - ids: action.payload.trackIds - }) - const metadatas = Object.keys(cachedTracks).map((trackId) => { + const { revokeAccessMap } = action.payload + const metadatas = Object.keys(revokeAccessMap).map((trackId) => { + const access = + revokeAccessMap[trackId] === 'stream' + ? { stream: false, download: false } + : { stream: true, download: false } const id = parseInt(trackId) return { id, - metadata: { - access: { stream: false, download: false } - } + metadata: { access } } }) yield* put(cacheActions.update(Kind.TRACKS, metadatas)) diff --git a/packages/common/src/store/gated-content/slice.ts b/packages/common/src/store/gated-content/slice.ts index aded50a4d21..a8b0d855bbe 100644 --- a/packages/common/src/store/gated-content/slice.ts +++ b/packages/common/src/store/gated-content/slice.ts @@ -1,10 +1,10 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' -import { ID, AccessSignature, GatedTrackStatus } from '~/models' +import { ID, GatedTrackStatus, NFTAccessSignature } from '~/models' import { Nullable } from '~/utils' type GatedContentState = { - nftAccessSignatureMap: { [id: ID]: Nullable } + nftAccessSignatureMap: { [id: ID]: Nullable } statusMap: { [id: ID]: GatedTrackStatus } lockedContentId: Nullable followeeIds: ID[] @@ -20,11 +20,11 @@ const initialState: GatedContentState = { } type UpdateNftAccessSignaturesPayload = { - [id: ID]: Nullable + [id: ID]: Nullable } type RevokeAccessPayload = { - trackIds: ID[] + revokeAccessMap: { [id: ID]: 'stream' | 'download' } } type UpdateGatedTrackStatusPayload = { From 34e9daada4fdacfc73e16c89ddd4bf49b4fb2266 Mon Sep 17 00:00:00 2001 From: Saliou Diallo Date: Tue, 6 Feb 2024 09:16:07 -0500 Subject: [PATCH 4/6] Update nft access signature query params --- packages/mobile/src/components/audio/AudioPlayer.tsx | 2 +- .../sagas/offlineQueueSagas/workers/downloadTrackWorker.ts | 2 +- packages/web/src/common/store/player/sagas.ts | 3 ++- packages/web/src/common/store/social/tracks/sagas.ts | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/mobile/src/components/audio/AudioPlayer.tsx b/packages/mobile/src/components/audio/AudioPlayer.tsx index bf6943ff385..9e781791b52 100644 --- a/packages/mobile/src/components/audio/AudioPlayer.tsx +++ b/packages/mobile/src/components/audio/AudioPlayer.tsx @@ -308,7 +308,7 @@ export const AudioPlayer = () => { if (gatedQueryParamsMap[trackId]) { queryParamsMap[trackId] = gatedQueryParamsMap[trackId] } else { - const nftAccessSignature = nftAccessSignatureMap[trackId] + const nftAccessSignature = nftAccessSignatureMap[trackId]?.mp3 ?? null queryParamsMap[trackId] = await getQueryParams({ audiusBackendInstance, nftAccessSignature diff --git a/packages/mobile/src/store/offline-downloads/sagas/offlineQueueSagas/workers/downloadTrackWorker.ts b/packages/mobile/src/store/offline-downloads/sagas/offlineQueueSagas/workers/downloadTrackWorker.ts index 0d46f7d8fd8..ffc2d32efb4 100644 --- a/packages/mobile/src/store/offline-downloads/sagas/offlineQueueSagas/workers/downloadTrackWorker.ts +++ b/packages/mobile/src/store/offline-downloads/sagas/offlineQueueSagas/workers/downloadTrackWorker.ts @@ -172,7 +172,7 @@ function* downloadTrackAudio(track: UserTrackMetadata) { const audiusBackendInstance = yield* getContext('audiusBackendInstance') const apiClient = yield* getContext('apiClient') const nftAccessSignatureMap = yield* select(getNftAccessSignatureMap) - const nftAccessSignature = nftAccessSignatureMap[track_id] + const nftAccessSignature = nftAccessSignatureMap[track_id]?.mp3 ?? null let queryParams: QueryParams = {} queryParams = yield* call(getQueryParams, { audiusBackendInstance, diff --git a/packages/web/src/common/store/player/sagas.ts b/packages/web/src/common/store/player/sagas.ts index 286a05146fd..dc736ad7277 100644 --- a/packages/web/src/common/store/player/sagas.ts +++ b/packages/web/src/common/store/player/sagas.ts @@ -102,7 +102,8 @@ export function* watchPlay() { let queryParams: QueryParams = {} const nftAccessSignatureMap = yield* select(getNftAccessSignatureMap) - const nftAccessSignature = nftAccessSignatureMap[track.track_id] + const nftAccessSignature = + nftAccessSignatureMap[track.track_id]?.mp3 ?? null queryParams = (yield* call(getQueryParams, { audiusBackendInstance, nftAccessSignature diff --git a/packages/web/src/common/store/social/tracks/sagas.ts b/packages/web/src/common/store/social/tracks/sagas.ts index 84abb657ea5..2552b303013 100644 --- a/packages/web/src/common/store/social/tracks/sagas.ts +++ b/packages/web/src/common/store/social/tracks/sagas.ts @@ -658,7 +658,7 @@ function* downloadTrackV1Helper({ const trackId = track.track_id const nftAccessSignatureMap = yield* select(getNftAccessSignatureMap) - const nftAccessSignature = nftAccessSignatureMap[trackId] + const nftAccessSignature = nftAccessSignatureMap[trackId]?.mp3 ?? null queryParams = (yield* call(getQueryParams, { audiusBackendInstance, nftAccessSignature @@ -769,7 +769,7 @@ function* downloadTracks({ let queryParams: QueryParams = {} const nftAccessSignatureMap = yield* select(getNftAccessSignatureMap) - const nftAccessSignature = nftAccessSignatureMap[parentTrackId] + const nftAccessSignature = nftAccessSignatureMap[parentTrackId]?.mp3 ?? null queryParams = (yield* call(getQueryParams, { audiusBackendInstance, nftAccessSignature From 4dd8b2c50f4c989047712ae14fb5de6a04eb0e83 Mon Sep 17 00:00:00 2001 From: Saliou Diallo Date: Wed, 7 Feb 2024 10:18:11 -0500 Subject: [PATCH 5/6] Address PR comments --- .../common/src/store/gated-content/sagas.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/common/src/store/gated-content/sagas.ts b/packages/common/src/store/gated-content/sagas.ts index 23d0bc0bb66..1557f23f0f7 100644 --- a/packages/common/src/store/gated-content/sagas.ts +++ b/packages/common/src/store/gated-content/sagas.ts @@ -210,7 +210,7 @@ function* handleSpecialAccessTrackSubscriptions(tracks: Track[]) { if (hasNoStreamAccess && shouldHaveStreamAccess) { statusMap[trackId] = 'UNLOCKING' - // todo: if necessary, update some ui status to show that the track download is unlocking + // TODO: if necessary, update some ui status to show that the track download is unlocking return true } } else if (downloadConditions) { @@ -222,7 +222,7 @@ function* handleSpecialAccessTrackSubscriptions(tracks: Track[]) { (isTipGated && tippedUserIds.includes(ownerId)) if (hasNoDownloadAccess && shouldHaveDownloadAccess) { - // todo: if necessary, update some ui status to show that the track download is unlocking + // TODO: if necessary, update some ui status to show that the track download is unlocking return true } } @@ -429,8 +429,8 @@ export function* pollGatedTrack({ ids: [trackId] }) const initialTrack = cachedTracks[trackId] - const initiallyHasNoStreamAccess = !initialTrack?.access.stream - const initiallyHasNoDownloadAccess = !initialTrack?.access.download + const initiallyHadNoStreamAccess = !initialTrack?.access.stream + const initiallyHadNoDownloadAccess = !initialTrack?.access.download // poll for access until it is granted while (true) { @@ -441,7 +441,7 @@ export function* pollGatedTrack({ const currentlyHasStreamAccess = !!track?.access?.stream const currentlyHasDownloadAccess = !!track?.access?.download - if (initiallyHasNoStreamAccess && currentlyHasStreamAccess) { + if (initiallyHadNoStreamAccess && currentlyHasStreamAccess) { yield* put( cacheActions.update(Kind.TRACKS, [ { @@ -453,7 +453,7 @@ export function* pollGatedTrack({ ]) ) yield* put(updateGatedTrackStatus({ trackId, status: 'UNLOCKED' })) - // todo: if necessary, update some ui status to show that the track download is unlocked + // TODO: if necessary, update some ui status to show that the track download is unlocked yield* put(removeFolloweeId({ id: track.owner_id })) yield* put(removeTippedUserId({ id: track.owner_id })) @@ -481,7 +481,7 @@ export function* pollGatedTrack({ }) } break - } else if (initiallyHasNoDownloadAccess && currentlyHasDownloadAccess) { + } else if (initiallyHadNoDownloadAccess && currentlyHasDownloadAccess) { yield* put( cacheActions.update(Kind.TRACKS, [ { @@ -492,7 +492,7 @@ export function* pollGatedTrack({ } ]) ) - // todo: if necessary, update some ui status to show that the track download is unlocked + // TODO: if necessary, update some ui status to show that the track download is unlocked yield* put(removeFolloweeId({ id: track.owner_id })) yield* put(removeTippedUserId({ id: track.owner_id })) @@ -568,10 +568,10 @@ function* updateSpecialAccessTracks( : isContentTipGated(downloadConditions) if (isTrackStreamGated && ownerId === trackOwnerId) { statusMap[id] = 'UNLOCKING' - // todo: if necessary, update some ui status to show that the track download is unlocking + // TODO: if necessary, update some ui status to show that the track download is unlocking tracksToPoll.add(id) } else if (isTrackDownloadGated && ownerId === trackOwnerId) { - // todo: if necessary, update some ui status to show that the track download is unlocking + // TODO: if necessary, update some ui status to show that the track download is unlocking tracksToPoll.add(id) } }) @@ -616,10 +616,10 @@ function* handleUnfollowUser( const isDownloadFollowGated = isContentFollowGated(downloadConditions) if (isStreamFollowGated && ownerId === action.userId) { statusMap[id] = 'LOCKED' - // todo: if necessary, update some ui status to show that the track download is locked + // TODO: if necessary, update some ui status to show that the track download is locked revokeAccessMap[id] = 'stream' } else if (isDownloadFollowGated && ownerId === action.userId) { - // todo: if necessary, update some ui status to show that the track download is locked + // TODO: if necessary, update some ui status to show that the track download is locked revokeAccessMap[id] = 'download' } }) From 4b7be8b0767b0b9dabef5a82e9b6aaf01eae568d Mon Sep 17 00:00:00 2001 From: Saliou Diallo Date: Wed, 7 Feb 2024 11:13:25 -0500 Subject: [PATCH 6/6] Pass correct nft access signature based on mp3 vs original download --- packages/web/src/common/store/social/tracks/sagas.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/web/src/common/store/social/tracks/sagas.ts b/packages/web/src/common/store/social/tracks/sagas.ts index 2552b303013..074a9121da3 100644 --- a/packages/web/src/common/store/social/tracks/sagas.ts +++ b/packages/web/src/common/store/social/tracks/sagas.ts @@ -658,7 +658,9 @@ function* downloadTrackV1Helper({ const trackId = track.track_id const nftAccessSignatureMap = yield* select(getNftAccessSignatureMap) - const nftAccessSignature = nftAccessSignatureMap[trackId]?.mp3 ?? null + const nftAccessSignature = original + ? nftAccessSignatureMap[trackId]?.original ?? null + : nftAccessSignatureMap[trackId]?.mp3 ?? null queryParams = (yield* call(getQueryParams, { audiusBackendInstance, nftAccessSignature @@ -769,7 +771,9 @@ function* downloadTracks({ let queryParams: QueryParams = {} const nftAccessSignatureMap = yield* select(getNftAccessSignatureMap) - const nftAccessSignature = nftAccessSignatureMap[parentTrackId]?.mp3 ?? null + const nftAccessSignature = original + ? nftAccessSignatureMap[parentTrackId]?.original ?? null + : nftAccessSignatureMap[parentTrackId]?.mp3 ?? null queryParams = (yield* call(getQueryParams, { audiusBackendInstance, nftAccessSignature