diff --git a/packages/common/src/hooks/purchaseContent/usePayExtraPresets.ts b/packages/common/src/hooks/purchaseContent/usePayExtraPresets.ts index 7fe10640e9b..3dc6e518086 100644 --- a/packages/common/src/hooks/purchaseContent/usePayExtraPresets.ts +++ b/packages/common/src/hooks/purchaseContent/usePayExtraPresets.ts @@ -8,8 +8,8 @@ import { useRemoteVar } from '../useRemoteVar' import { PayExtraAmountPresetValues, PayExtraPreset } from './types' /** Extracts and parses the Pay Extra presets from remote config */ -export const usePayExtraPresets = (key: StringKeys) => { - const configValue = useRemoteVar(key) +export const usePayExtraPresets = () => { + const configValue = useRemoteVar(StringKeys.PAY_EXTRA_PRESET_CENT_AMOUNTS) return useMemo(() => { const [low, medium, high] = parseIntList(configValue) return { diff --git a/packages/common/src/services/remote-config/defaults.ts b/packages/common/src/services/remote-config/defaults.ts index 6936a49f2ed..a661ff05402 100644 --- a/packages/common/src/services/remote-config/defaults.ts +++ b/packages/common/src/services/remote-config/defaults.ts @@ -47,7 +47,8 @@ export const remoteConfigIntDefaults: { [key in IntKeys]: number | null } = { [IntKeys.DISCOVERY_NOTIFICATIONS_GENESIS_UNIX_TIMESTAMP]: 0, [IntKeys.CACHE_ENTRY_TTL]: DEFAULT_ENTRY_TTL, [IntKeys.HANDLE_VERIFICATION_TIMEOUT_MILLIS]: - DEFAULT_HANDLE_VERIFICATION_TIMEOUT_MILLIS + DEFAULT_HANDLE_VERIFICATION_TIMEOUT_MILLIS, + [IntKeys.COINFLOW_MAXIMUM_CENTS]: 1000 } export const remoteConfigStringDefaults: { @@ -90,7 +91,6 @@ export const remoteConfigStringDefaults: { [StringKeys.STRIPE_ALLOWED_COUNTRIES_2_LETTER]: '', [StringKeys.AUDIO_FEATURES_DEGRADED_TEXT]: null, [StringKeys.PAY_EXTRA_PRESET_CENT_AMOUNTS]: '200,500,1000', - [StringKeys.COINFLOW_ADD_FUNDS_PRESET_CENT_AMOUNTS]: '200,500,1000', [StringKeys.EXPLORE_PREMIUM_DENIED_USERS]: '' } diff --git a/packages/common/src/services/remote-config/types.ts b/packages/common/src/services/remote-config/types.ts index 54e83dc577d..1d92a6399c2 100644 --- a/packages/common/src/services/remote-config/types.ts +++ b/packages/common/src/services/remote-config/types.ts @@ -195,7 +195,10 @@ export enum IntKeys { /** * Timeout for handle verification from socials. */ - HANDLE_VERIFICATION_TIMEOUT_MILLIS = 'HANDLE_VERIFICATION_TIMEOUT_MILLIS' + HANDLE_VERIFICATION_TIMEOUT_MILLIS = 'HANDLE_VERIFICATION_TIMEOUT_MILLIS', + + /** Coinflow maximum */ + COINFLOW_MAXIMUM_CENTS = 'COINFLOW_MAXIMUM_CENTS' } export enum BooleanKeys { @@ -390,10 +393,7 @@ export enum StringKeys { PAY_EXTRA_PRESET_CENT_AMOUNTS = 'PAY_EXTRA_PRESET_CENT_AMOUNTS', /** Denylist of user ids for explore premium tracks page */ - EXPLORE_PREMIUM_DENIED_USERS = 'EXPLORE_PREMIUM_DENIED_USERS', - - /** Add funds preset amounts for Coinflow */ - COINFLOW_ADD_FUNDS_PRESET_CENT_AMOUNTS = 'COINFLOW_ADD_FUNDS_PRESET_CENT_AMOUNTS' + EXPLORE_PREMIUM_DENIED_USERS = 'EXPLORE_PREMIUM_DENIED_USERS' } export type AllRemoteConfigKeys = diff --git a/packages/common/src/store/cache/collections/actions.ts b/packages/common/src/store/cache/collections/actions.ts index be03a971ea7..1b90e1c9277 100644 --- a/packages/common/src/store/cache/collections/actions.ts +++ b/packages/common/src/store/cache/collections/actions.ts @@ -72,9 +72,10 @@ export function createPlaylist( export function createPlaylistRequested( playlistId: ID, - noticeType: 'route' | 'toast' + noticeType: 'route' | 'toast', + isAlbum: boolean ) { - return { type: CREATE_PLAYLIST_REQUESTED, playlistId, noticeType } + return { type: CREATE_PLAYLIST_REQUESTED, playlistId, noticeType, isAlbum } } export function createPlaylistSucceeded() { diff --git a/packages/es-indexer/package.json b/packages/es-indexer/package.json index 8f71bd3f560..25922d380f0 100644 --- a/packages/es-indexer/package.json +++ b/packages/es-indexer/package.json @@ -15,11 +15,11 @@ "author": "", "devDependencies": { "@types/lodash": "4.14.182", - "@types/node": "17.0.29", + "@types/node": "20.10.2", "@types/pg": "8.6.5", "ts-node": "10.7.0", "tsconfig-paths": "3.14.1", - "typescript": "4.6.3" + "typescript": "5.3.2" }, "dependencies": { "@elastic/elasticsearch": "8.1.0", @@ -31,4 +31,4 @@ "pino": "7.11.0", "pm2": "5.2.0" } -} +} \ No newline at end of file diff --git a/packages/es-indexer/src/indexNames.ts b/packages/es-indexer/src/indexNames.ts index 17f33742f08..d8ab1a126cf 100644 --- a/packages/es-indexer/src/indexNames.ts +++ b/packages/es-indexer/src/indexNames.ts @@ -2,6 +2,6 @@ export const indexNames = { playlists: 'playlists18', reposts: 'reposts13', saves: 'saves13', - tracks: 'tracks16', + tracks: 'tracks17', users: 'users16', } diff --git a/packages/es-indexer/src/indexers/TrackIndexer.ts b/packages/es-indexer/src/indexers/TrackIndexer.ts index b390c7d7f96..d8e43ca5390 100644 --- a/packages/es-indexer/src/indexers/TrackIndexer.ts +++ b/packages/es-indexer/src/indexers/TrackIndexer.ts @@ -170,6 +170,16 @@ export class TrackIndexer extends BaseIndexer { row.repost_count = row.reposted_by.length row.favorite_count = row.saved_by.length + // get_feed_es uses `created_at` for tracks + playlists + reposts to sequence events + // and has additional logic to compute the "earliest" created_at for an item + // that can be either a track or a repost. + // + // while it would be possible to go adjust all this logic to conditionally use release_date for tracks + // it's much easier to set the `created_at` to be `release_date` for tracks. + // + // my hope is to revisit the action_log concept which will simplify this complex feed logic. + row.created_at = row.release_date || row.created_at + // permalink const currentRoute = row.routes[row.routes.length - 1] row.permalink = `/${row.user.handle}/${currentRoute}` diff --git a/packages/es-indexer/src/types/db.ts b/packages/es-indexer/src/types/db.ts index 52cd8777c79..25728f6353c 100644 --- a/packages/es-indexer/src/types/db.ts +++ b/packages/es-indexer/src/types/db.ts @@ -1,736 +1,877 @@ /* - * This file was generated by a tool. - * Rerun sql-ts to regenerate this file. - */ +* This file was generated by a tool. +* Rerun sql-ts to regenerate this file. +*/ export interface AggregateDailyAppNameMetricRow { - application_name: string - count: number - created_at?: Date - id?: number - timestamp: Date - updated_at?: Date + 'application_name': string; + 'count': number; + 'created_at': Date; + 'id': number; + 'timestamp': Date; + 'updated_at': Date; } export interface AggregateDailyTotalUsersMetricRow { - count: number - created_at?: Date - id?: number - timestamp: Date - updated_at?: Date + 'count': number; + 'created_at': Date; + 'id': number; + 'personal_count': number | null; + 'timestamp': Date; + 'updated_at': Date; } export interface AggregateDailyUniqueUsersMetricRow { - count: number - created_at?: Date - id?: number - summed_count?: number | null - timestamp: Date - updated_at?: Date + 'count': number; + 'created_at': Date; + 'id': number; + 'personal_count': number | null; + 'summed_count': number | null; + 'timestamp': Date; + 'updated_at': Date; } export interface AggregateIntervalPlayRow { - created_at?: Date | null - genre?: string | null - month_listen_counts?: string | null - track_id?: number | null - week_listen_counts?: string | null + 'created_at': Date | null; + 'genre': string | null; + 'month_listen_counts': string | null; + 'track_id': number | null; + 'week_listen_counts': string | null; } export interface AggregateMonthlyAppNameMetricRow { - application_name: string - count: number - created_at?: Date - id?: number - timestamp: Date - updated_at?: Date + 'application_name': string; + 'count': number; + 'created_at': Date; + 'id': number; + 'timestamp': Date; + 'updated_at': Date; } export interface AggregateMonthlyPlayRow { - count: number - play_item_id: number - timestamp?: Date + 'count': number; + 'play_item_id': number; + 'timestamp': Date; } export interface AggregateMonthlyTotalUsersMetricRow { - count: number - created_at?: Date - id?: number - timestamp: Date - updated_at?: Date + 'count': number; + 'created_at': Date; + 'id': number; + 'personal_count': number | null; + 'timestamp': Date; + 'updated_at': Date; } export interface AggregateMonthlyUniqueUsersMetricRow { - count: number - created_at?: Date - id?: number - summed_count?: number | null - timestamp: Date - updated_at?: Date + 'count': number; + 'created_at': Date; + 'id': number; + 'personal_count': number | null; + 'summed_count': number | null; + 'timestamp': Date; + 'updated_at': Date; } export interface AggregatePlaylistRow { - is_album?: boolean | null - playlist_id: number - repost_count?: number | null - save_count?: number | null + 'is_album': boolean | null; + 'playlist_id': number; + 'repost_count': number | null; + 'save_count': number | null; } export interface AggregatePlayRow { - count?: string | null - play_item_id: number + 'count': string | null; + 'play_item_id': number; } export interface AggregateTrackRow { - repost_count?: number - save_count?: number - track_id: number + 'repost_count': number; + 'save_count': number; + 'track_id': number; } export interface AggregateUserRow { - album_count?: string | null - follower_count?: string | null - following_count?: string | null - playlist_count?: string | null - repost_count?: string | null - supporter_count?: number - supporting_count?: number - track_count?: string | null - track_save_count?: string | null - user_id: number + 'album_count': string | null; + 'follower_count': string | null; + 'following_count': string | null; + 'playlist_count': string | null; + 'repost_count': string | null; + 'supporter_count': number; + 'supporting_count': number; + 'track_count': string | null; + 'track_save_count': string | null; + 'user_id': number; } export interface AggregateUserTipRow { - amount: string - receiver_user_id: number - sender_user_id: number + 'amount': string; + 'receiver_user_id': number; + 'sender_user_id': number; } export interface AppNameMetricRow { - application_name: string - count: number - created_at?: Date - id?: string - ip?: string | null - timestamp?: Date - updated_at?: Date + 'application_name': string; + 'count': number; + 'created_at': Date; + 'id': string; + 'ip': string | null; + 'timestamp': Date; + 'updated_at': Date; } export interface AppNameMetricsAllTimeRow { - count?: string | null - name?: string | null + 'count': string | null; + 'name': string | null; } export interface AppNameMetricsTrailingMonthRow { - count?: string | null - name?: string | null + 'count': string | null; + 'name': string | null; } export interface AppNameMetricsTrailingWeekRow { - count?: string | null - name?: string | null + 'count': string | null; + 'name': string | null; } export interface AssociatedWalletRow { - blockhash: string - blocknumber: number - chain: wallet_chain - id?: number - is_current: boolean - is_delete: boolean - user_id: number - wallet: string + 'blockhash': string; + 'blocknumber': number; + 'chain': wallet_chain; + 'id': number; + 'is_current': boolean; + 'is_delete': boolean; + 'user_id': number; + 'wallet': string; } export interface AudioTransactionsHistoryRow { - balance: string - change: string - created_at?: Date - method: string - signature: string - slot: number - transaction_created_at: Date - transaction_type: string - tx_metadata?: string | null - updated_at?: Date - user_bank: string + 'balance': string; + 'change': string; + 'created_at': Date; + 'method': string; + 'signature': string; + 'slot': number; + 'transaction_created_at': Date; + 'transaction_type': string; + 'tx_metadata': string | null; + 'updated_at': Date; + 'user_bank': string; } export interface AudiusDataTxRow { - signature: string - slot: number + 'signature': string; + 'slot': number; } export interface BlockRow { - blockhash: string - is_current?: boolean | null - number?: number | null - parenthash?: string | null + 'blockhash': string; + 'is_current': boolean | null; + 'number': number | null; + 'parenthash': string | null; } export interface ChallengeDisbursementRow { - amount: string - challenge_id: string - signature: string - slot: number - specifier: string - user_id: number + 'amount': string; + 'challenge_id': string; + 'created_at': Date | null; + 'signature': string; + 'slot': number; + 'specifier': string; + 'user_id': number; } export interface ChallengeListenStreakRow { - last_listen_date?: Date | null - listen_streak: number - user_id?: number + 'last_listen_date': Date | null; + 'listen_streak': number; + 'user_id': number; } export interface ChallengeProfileCompletionRow { - favorites: boolean - follows: boolean - profile_cover_photo: boolean - profile_description: boolean - profile_name: boolean - profile_picture: boolean - reposts: boolean - user_id?: number + 'favorites': boolean; + 'follows': boolean; + 'profile_cover_photo': boolean; + 'profile_description': boolean; + 'profile_name': boolean; + 'profile_picture': boolean; + 'reposts': boolean; + 'user_id': number; } export interface ChallengeRow { - active: boolean - amount: string - id: string - starting_block?: number | null - step_count?: number | null - type: challengetype + 'active': boolean; + 'amount': string; + 'cooldown_days': number | null; + 'id': string; + 'starting_block': number | null; + 'step_count': number | null; + 'type': challengetype; + 'weekly_pool': number | null; } export interface ChatRow { - chat_id: string - created_at: Date - last_message_at: Date + 'chat_id': string; + 'created_at': Date; + 'last_message': string | null; + 'last_message_at': Date; +} +export interface ChatBanRow { + 'is_banned': boolean; + 'updated_at': Date; + 'user_id': number; } export interface ChatBlockedUserRow { - blockee_user_id: number - blocker_user_id: number - created_at?: Date + 'blockee_user_id': number; + 'blocker_user_id': number; + 'created_at': Date; } export interface ChatMemberRow { - chat_id: string - cleared_history_at?: Date | null - invite_code: string - invited_by_user_id: number - last_active_at?: Date | null - unread_count?: number - user_id: number + 'chat_id': string; + 'cleared_history_at': Date | null; + 'created_at': Date; + 'invite_code': string; + 'invited_by_user_id': number; + 'last_active_at': Date | null; + 'unread_count': number; + 'user_id': number; } export interface ChatMessageRow { - chat_id: string - ciphertext: string - created_at: Date - message_id: string - user_id: number + 'chat_id': string; + 'ciphertext': string; + 'created_at': Date; + 'message_id': string; + 'user_id': number; } export interface ChatMessageReactionRow { - created_at?: Date - message_id: string - reaction: string - updated_at?: Date - user_id: number + 'created_at': Date; + 'message_id': string; + 'reaction': string; + 'updated_at': Date; + 'user_id': number; } export interface ChatPermissionRow { - permits?: string | null - user_id: number + 'permits': string | null; + 'updated_at': Date; + 'user_id': number; } export interface CidDataRow { - cid: string - data?: any | null - type?: string | null + 'cid': string; + 'data': any | null; + 'type': string | null; +} +export interface DashboardWalletUserRow { + 'blockhash': string | null; + 'blocknumber': number | null; + 'created_at': Date; + 'is_delete': boolean; + 'txhash': string; + 'updated_at': Date; + 'user_id': number; + 'wallet': string; +} +export interface DelistStatusCursorRow { + 'created_at': Date; + 'entity': delist_entity; + 'host': string; +} +export interface DeveloperAppRow { + 'address': string; + 'blockhash': string | null; + 'blocknumber': number | null; + 'created_at': Date; + 'description': string | null; + 'is_current': boolean; + 'is_delete': boolean; + 'is_personal_access': boolean; + 'name': string; + 'txhash': string; + 'updated_at': Date; + 'user_id': number | null; } export interface EthBlockRow { - created_at?: Date - last_scanned_block?: number - updated_at?: Date + 'created_at': Date; + 'last_scanned_block': number; + 'updated_at': Date; } export interface FollowRow { - blockhash?: string | null - blocknumber?: number | null - created_at: Date - followee_user_id: number - follower_user_id: number - is_current: boolean - is_delete: boolean - slot?: number | null - txhash?: string + 'blockhash': string | null; + 'blocknumber': number | null; + 'created_at': Date; + 'followee_user_id': number; + 'follower_user_id': number; + 'is_current': boolean; + 'is_delete': boolean; + 'slot': number | null; + 'txhash': string; +} +export interface GrantRow { + 'blockhash': string | null; + 'blocknumber': number | null; + 'created_at': Date; + 'grantee_address': string; + 'is_approved': boolean; + 'is_current': boolean; + 'is_revoked': boolean; + 'txhash': string; + 'updated_at': Date; + 'user_id': number; } export interface HourlyPlayCountRow { - hourly_timestamp: Date - play_count: number + 'hourly_timestamp': Date; + 'play_count': number; } export interface IndexingCheckpointRow { - last_checkpoint: number - signature?: string | null - tablename: string + 'last_checkpoint': number; + 'signature': string | null; + 'tablename': string; } export interface MilestoneRow { - blocknumber?: number | null - id: number - name: string - slot?: number | null - threshold: number - timestamp: Date + 'blocknumber': number | null; + 'id': number; + 'name': string; + 'slot': number | null; + 'threshold': number; + 'timestamp': Date; } export interface NotificationRow { - blocknumber?: number | null - data?: any | null - group_id: string - id?: number - slot?: number | null - specifier: string - timestamp: Date - type: string - user_ids?: any | null + 'blocknumber': number | null; + 'data': any | null; + 'group_id': string; + 'id': number; + 'slot': number | null; + 'specifier': string; + 'timestamp': Date; + 'type': string; + 'type_v2': string | null; + 'user_ids': any | null; } export interface NotificationSeenRow { - blockhash?: string | null - blocknumber?: number | null - seen_at: Date - txhash?: string | null - user_id: number + 'blockhash': string | null; + 'blocknumber': number | null; + 'seen_at': Date; + 'txhash': string | null; + 'user_id': number; +} +export interface PaymentRouterTxRow { + 'created_at': Date; + 'signature': string; + 'slot': number; } export interface PgStatStatementRow { - blk_read_time?: number | null - blk_write_time?: number | null - calls?: string | null - dbid?: any | null - local_blks_dirtied?: string | null - local_blks_hit?: string | null - local_blks_read?: string | null - local_blks_written?: string | null - max_time?: number | null - mean_time?: number | null - min_time?: number | null - query?: string | null - queryid?: string | null - rows?: string | null - shared_blks_dirtied?: string | null - shared_blks_hit?: string | null - shared_blks_read?: string | null - shared_blks_written?: string | null - stddev_time?: number | null - temp_blks_read?: string | null - temp_blks_written?: string | null - total_time?: number | null - userid?: any | null + 'blk_read_time': number | null; + 'blk_write_time': number | null; + 'calls': string | null; + 'dbid': any | null; + 'jit_emission_count': string | null; + 'jit_emission_time': number | null; + 'jit_functions': string | null; + 'jit_generation_time': number | null; + 'jit_inlining_count': string | null; + 'jit_inlining_time': number | null; + 'jit_optimization_count': string | null; + 'jit_optimization_time': number | null; + 'local_blks_dirtied': string | null; + 'local_blks_hit': string | null; + 'local_blks_read': string | null; + 'local_blks_written': string | null; + 'max_exec_time': number | null; + 'max_plan_time': number | null; + 'mean_exec_time': number | null; + 'mean_plan_time': number | null; + 'min_exec_time': number | null; + 'min_plan_time': number | null; + 'plans': string | null; + 'query': string | null; + 'queryid': string | null; + 'rows': string | null; + 'shared_blks_dirtied': string | null; + 'shared_blks_hit': string | null; + 'shared_blks_read': string | null; + 'shared_blks_written': string | null; + 'stddev_exec_time': number | null; + 'stddev_plan_time': number | null; + 'temp_blk_read_time': number | null; + 'temp_blk_write_time': number | null; + 'temp_blks_read': string | null; + 'temp_blks_written': string | null; + 'toplevel': boolean | null; + 'total_exec_time': number | null; + 'total_plan_time': number | null; + 'userid': any | null; + 'wal_bytes': string | null; + 'wal_fpi': string | null; + 'wal_records': string | null; +} +export interface PgStatStatementsInfoRow { + 'dealloc': string | null; + 'stats_reset': Date | null; } export interface PlaylistRouteRow { - blockhash: string - blocknumber: number - collision_id: number - is_current: boolean - owner_id: number - playlist_id: number - slug: string - title_slug: string - txhash: string + 'blockhash': string; + 'blocknumber': number; + 'collision_id': number; + 'is_current': boolean; + 'owner_id': number; + 'playlist_id': number; + 'slug': string; + 'title_slug': string; + 'txhash': string; } export interface PlaylistSeenRow { - blockhash?: string | null - blocknumber?: number | null - is_current: boolean - playlist_id: number - seen_at: Date - txhash?: string | null - user_id: number + 'blockhash': string | null; + 'blocknumber': number | null; + 'is_current': boolean; + 'playlist_id': number; + 'seen_at': Date; + 'txhash': string | null; + 'user_id': number; } export interface PlaylistRow { - blockhash?: string | null - blocknumber?: number | null - created_at: Date - description?: string | null - is_album: boolean - is_current: boolean - is_delete: boolean - is_private: boolean - last_added_to?: Date | null - metadata_multihash?: string | null - playlist_contents: any - playlist_id: number - playlist_image_multihash?: string | null - playlist_image_sizes_multihash?: string | null - playlist_name?: string | null - playlist_owner_id: number - slot?: number | null - txhash?: string - upc?: string | null - updated_at: Date -} - -export interface PlaylistRouteRow { - blockhash: string - blocknumber: number - collision_id: number - is_current: boolean - owner_id: number - slug: string - title_slug: string - playlist_id: number - txhash: string -} - + 'blockhash': string | null; + 'blocknumber': number | null; + 'created_at': Date; + 'description': string | null; + 'is_album': boolean; + 'is_current': boolean; + 'is_delete': boolean; + 'is_image_autogenerated': boolean; + 'is_private': boolean; + 'last_added_to': Date | null; + 'metadata_multihash': string | null; + 'playlist_contents': any; + 'playlist_id': number; + 'playlist_image_multihash': string | null; + 'playlist_image_sizes_multihash': string | null; + 'playlist_name': string | null; + 'playlist_owner_id': number; + 'slot': number | null; + 'txhash': string; + 'upc': string | null; + 'updated_at': Date; +} export interface PlayRow { - city?: string | null - country?: string | null - created_at?: Date - id?: number - play_item_id: number - region?: string | null - signature?: string | null - slot?: number | null - source?: string | null - updated_at?: Date - user_id?: number | null -} -export interface PlaysArchiveRow { - archived_at?: Date | null - created_at?: Date - id: number - play_item_id: number - signature?: string | null - slot?: number | null - source?: string | null - updated_at?: Date - user_id?: number | null + 'city': string | null; + 'country': string | null; + 'created_at': Date; + 'id': number; + 'play_item_id': number; + 'region': string | null; + 'signature': string | null; + 'slot': number | null; + 'source': string | null; + 'updated_at': Date; + 'user_id': number | null; } export interface PubkeyRow { - pubkey?: string | null - wallet: string + 'pubkey': string | null; + 'wallet': string; } export interface ReactionRow { - id?: number - reacted_to: string - reaction_type: string - reaction_value: number - sender_wallet: string - slot: number - timestamp: Date - tx_signature?: string | null + 'id': number; + 'reacted_to': string; + 'reaction_type': string; + 'reaction_value': number; + 'sender_wallet': string; + 'slot': number; + 'timestamp': Date; + 'tx_signature': string | null; } export interface RelatedArtistRow { - created_at?: Date - related_artist_user_id: number - score: number - user_id: number + 'created_at': Date; + 'related_artist_user_id': number; + 'score': number; + 'user_id': number; } export interface RemixeRow { - child_track_id: number - parent_track_id: number + 'child_track_id': number; + 'parent_track_id': number; } export interface RepostRow { - blockhash?: string | null - blocknumber?: number | null - created_at: Date - is_current: boolean - is_delete: boolean - repost_item_id: number - repost_type: reposttype - slot?: number | null - txhash?: string - user_id: number + 'blockhash': string | null; + 'blocknumber': number | null; + 'created_at': Date; + 'is_current': boolean; + 'is_delete': boolean; + 'is_repost_of_repost': boolean; + 'repost_item_id': number; + 'repost_type': reposttype; + 'slot': number | null; + 'txhash': string; + 'user_id': number; +} +export interface RevertBlockRow { + 'blocknumber': number; + 'prev_records': any; } export interface RewardManagerTxRow { - created_at: Date - signature: string - slot: number + 'created_at': Date; + 'signature': string; + 'slot': number; } export interface RewardsManagerBackfillTxRow { - created_at: Date - signature: string - slot: number + 'created_at': Date; + 'signature': string; + 'slot': number; } export interface RouteMetricRow { - count: number - created_at?: Date - id?: string - ip?: string | null - query_string?: string - route_path: string - timestamp?: Date - updated_at?: Date - version: string + 'count': number; + 'created_at': Date; + 'id': string; + 'ip': string | null; + 'query_string': string; + 'route_path': string; + 'timestamp': Date; + 'updated_at': Date; + 'version': string; } export interface RouteMetricsAllTimeRow { - count?: string | null - unique_count?: string | null + 'count': string | null; + 'unique_count': string | null; } export interface RouteMetricsDayBucketRow { - count?: string | null - time?: Date | null - unique_count?: string | null + 'count': string | null; + 'time': Date | null; + 'unique_count': string | null; } export interface RouteMetricsMonthBucketRow { - count?: string | null - time?: Date | null - unique_count?: string | null + 'count': string | null; + 'time': Date | null; + 'unique_count': string | null; } export interface RouteMetricsTrailingMonthRow { - count?: string | null - unique_count?: string | null + 'count': string | null; + 'unique_count': string | null; } export interface RouteMetricsTrailingWeekRow { - count?: string | null - unique_count?: string | null + 'count': string | null; + 'unique_count': string | null; +} +export interface RpcCursorRow { + 'relayed_at': Date; + 'relayed_by': string; +} +export interface RpcErrorRow { + 'error_count': number; + 'error_text': string; + 'last_attempt': Date; + 'rpc_log_json': any; + 'sig': string; } export interface RpcLogRow { - from_wallet?: string | null - jetstream_sequence: number - jetstream_timestamp: Date - rpc: Object - sig: string + 'applied_at': Date; + 'from_wallet': string; + 'relayed_at': Date; + 'relayed_by': string; + 'rpc': Object; + 'sig': string; } export interface RpclogRow { - cuid: string - jetstream_seq?: number | null - method?: string | null - params?: any | null - wallet?: string | null + 'cuid': string; + 'jetstream_seq': number | null; + 'method': string | null; + 'params': any | null; + 'wallet': string | null; } export interface SaveRow { - blockhash?: string | null - blocknumber?: number | null - created_at: Date - is_current: boolean - is_delete: boolean - save_item_id: number - save_type: savetype - slot?: number | null - txhash?: string - user_id: number + 'blockhash': string | null; + 'blocknumber': number | null; + 'created_at': Date; + 'is_current': boolean; + 'is_delete': boolean; + 'is_save_of_repost': boolean; + 'save_item_id': number; + 'save_type': savetype; + 'slot': number | null; + 'txhash': string; + 'user_id': number; } export interface SchemaMigrationRow { - version: string + 'version': string; +} +export interface SchemaVersionRow { + 'applied_at': Date; + 'file_name': string; + 'md5': string | null; } export interface SequelizeMetaRow { - name: string + 'name': string; } export interface SkippedTransactionRow { - blockhash: string - blocknumber: number - created_at?: Date - id?: number - level?: skippedtransactionlevel | null - txhash: string - updated_at?: Date + 'blockhash': string; + 'blocknumber': number; + 'created_at': Date; + 'id': number; + 'level': skippedtransactionlevel | null; + 'txhash': string; + 'updated_at': Date; } export interface SplTokenBackfillTxRow { - created_at: Date - last_scanned_slot?: number - signature: string - updated_at: Date + 'created_at': Date; + 'last_scanned_slot': number; + 'signature': string; + 'updated_at': Date; } export interface SplTokenTxRow { - created_at?: Date - last_scanned_slot: number - signature: string - updated_at?: Date + 'created_at': Date; + 'last_scanned_slot': number; + 'signature': string; + 'updated_at': Date; } export interface StemRow { - child_track_id: number - parent_track_id: number + 'child_track_id': number; + 'parent_track_id': number; } export interface SubscriptionRow { - blockhash?: string | null - blocknumber?: number | null - created_at?: Date - is_current: boolean - is_delete: boolean - subscriber_id: number - txhash?: string - user_id: number + 'blockhash': string | null; + 'blocknumber': number | null; + 'created_at': Date; + 'is_current': boolean; + 'is_delete': boolean; + 'subscriber_id': number; + 'txhash': string; + 'user_id': number; } export interface SupporterRankUpRow { - rank: number - receiver_user_id: number - sender_user_id: number - slot: number + 'rank': number; + 'receiver_user_id': number; + 'sender_user_id': number; + 'slot': number; } export interface TagTrackUserRow { - owner_id?: number | null - tag?: string | null - track_id?: number | null + 'owner_id': number | null; + 'tag': string | null; + 'track_id': number | null; +} +export interface TrackDelistStatuseRow { + 'created_at': Date; + 'delisted': boolean; + 'owner_id': number; + 'reason': delist_track_reason; + 'track_cid': string; + 'track_id': number; +} +export interface TrackPriceHistoryRow { + 'block_timestamp': Date; + 'blocknumber': number; + 'created_at': Date; + 'splits': any; + 'total_price_cents': string; + 'track_id': number; } export interface TrackRouteRow { - blockhash: string - blocknumber: number - collision_id: number - is_current: boolean - owner_id: number - slug: string - title_slug: string - track_id: number - txhash: string + 'blockhash': string; + 'blocknumber': number; + 'collision_id': number; + 'is_current': boolean; + 'owner_id': number; + 'slug': string; + 'title_slug': string; + 'track_id': number; + 'txhash': string; } export interface TrackTrendingScoreRow { - created_at: Date - genre?: string | null - score: number - time_range: string - track_id: number - type: string - version: string + 'created_at': Date; + 'genre': string | null; + 'score': number; + 'time_range': string; + 'track_id': number; + 'type': string; + 'version': string; } export interface TrackRow { - blockhash?: string | null - blocknumber?: number | null - cover_art?: string | null - cover_art_sizes?: string | null - create_date?: string | null - created_at: Date - credits_splits?: string | null - description?: string | null - download?: any | null - field_visibility?: any | null - file_type?: string | null - genre?: string | null - is_available?: boolean - is_current: boolean - is_delete: boolean - is_playlist_upload?: boolean - is_premium?: boolean - is_unlisted?: boolean - isrc?: string | null - iswc?: string | null - license?: string | null - metadata_multihash?: string | null - mood?: string | null - owner_id: number - premium_conditions?: any | null - release_date?: string | null - remix_of?: any | null - route_id?: string | null - slot?: number | null - stem_of?: any | null - tags?: string | null - title?: string | null - track_cid?: string | null - track_id: number - track_segments: any - txhash?: string - updated_at: Date + 'ai_attribution_user_id': number | null; + 'audio_upload_id': string | null; + 'blockhash': string | null; + 'blocknumber': number | null; + 'cover_art': string | null; + 'cover_art_sizes': string | null; + 'create_date': string | null; + 'created_at': Date; + 'credits_splits': string | null; + 'description': string | null; + 'download': any | null; + 'duration': number | null; + 'field_visibility': any | null; + 'file_type': string | null; + 'genre': string | null; + 'is_available': boolean; + 'is_current': boolean; + 'is_delete': boolean; + 'is_playlist_upload': boolean; + 'is_premium': boolean; + 'is_unlisted': boolean; + 'isrc': string | null; + 'iswc': string | null; + 'license': string | null; + 'metadata_multihash': string | null; + 'mood': string | null; + 'owner_id': number; + 'premium_conditions': any | null; + 'preview_cid': string | null; + 'preview_start_seconds': number | null; + 'release_date': Date | null; + 'remix_of': any | null; + 'route_id': string | null; + 'slot': number | null; + 'stem_of': any | null; + 'tags': string | null; + 'title': string | null; + 'track_cid': string | null; + 'track_id': number; + 'track_segments': any; + 'txhash': string; + 'updated_at': Date; } export interface TrendingParamRow { - genre?: string | null - karma?: string | null - owner_follower_count?: string | null - owner_id?: number | null - play_count?: string | null - repost_count?: number | null - repost_month_count?: string | null - repost_week_count?: string | null - repost_year_count?: string | null - save_count?: number | null - save_month_count?: string | null - save_week_count?: string | null - save_year_count?: string | null - track_id?: number | null + 'genre': string | null; + 'karma': string | null; + 'owner_follower_count': string | null; + 'owner_id': number | null; + 'play_count': string | null; + 'repost_count': number | null; + 'repost_month_count': string | null; + 'repost_week_count': string | null; + 'repost_year_count': string | null; + 'save_count': number | null; + 'save_month_count': string | null; + 'save_week_count': string | null; + 'save_year_count': string | null; + 'track_id': number | null; } export interface TrendingResultRow { - id?: string | null - rank: number - type: string - user_id: number - version: string - week: Date -} -export interface UrsmContentNodeRow { - blockhash?: string | null - blocknumber?: number | null - cnode_sp_id: number - created_at: Date - delegate_owner_wallet: string - endpoint?: string | null - is_current: boolean - owner_wallet: string - proposer_1_delegate_owner_wallet: string - proposer_2_delegate_owner_wallet: string - proposer_3_delegate_owner_wallet: string - proposer_sp_ids: any - slot?: number | null - txhash?: string + 'id': string | null; + 'rank': number; + 'type': string; + 'user_id': number; + 'version': string; + 'week': Date; +} +export interface UsdcPurchaseRow { + 'amount': string; + 'buyer_user_id': number; + 'content_id': number; + 'content_type': usdc_purchase_content_type; + 'created_at': Date; + 'extra_amount': string; + 'seller_user_id': number; + 'signature': string; + 'slot': number; + 'updated_at': Date; +} +export interface UsdcTransactionsHistoryRow { + 'balance': string; + 'change': string; + 'created_at': Date; + 'method': string; + 'signature': string; + 'slot': number; + 'transaction_created_at': Date; + 'transaction_type': string; + 'tx_metadata': string | null; + 'updated_at': Date; + 'user_bank': string; +} +export interface UsdcUserBankAccountRow { + 'bank_account': string; + 'created_at': Date; + 'ethereum_address': string; + 'signature': string; } export interface UserBalanceChangeRow { - blocknumber: number - created_at?: Date - current_balance: string - previous_balance: string - updated_at?: Date - user_id?: number + 'blocknumber': number; + 'created_at': Date; + 'current_balance': string; + 'previous_balance': string; + 'updated_at': Date; + 'user_id': number; } export interface UserBalanceRow { - associated_sol_wallets_balance?: string - associated_wallets_balance?: string - balance: string - created_at?: Date - updated_at?: Date - user_id?: number - waudio?: string | null + 'associated_sol_wallets_balance': string; + 'associated_wallets_balance': string; + 'balance': string; + 'created_at': Date; + 'updated_at': Date; + 'user_id': number; + 'waudio': string | null; } export interface UserBankAccountRow { - bank_account: string - created_at: Date - ethereum_address: string - signature: string + 'bank_account': string; + 'created_at': Date; + 'ethereum_address': string; + 'signature': string; } export interface UserBankBackfillTxRow { - created_at: Date - signature: string - slot: number + 'created_at': Date; + 'signature': string; + 'slot': number; } export interface UserBankTxRow { - created_at: Date - signature: string - slot: number + 'created_at': Date; + 'signature': string; + 'slot': number; } export interface UserChallengeRow { - challenge_id: string - completed_blocknumber?: number | null - current_step_count?: number | null - is_complete: boolean - specifier: string - user_id: number + 'amount': number; + 'challenge_id': string; + 'completed_blocknumber': number | null; + 'created_at': Date; + 'current_step_count': number | null; + 'is_complete': boolean; + 'specifier': string; + 'user_id': number; +} +export interface UserDelistStatuseRow { + 'created_at': Date; + 'delisted': boolean; + 'reason': delist_user_reason; + 'user_id': number; } export interface UserEventRow { - blockhash?: string | null - blocknumber?: number | null - id?: number - is_current: boolean - is_mobile_user?: boolean - referrer?: number | null - slot?: number | null - user_id: number + 'blockhash': string | null; + 'blocknumber': number | null; + 'id': number; + 'is_current': boolean; + 'is_mobile_user': boolean; + 'referrer': number | null; + 'slot': number | null; + 'user_id': number; } export interface UserListeningHistoryRow { - listening_history: any - user_id?: number + 'listening_history': any; + 'user_id': number; +} +export interface UserPubkeyRow { + 'pubkey_base64': string; + 'user_id': number; } export interface UserTipRow { - amount: string - created_at?: Date - receiver_user_id: number - sender_user_id: number - signature: string - slot: number - updated_at?: Date + 'amount': string; + 'created_at': Date; + 'receiver_user_id': number; + 'sender_user_id': number; + 'signature': string; + 'slot': number; + 'updated_at': Date; } export interface UserRow { - artist_pick_track_id?: number | null - bio?: string | null - blockhash?: string | null - blocknumber?: number | null - cover_photo?: string | null - cover_photo_sizes?: string | null - created_at?: Date - creator_node_endpoint?: string | null - handle?: string | null - handle_lc?: string | null - has_collectibles?: boolean - is_current: boolean - is_deactivated?: boolean - is_available?: boolean - is_verified?: boolean - location?: string | null - metadata_multihash?: string | null - name?: string | null - playlist_library?: any | null - primary_id?: number | null - profile_picture?: string | null - profile_picture_sizes?: string | null - replica_set_update_signer?: string | null - secondary_ids?: any | null - slot?: number | null - txhash?: string - updated_at?: Date - user_authority_account?: string | null - user_id: number - user_storage_account?: string | null - wallet?: string | null + 'allow_ai_attribution': boolean; + 'artist_pick_track_id': number | null; + 'bio': string | null; + 'blockhash': string | null; + 'blocknumber': number | null; + 'cover_photo': string | null; + 'cover_photo_sizes': string | null; + 'created_at': Date; + 'creator_node_endpoint': string | null; + 'handle': string | null; + 'handle_lc': string | null; + 'has_collectibles': boolean; + 'is_available': boolean; + 'is_current': boolean; + 'is_deactivated': boolean; + 'is_storage_v2': boolean; + 'is_verified': boolean; + 'location': string | null; + 'metadata_multihash': string | null; + 'name': string | null; + 'playlist_library': any | null; + 'primary_id': number | null; + 'profile_picture': string | null; + 'profile_picture_sizes': string | null; + 'replica_set_update_signer': string | null; + 'secondary_ids': any | null; + 'slot': number | null; + 'txhash': string; + 'updated_at': Date; + 'user_authority_account': string | null; + 'user_id': number; + 'user_storage_account': string | null; + 'wallet': string | null; } export enum wallet_chain { 'eth' = 'eth', 'sol' = 'sol', } +export enum usdc_purchase_content_type { + 'track' = 'track', + 'playlist' = 'playlist', + 'album' = 'album', +} export enum skippedtransactionlevel { 'node' = 'node', 'network' = 'network', @@ -745,9 +886,40 @@ export enum reposttype { 'playlist' = 'playlist', 'album' = 'album', } +export enum delist_user_reason { + 'STRIKE_THRESHOLD' = 'STRIKE_THRESHOLD', + 'COPYRIGHT_SCHOOL' = 'COPYRIGHT_SCHOOL', + 'MANUAL' = 'MANUAL', +} +export enum delist_track_reason { + 'DMCA' = 'DMCA', + 'ACR' = 'ACR', + 'MANUAL' = 'MANUAL', + 'ACR_COUNTER_NOTICE' = 'ACR_COUNTER_NOTICE', + 'DMCA_RETRACTION' = 'DMCA_RETRACTION', + 'DMCA_COUNTER_NOTICE' = 'DMCA_COUNTER_NOTICE', + 'DMCA_AND_ACR_COUNTER_NOTICE' = 'DMCA_AND_ACR_COUNTER_NOTICE', +} +export enum delist_entity { + 'TRACKS' = 'TRACKS', + 'USERS' = 'USERS', +} export enum challengetype { 'boolean' = 'boolean', 'numeric' = 'numeric', 'aggregate' = 'aggregate', 'trending' = 'trending', } +export interface SaveRow { + 'blockhash': string | null; + 'blocknumber': number | null; + 'created_at': Date | null; + 'is_current': boolean | null; + 'is_delete': boolean | null; + 'is_save_of_repost': boolean | null; + 'save_item_id': number | null; + 'save_type': savetype | null; + 'slot': number | null; + 'txhash': string | null; + 'user_id': number | null; +} diff --git a/packages/mobile/src/components/add-to-collection-drawer/AddToCollectionDrawer.tsx b/packages/mobile/src/components/add-to-collection-drawer/AddToCollectionDrawer.tsx index 25143e80ef6..b0b7b44987e 100644 --- a/packages/mobile/src/components/add-to-collection-drawer/AddToCollectionDrawer.tsx +++ b/packages/mobile/src/components/add-to-collection-drawer/AddToCollectionDrawer.tsx @@ -9,8 +9,11 @@ import { cacheCollectionsActions, addToCollectionUISelectors } from '@audius/common' +import { fetchAccountCollections } from 'common/store/saved-collections/actions' +import { capitalize } from 'lodash' import { View } from 'react-native' import { useDispatch, useSelector } from 'react-redux' +import { useEffectOnce } from 'react-use' import { Card } from 'app/components/card' import { AppDrawer, useDrawerState } from 'app/components/drawer' @@ -22,18 +25,20 @@ import { CollectionList } from '../collection-list' import { AddCollectionCard } from '../collection-list/AddCollectionCard' import type { ImageProps } from '../image/FastImage' -const { addTrackToPlaylist, createPlaylist } = cacheCollectionsActions -const { getTrackId, getTrackTitle, getTrackIsUnlisted } = +const { addTrackToPlaylist, createAlbum, createPlaylist } = + cacheCollectionsActions +const { getTrackId, getTrackTitle, getTrackIsUnlisted, getCollectionType } = addToCollectionUISelectors -const { getAccountWithOwnPlaylists } = accountSelectors +const { getAccountWithNameSortedPlaylistsAndAlbums } = accountSelectors const { requestOpen: openDuplicateAddConfirmation } = duplicateAddConfirmationModalUIActions -const messages = { - title: 'Add To Playlist', - addedToast: 'Added To Playlist!', - hiddenAdd: 'You cannot add hidden tracks to a public playlist.' -} +const getMessages = (collectionType: 'album' | 'playlist') => ({ + title: `Add To ${capitalize(collectionType)}`, + addedToast: `Added To ${capitalize(collectionType)}!`, + newCollection: `New ${capitalize(collectionType)}`, + hiddenAdd: `You cannot add hidden tracks to a public ${collectionType}.` +}) const useStyles = makeStyles(() => ({ buttonContainer: { @@ -55,10 +60,18 @@ export const AddToCollectionDrawer = () => { const { toast } = useToast() const dispatch = useDispatch() const { onClose } = useDrawerState('AddToCollection') + const collectionType = useSelector(getCollectionType) + const isAlbumType = collectionType === 'album' const trackId = useSelector(getTrackId) const trackTitle = useSelector(getTrackTitle) const isTrackUnlisted = useSelector(getTrackIsUnlisted) - const user = useSelector(getAccountWithOwnPlaylists) + const account = useSelector(getAccountWithNameSortedPlaylistsAndAlbums) + + const messages = getMessages(collectionType) + + useEffectOnce(() => { + dispatch(fetchAccountCollections()) + }) const renderImage = useCallback( (item) => (props?: ImageProps) => @@ -72,21 +85,25 @@ export const AddToCollectionDrawer = () => { [] ) - const userPlaylists = user?.playlists ?? [] + const filteredCollections = + (isAlbumType ? account?.albums : account?.playlists) ?? [] - const playlistTrackIdMap = useMemo(() => { - const playlists = user?.playlists ?? [] - return playlists.reduce((acc, playlist) => { + const collectionTrackIdMap = useMemo(() => { + const collections = + (isAlbumType ? account?.albums : account?.playlists) ?? [] + return collections.reduce((acc, playlist) => { const trackIds = playlist.playlist_contents.track_ids.map((t) => t.track) acc[playlist.playlist_id] = trackIds return acc }, {}) - }, [user?.playlists]) + }, [account?.albums, account?.playlists, isAlbumType]) - const addToNewPlaylist = useCallback(() => { - const metadata = { playlist_name: trackTitle ?? 'New Playlist' } + const addToNewCollection = useCallback(() => { + const metadata = { + playlist_name: trackTitle ?? messages.newCollection + } dispatch( - createPlaylist( + (isAlbumType ? createAlbum : createPlaylist)( metadata, CreatePlaylistSource.FROM_TRACK, trackId, @@ -94,7 +111,14 @@ export const AddToCollectionDrawer = () => { ) ) onClose() - }, [dispatch, onClose, trackId, trackTitle]) + }, [ + dispatch, + isAlbumType, + messages.newCollection, + onClose, + trackId, + trackTitle + ]) const renderCard = useCallback( ({ item }: { item: Collection | { _create: boolean } }) => @@ -102,7 +126,8 @@ export const AddToCollectionDrawer = () => { ) : ( { type='collection' id={item.playlist_id} primaryText={item.playlist_name} - secondaryText={user?.name} + secondaryText={account?.name} onPress={() => { if (!trackId) return - // Don't add if the track is hidden, but playlist is public + // Don't add if the track is hidden, but collection is public if (isTrackUnlisted && !item.is_private) { toast({ content: messages.hiddenAdd }) return } - const doesPlaylistContainTrack = - playlistTrackIdMap[item.playlist_id]?.includes(trackId) + const doesCollectionContainTrack = + collectionTrackIdMap[item.playlist_id]?.includes(trackId) - if (doesPlaylistContainTrack) { + if (doesCollectionContainTrack) { dispatch( openDuplicateAddConfirmation({ playlistId: item.playlist_id, @@ -141,19 +166,21 @@ export const AddToCollectionDrawer = () => { /> ), [ - addToNewPlaylist, - dispatch, + trackId, + addToNewCollection, isTrackUnlisted, - onClose, - playlistTrackIdMap, + account?.name, renderImage, + collectionTrackIdMap, + onClose, toast, - trackId, - user?.name + messages.hiddenAdd, + messages.addedToast, + dispatch ] ) - if (!user || !trackId || !trackTitle) { + if (!account || !trackId || !trackTitle) { return null } @@ -167,7 +194,7 @@ export const AddToCollectionDrawer = () => { diff --git a/packages/mobile/src/components/collection-list/AddCollectionCard.tsx b/packages/mobile/src/components/collection-list/AddCollectionCard.tsx index 6aba0049142..bb7f5afcb82 100644 --- a/packages/mobile/src/components/collection-list/AddCollectionCard.tsx +++ b/packages/mobile/src/components/collection-list/AddCollectionCard.tsx @@ -2,6 +2,7 @@ import { useCallback } from 'react' import type { ID } from '@audius/common' import { cacheCollectionsActions, CreatePlaylistSource } from '@audius/common' +import { capitalize } from 'lodash' import { View } from 'react-native' import { useDispatch } from 'react-redux' @@ -10,16 +11,18 @@ import { Text, Tile } from 'app/components/core' import { makeStyles } from 'app/styles' import { useThemeColors } from 'app/utils/theme' -const { createPlaylist } = cacheCollectionsActions +const { createPlaylist, createAlbum } = cacheCollectionsActions const messages = { - createPlaylist: 'Create Playlist' + createPlaylist: (collectionType: 'album' | 'playlist') => + `Create ${capitalize(collectionType)}` } type AddCollectionCardProps = { onCreate?: () => void source: CreatePlaylistSource sourceTrackId?: ID | null + collectionType: 'album' | 'playlist' } const useStyles = makeStyles(({ spacing }) => ({ @@ -42,7 +45,8 @@ const useStyles = makeStyles(({ spacing }) => ({ export const AddCollectionCard = ({ onCreate, source = CreatePlaylistSource.LIBRARY_PAGE, - sourceTrackId = null + sourceTrackId = null, + collectionType }: AddCollectionCardProps) => { const styles = useStyles() const { neutralLight2 } = useThemeColors() @@ -52,14 +56,14 @@ export const AddCollectionCard = ({ if (onCreate) return onCreate() dispatch( - createPlaylist( + (collectionType === 'album' ? createAlbum : createPlaylist)( { playlist_name: 'New Playlist' }, source, sourceTrackId, source === CreatePlaylistSource.FROM_TRACK ? 'toast' : 'route' ) ) - }, [onCreate, dispatch, source, sourceTrackId]) + }, [onCreate, dispatch, collectionType, source, sourceTrackId]) return ( @@ -77,7 +81,7 @@ export const AddCollectionCard = ({ fontSize='medium' weight='bold' > - {messages.createPlaylist} + {messages.createPlaylist(collectionType)} diff --git a/packages/mobile/src/components/collection-list/CollectionList.tsx b/packages/mobile/src/components/collection-list/CollectionList.tsx index e2f1fe9f9d9..2fef6319aa7 100644 --- a/packages/mobile/src/components/collection-list/CollectionList.tsx +++ b/packages/mobile/src/components/collection-list/CollectionList.tsx @@ -60,6 +60,8 @@ const FullCollectionList = (props: FullCollectionListProps) => { source={createPlaylistSource!} sourceTrackId={createPlaylistTrackId} onCreate={createPlaylistCallback} + // TODO: support album type (we don't have use case currently) + collectionType='playlist' /> ) : ( { source={createPlaylistSource!} sourceTrackId={createPlaylistTrackId} onCreate={createPlaylistCallback} + // TODO: support album type (we don't have use case currently) + collectionType='playlist' /> ) : ( diff --git a/packages/mobile/src/components/duplicate-add-confirmation-drawer/DuplicateAddConfirmationDrawer.tsx b/packages/mobile/src/components/duplicate-add-confirmation-drawer/DuplicateAddConfirmationDrawer.tsx index 39fc53a494d..9583d0b9cec 100644 --- a/packages/mobile/src/components/duplicate-add-confirmation-drawer/DuplicateAddConfirmationDrawer.tsx +++ b/packages/mobile/src/components/duplicate-add-confirmation-drawer/DuplicateAddConfirmationDrawer.tsx @@ -6,6 +6,7 @@ import { duplicateAddConfirmationModalUISelectors, fillString } from '@audius/common' +import { capitalize } from 'lodash' import { View } from 'react-native' import { useDispatch, useSelector } from 'react-redux' @@ -19,13 +20,13 @@ const { getPlaylistId, getTrackId } = duplicateAddConfirmationModalUISelectors const { addTrackToPlaylist } = cacheCollectionsActions const { getCollection } = cacheCollectionsSelectors -const messages = { +const getMessages = (collectionType: 'album' | 'playlist') => ({ drawerTitle: 'Already Added', - drawerBody: 'This is already in your%0 playlist.', + drawerBody: `This is already in your%0 ${collectionType}.`, buttonAddText: 'Add Anyway', buttonCancelText: "Don't Add", - addedToast: 'Added To Playlist!' -} + addedToast: `Added To ${capitalize(collectionType)}!` +}) const useStyles = makeStyles(({ palette, spacing }) => ({ title: { @@ -65,13 +66,15 @@ export const DuplicateAddConfirmationDrawer = () => { const { toast } = useToast() const { isOpen, onClose } = useDrawerState('DuplicateAddConfirmation') + const messages = getMessages(playlist?.is_album ? 'album' : 'playlist') + const handleAdd = useCallback(() => { if (playlistId && trackId) { toast({ content: messages.addedToast }) dispatch(addTrackToPlaylist(trackId, playlistId)) } onClose() - }, [playlistId, trackId, onClose, toast, dispatch]) + }, [playlistId, trackId, onClose, toast, messages.addedToast, dispatch]) return ( diff --git a/packages/mobile/src/components/lineup-tile/TrackTile.tsx b/packages/mobile/src/components/lineup-tile/TrackTile.tsx index a43c2a37630..220f16669be 100644 --- a/packages/mobile/src/components/lineup-tile/TrackTile.tsx +++ b/packages/mobile/src/components/lineup-tile/TrackTile.tsx @@ -84,6 +84,10 @@ export const TrackTileComponent = ({ FeatureFlags.PODCAST_CONTROL_UPDATES_ENABLED, FeatureFlags.PODCAST_CONTROL_UPDATES_ENABLED_FALLBACK ) + const { isEnabled: isEditAlbumsEnabled } = useFeatureFlag( + FeatureFlags.EDIT_ALBUMS + ) + const isUSDCEnabled = useIsUSDCEnabled() const dispatch = useDispatch() const navigation = useNavigation() @@ -149,6 +153,7 @@ export const TrackTileComponent = ({ genre === Genre.PODCASTS || genre === Genre.AUDIOBOOKS const overflowActions = [ + isEditAlbumsEnabled && isOwner ? OverflowAction.ADD_TO_ALBUM : null, !isPremium ? OverflowAction.ADD_TO_PLAYLIST : null, isNewPodcastControlsEnabled && isLongFormContent ? OverflowAction.VIEW_EPISODE_PAGE @@ -173,11 +178,12 @@ export const TrackTileComponent = ({ }, [ track_id, genre, + isEditAlbumsEnabled, + isOwner, isPremium, isNewPodcastControlsEnabled, playbackPositionInfo?.status, isOnArtistsTracksTab, - isOwner, dispatch ]) diff --git a/packages/mobile/src/components/now-playing-drawer/ActionsBar.tsx b/packages/mobile/src/components/now-playing-drawer/ActionsBar.tsx index 89f8c48e972..eb16b1df6ea 100644 --- a/packages/mobile/src/components/now-playing-drawer/ActionsBar.tsx +++ b/packages/mobile/src/components/now-playing-drawer/ActionsBar.tsx @@ -110,6 +110,11 @@ export const ActionsBar = ({ track }: ActionsBarProps) => { FeatureFlags.PODCAST_CONTROL_UPDATES_ENABLED, FeatureFlags.PODCAST_CONTROL_UPDATES_ENABLED_FALLBACK ) + const { isEnabled: isEditAlbumsEnabled } = useFeatureFlag( + FeatureFlags.EDIT_ALBUMS + ) + + const isOwner = track?.owner_id === accountUser?.user_id const { onOpen: openPremiumContentPurchaseModal } = usePremiumContentPurchaseModal() @@ -137,25 +142,25 @@ export const ActionsBar = ({ track }: ActionsBarProps) => { if (track) { if (track.has_current_user_saved) { dispatch(unsaveTrack(track.track_id, FavoriteSource.NOW_PLAYING)) - } else if (track.owner_id === accountUser?.user_id) { + } else if (isOwner) { toast({ content: messages.favoriteProhibited }) } else { dispatch(saveTrack(track.track_id, FavoriteSource.NOW_PLAYING)) } } - }, [accountUser?.user_id, dispatch, toast, track]) + }, [dispatch, isOwner, toast, track]) const handleRepost = useCallback(() => { if (track) { if (track.has_current_user_reposted) { dispatch(undoRepostTrack(track.track_id, RepostSource.NOW_PLAYING)) - } else if (track.owner_id === accountUser?.user_id) { + } else if (isOwner) { toast({ content: messages.repostProhibited }) } else { dispatch(repostTrack(track.track_id, RepostSource.NOW_PLAYING)) } } - }, [accountUser?.user_id, dispatch, toast, track]) + }, [dispatch, isOwner, toast, track]) const handleShare = useCallback(() => { if (track) { @@ -180,6 +185,7 @@ export const ActionsBar = ({ track }: ActionsBarProps) => { const isLongFormContent = track.genre === Genre.PODCASTS || track.genre === Genre.AUDIOBOOKS const overflowActions = [ + isEditAlbumsEnabled && isOwner ? OverflowAction.ADD_TO_ALBUM : null, !track.is_premium ? OverflowAction.ADD_TO_PLAYLIST : null, isNewPodcastControlsEnabled && isLongFormContent ? OverflowAction.VIEW_EPISODE_PAGE @@ -202,6 +208,7 @@ export const ActionsBar = ({ track }: ActionsBarProps) => { } }, [ track, + isOwner, isNewPodcastControlsEnabled, playbackPositionInfo?.status, dispatch diff --git a/packages/mobile/src/components/overflow-menu-drawer/TrackOverflowMenuDrawer.tsx b/packages/mobile/src/components/overflow-menu-drawer/TrackOverflowMenuDrawer.tsx index 1823005d13b..91b5335e326 100644 --- a/packages/mobile/src/components/overflow-menu-drawer/TrackOverflowMenuDrawer.tsx +++ b/packages/mobile/src/components/overflow-menu-drawer/TrackOverflowMenuDrawer.tsx @@ -99,6 +99,8 @@ const TrackOverflowMenuDrawer = ({ render }: Props) => { source: ShareSource.OVERFLOW }) ), + [OverflowAction.ADD_TO_ALBUM]: () => + dispatch(openAddToCollectionModal('album', id, title, is_unlisted)), [OverflowAction.ADD_TO_PLAYLIST]: () => dispatch(openAddToCollectionModal('playlist', id, title, is_unlisted)), [OverflowAction.REMOVE_FROM_PLAYLIST]: () => { diff --git a/packages/mobile/src/components/payment-method/PaymentMethod.tsx b/packages/mobile/src/components/payment-method/PaymentMethod.tsx index f5bab1d2280..11369153df1 100644 --- a/packages/mobile/src/components/payment-method/PaymentMethod.tsx +++ b/packages/mobile/src/components/payment-method/PaymentMethod.tsx @@ -5,8 +5,6 @@ import { PurchaseMethod, formatUSDCWeiToFloorCentsNumber, formatCurrencyBalance, - FeatureFlags, - useFeatureFlag, PurchaseVendor, removeNullable } from '@audius/common' @@ -68,6 +66,7 @@ type PaymentMethodProps = { balance?: Nullable isExistingBalanceDisabled?: boolean showExistingBalance?: boolean + isCoinflowEnabled?: boolean } export const PaymentMethod = ({ @@ -75,7 +74,8 @@ export const PaymentMethod = ({ setSelectedMethod, balance, isExistingBalanceDisabled, - showExistingBalance + showExistingBalance, + isCoinflowEnabled }: PaymentMethodProps) => { const styles = useStyles() const neutral = useColor('neutral') @@ -86,19 +86,17 @@ export const PaymentMethod = ({ ) const balanceFormatted = formatCurrencyBalance(balanceCents / 100) const purchaseVendor = useSelector(getPurchaseVendor) - const { isEnabled: isCoinflowEnabled, isLoaded: isCoinflowEnabledLoaded } = - useFeatureFlag(FeatureFlags.BUY_WITH_COINFLOW) const vendorOptions = [ isCoinflowEnabled ? PurchaseVendor.COINFLOW : null, PurchaseVendor.STRIPE ].filter(removeNullable) - // Initial state is coinflow by default, but set to stripe if coinflow is disabled. + // Set initial state if coinflow is enabled useEffect(() => { - if (isCoinflowEnabledLoaded && !isCoinflowEnabled) { - dispatch(setPurchaseVendor(PurchaseVendor.STRIPE)) + if (isCoinflowEnabled) { + dispatch(setPurchaseVendor(PurchaseVendor.COINFLOW)) } - }, [dispatch, isCoinflowEnabledLoaded, isCoinflowEnabled]) + }, [dispatch, isCoinflowEnabled]) const items: SummaryTableItem[] = [ { diff --git a/packages/mobile/src/components/premium-track-purchase-drawer/PremiumTrackPurchaseDrawer.tsx b/packages/mobile/src/components/premium-track-purchase-drawer/PremiumTrackPurchaseDrawer.tsx index ab9f6a384cf..acc7f388af7 100644 --- a/packages/mobile/src/components/premium-track-purchase-drawer/PremiumTrackPurchaseDrawer.tsx +++ b/packages/mobile/src/components/premium-track-purchase-drawer/PremiumTrackPurchaseDrawer.tsx @@ -24,7 +24,9 @@ import { usePurchaseMethod, useUSDCBalance, PURCHASE_VENDOR, - StringKeys + useRemoteVar, + IntKeys, + PurchaseVendor } from '@audius/common' import { Formik, useField, useFormikContext } from 'formik' import { @@ -235,9 +237,7 @@ const RenderForm = ({ const styles = useStyles() const dispatch = useDispatch() const { specialLightGreen, primary } = useThemeColors() - const presetValues = usePayExtraPresets( - StringKeys.PAY_EXTRA_PRESET_CENT_AMOUNTS - ) + const presetValues = usePayExtraPresets() const { isEnabled: isIOSUSDCPurchaseEnabled } = useFeatureFlag( FeatureFlags.IOS_USDC_PURCHASE_ENABLED ) @@ -268,6 +268,10 @@ const RenderForm = ({ price, currentBalance: balance }) + const { isEnabled: isCoinflowEnabled } = useFeatureFlag( + FeatureFlags.BUY_WITH_COINFLOW + ) + const coinflowMaximumCents = useRemoteVar(IntKeys.COINFLOW_MAXIMUM_CENTS) const { isExistingBalanceDisabled, totalPriceInCents } = usePurchaseMethod({ price, @@ -301,6 +305,15 @@ const RenderForm = ({ dispatch(setPurchasePage({ page: PurchaseContentPage.PURCHASE })) }, [dispatch]) + const showCoinflow = + isCoinflowEnabled && totalPriceInCents <= coinflowMaximumCents + + useEffect(() => { + if (purchaseVendor === PurchaseVendor.COINFLOW && !showCoinflow) { + setPurchaseVendor(PurchaseVendor.STRIPE) + } + }, [setPurchaseVendor, showCoinflow, purchaseVendor]) + return ( {page === PurchaseContentPage.PURCHASE ? ( @@ -329,6 +342,7 @@ const RenderForm = ({ balance={balance} isExistingBalanceDisabled={isExistingBalanceDisabled} showExistingBalance={!balance?.isZero()} + isCoinflowEnabled={showCoinflow} /> )} @@ -401,10 +415,7 @@ export const PremiumTrackPurchaseDrawer = () => { const styles = useStyles() const dispatch = useDispatch() const isUSDCEnabled = useIsUSDCEnabled() - const presetValues = usePayExtraPresets( - StringKeys.PAY_EXTRA_PRESET_CENT_AMOUNTS - ) - + const presetValues = usePayExtraPresets() const { data: { contentId: trackId }, isOpen, diff --git a/packages/mobile/src/components/premium-track-purchase-drawer/hooks/usePurchaseSummaryValues.ts b/packages/mobile/src/components/premium-track-purchase-drawer/hooks/usePurchaseSummaryValues.ts index 21715108c86..dd07819131a 100644 --- a/packages/mobile/src/components/premium-track-purchase-drawer/hooks/usePurchaseSummaryValues.ts +++ b/packages/mobile/src/components/premium-track-purchase-drawer/hooks/usePurchaseSummaryValues.ts @@ -6,8 +6,7 @@ import { getPurchaseSummaryValues, PayExtraPreset, usePayExtraPresets, - useUSDCPurchaseConfig, - StringKeys + useUSDCPurchaseConfig } from '@audius/common' import { useField } from 'formik' @@ -20,9 +19,7 @@ export const usePurchaseSummaryValues = ({ }) => { const [{ value: customAmount }] = useField(CUSTOM_AMOUNT) const [{ value: amountPreset }] = useField(AMOUNT_PRESET) - const presetValues = usePayExtraPresets( - StringKeys.PAY_EXTRA_PRESET_CENT_AMOUNTS - ) + const presetValues = usePayExtraPresets() const { minUSDCPurchaseAmountCents } = useUSDCPurchaseConfig() const extraAmount = getExtraAmount({ diff --git a/packages/mobile/src/components/track-list/TrackListItem.tsx b/packages/mobile/src/components/track-list/TrackListItem.tsx index 7810d1beba1..e4c8e551ea3 100644 --- a/packages/mobile/src/components/track-list/TrackListItem.tsx +++ b/packages/mobile/src/components/track-list/TrackListItem.tsx @@ -231,6 +231,10 @@ const TrackListItemComponent = (props: TrackListItemComponentProps) => { owner_id, is_premium: isPremium } = track + const { isEnabled: isEditAlbumsEnabled } = useFeatureFlag( + FeatureFlags.EDIT_ALBUMS + ) + const { is_deactivated, name } = user const isDeleted = is_delete || !!is_deactivated @@ -305,6 +309,7 @@ const TrackListItemComponent = (props: TrackListItemComponentProps) => { ? OverflowAction.UNREPOST : OverflowAction.REPOST : null, + isEditAlbumsEnabled && isTrackOwner ? OverflowAction.ADD_TO_ALBUM : null, !isPremium ? OverflowAction.ADD_TO_PLAYLIST : null, isNewPodcastControlsEnabled && isLongFormContent ? OverflowAction.VIEW_EPISODE_PAGE diff --git a/packages/mobile/src/screens/edit-track-screen/screens/ReleaseDateScreen.tsx b/packages/mobile/src/screens/edit-track-screen/screens/ReleaseDateScreen.tsx index 65e11c9c63d..3ec29024a11 100644 --- a/packages/mobile/src/screens/edit-track-screen/screens/ReleaseDateScreen.tsx +++ b/packages/mobile/src/screens/edit-track-screen/screens/ReleaseDateScreen.tsx @@ -121,7 +121,7 @@ export const ScheduledReleaseRadioField = (props) => { setReleaseDateValue(newReleaseDate.toString()) setIsTimeOpen(false) }, - [setReleaseDateValue, setIsTimeOpen] + [releaseDateValue, setReleaseDateValue] ) return ( diff --git a/packages/mobile/src/screens/root-screen/RootScreen.tsx b/packages/mobile/src/screens/root-screen/RootScreen.tsx index 3a05328c93d..93773e5211e 100644 --- a/packages/mobile/src/screens/root-screen/RootScreen.tsx +++ b/packages/mobile/src/screens/root-screen/RootScreen.tsx @@ -15,6 +15,7 @@ import useAppState from 'app/hooks/useAppState' import { useFeatureFlag } from 'app/hooks/useRemoteConfig' import { useUpdateRequired } from 'app/hooks/useUpdateRequired' import { useSyncCodePush } from 'app/screens/root-screen/useSyncCodePush' +import { SignOnScreen } from 'app/screens/signon' import { SplashScreen } from 'app/screens/splash-screen' import { UpdateRequiredScreen, @@ -25,7 +26,6 @@ import { enterBackground, enterForeground } from 'app/store/lifecycle/actions' import { AppDrawerScreen } from '../app-drawer-screen' import { ResetPasswordModalScreen } from '../reset-password-screen' import { SignOnStack } from '../sign-on-screen' -import SignOn from '../signon/SignOn' import { StatusBar } from './StatusBar' @@ -120,9 +120,9 @@ export const RootScreen = () => { ) : showHomeStack ? ( ) : isSignUpRedesignEnabled ? ( - + ) : ( - + )} { const isLongFormContent = genre === Genre.PODCASTS || genre === Genre.AUDIOBOOKS + const addToAlbumAction = + isEditAlbumsEnabled && isOwner ? OverflowAction.ADD_TO_ALBUM : null const addToPlaylistAction = !isPremium ? OverflowAction.ADD_TO_PLAYLIST : null const overflowActions = [ + addToAlbumAction, addToPlaylistAction, isOwner ? null diff --git a/packages/mobile/src/screens/user-list-screen/UserList.tsx b/packages/mobile/src/screens/user-list-screen/UserList.tsx index d4f496b230d..cd711426f59 100644 --- a/packages/mobile/src/screens/user-list-screen/UserList.tsx +++ b/packages/mobile/src/screens/user-list-screen/UserList.tsx @@ -130,11 +130,12 @@ export const UserList = (props: UserListProps) => { }, [hasMore, isFocused, dispatch, tag]) const shouldUseCachedData = isEmpty || isRefreshing || loading || !isFocused + const showSkeletons = hasMore || loading const data = useMemo(() => { const userData = shouldUseCachedData ? cachedUsers.current : users - return [...userData, ...skeletonData] - }, [shouldUseCachedData, users]) + return [...userData, ...(showSkeletons ? skeletonData : [])] + }, [shouldUseCachedData, users, showSkeletons]) const renderItem = useCallback( ({ item }) => @@ -147,20 +148,6 @@ export const UserList = (props: UserListProps) => { [tag] ) - const getItemLayout = useCallback( - (_data: User[], index: number) => { - const itemHeight = ['SUPPORTING', 'TOP SUPPORTERS'].includes(tag) - ? 167 - : 147 - return { - length: itemHeight, - offset: itemHeight * index, - index - } - }, - [tag] - ) - const loadingSpinner = ( { data={data} renderItem={renderItem} keyExtractor={keyExtractor} - getItemLayout={getItemLayout} ItemSeparatorComponent={Divider} onEndReached={handleEndReached} onEndReachedThreshold={3} diff --git a/packages/mobile/src/screens/user-list-screen/UserListItemSkeleton.tsx b/packages/mobile/src/screens/user-list-screen/UserListItemSkeleton.tsx index 48a3a9cdeda..4fcabb49d11 100644 --- a/packages/mobile/src/screens/user-list-screen/UserListItemSkeleton.tsx +++ b/packages/mobile/src/screens/user-list-screen/UserListItemSkeleton.tsx @@ -1,6 +1,6 @@ import { css } from '@emotion/native' -import { Box, Flex } from '@audius/harmony-native' +import { Flex, useTheme } from '@audius/harmony-native' import { StaticSkeleton } from 'app/components/skeleton' type Props = { @@ -14,26 +14,31 @@ export const UserListItemSkeleton = (props: Props) => { const { tag } = props const isSupporterTile = ['SUPPORTING', 'TOP SUPPORTERS'].includes(tag) const itemHeight = isSupporterTile ? 167 : 147 + const { cornerRadius } = useTheme() return ( - + - - + + - + {isSupporterTile ? ( - - - + + + ) : null} - + ) } diff --git a/packages/mobile/src/store/cache/collections/createPlaylistRequestedSaga.ts b/packages/mobile/src/store/cache/collections/createPlaylistRequestedSaga.ts index a36f52ba903..93a4d0b7b37 100644 --- a/packages/mobile/src/store/cache/collections/createPlaylistRequestedSaga.ts +++ b/packages/mobile/src/store/cache/collections/createPlaylistRequestedSaga.ts @@ -6,7 +6,8 @@ import { navigationRef } from 'app/components/navigation-container/NavigationCon const { registerToast } = toastActions const messages = { - createdToast: 'Playlist Created!', + createdToast: (isAlbum: boolean) => + `${isAlbum ? 'Album' : 'Playlist'} Created`, view: 'View' } @@ -16,13 +17,13 @@ export function* createPlaylistRequestedSaga() { function* ( action: ReturnType ) { - const { playlistId, noticeType } = action + const { playlistId, noticeType, isAlbum } = action switch (noticeType) { case 'toast': { yield* put( registerToast({ - content: messages.createdToast, + content: messages.createdToast(isAlbum), key: uuid(), linkText: messages.view, linkConfig: { screen: 'Collection', params: { id: playlistId } } diff --git a/packages/sql-ts/package.json b/packages/sql-ts/package.json index 904161cbe05..85e4e031052 100644 --- a/packages/sql-ts/package.json +++ b/packages/sql-ts/package.json @@ -8,11 +8,11 @@ }, "author": "", "license": "ISC", - "dependencies": { + "devDependencies": { "@rmp135/sql-ts": "1.18.0", + "dotenv": "^16.3.1", "knex": "2.5.1", "pg": "8.11.3" }, - "main": "src/index.ts", - "devDependencies": {} -} + "main": "src/index.ts" +} \ No newline at end of file diff --git a/packages/sql-ts/src/index.ts b/packages/sql-ts/src/index.ts index e4c02f7cf2a..7abfa91c5e6 100644 --- a/packages/sql-ts/src/index.ts +++ b/packages/sql-ts/src/index.ts @@ -1,17 +1,20 @@ +import 'dotenv/config' + import Knex from 'knex' import sqlts from '@rmp135/sql-ts' import { writeFileSync } from 'fs' const kpg = Knex({ client: 'pg', - connection: process.env.audius_db_url, + connection: process.env.audius_db_url }) async function updateDbTypes() { const config = { singularTableNames: true, + globalOptionality: 'required' as 'required', tableNameCasing: 'pascal', - interfaceNameFormat: '${table}Row', + interfaceNameFormat: '${table}Row' } const tsString = await sqlts.toTypeScript(config, kpg as any) console.log(tsString) diff --git a/packages/web/src/common/store/cache/collections/createPlaylistRequestedSaga.ts b/packages/web/src/common/store/cache/collections/createPlaylistRequestedSaga.ts index 12d0dba190a..8f743907d4f 100644 --- a/packages/web/src/common/store/cache/collections/createPlaylistRequestedSaga.ts +++ b/packages/web/src/common/store/cache/collections/createPlaylistRequestedSaga.ts @@ -10,7 +10,8 @@ const { toast } = toastActions const { getCollection } = cacheCollectionsSelectors const messages = { - createdToast: 'Playlist Created!', + createdToast: (isAlbum: boolean) => + `${isAlbum ? 'Album' : 'Playlist'} Created`, view: 'View' } // Either route user to created playlist page, or post a toast @@ -20,7 +21,7 @@ export function* createPlaylistRequestedSaga() { function* ( action: ReturnType ) { - const { playlistId, noticeType } = action + const { playlistId, noticeType, isAlbum } = action const playlist = yield* select(getCollection, { id: playlistId }) if (!playlist?.permalink) return @@ -30,7 +31,7 @@ export function* createPlaylistRequestedSaga() { case 'toast': { yield* put( toast({ - content: messages.createdToast, + content: messages.createdToast(isAlbum), linkText: messages.view, link: permalink }) diff --git a/packages/web/src/common/store/cache/collections/createPlaylistSaga.ts b/packages/web/src/common/store/cache/collections/createPlaylistSaga.ts index f8859ef5d88..b3c0b754d17 100644 --- a/packages/web/src/common/store/cache/collections/createPlaylistSaga.ts +++ b/packages/web/src/common/store/cache/collections/createPlaylistSaga.ts @@ -77,7 +77,11 @@ function* createPlaylistWorker( yield* call(optimisticallySavePlaylist, collectionId, collection, initTrack) yield* put( - cacheCollectionsActions.createPlaylistRequested(collectionId, noticeType) + cacheCollectionsActions.createPlaylistRequested( + collectionId, + noticeType, + isAlbum + ) ) yield* call( createAndConfirmPlaylist, diff --git a/packages/web/src/common/store/saved-collections/actions.ts b/packages/web/src/common/store/saved-collections/actions.ts new file mode 100644 index 00000000000..e40999e6661 --- /dev/null +++ b/packages/web/src/common/store/saved-collections/actions.ts @@ -0,0 +1,9 @@ +export const FETCH_ACCOUNT_COLLECTIONS = 'SAVED_COLLECTIONS/FETCH_COLLECTIONS' + +export type FetchAccountCollectionsAction = { + type: typeof FETCH_ACCOUNT_COLLECTIONS +} + +export function fetchAccountCollections(): FetchAccountCollectionsAction { + return { type: FETCH_ACCOUNT_COLLECTIONS } +} diff --git a/packages/web/src/common/store/saved-collections/sagas.ts b/packages/web/src/common/store/saved-collections/sagas.ts index 6c0a3b0fb78..5f2e964c2dc 100644 --- a/packages/web/src/common/store/saved-collections/sagas.ts +++ b/packages/web/src/common/store/saved-collections/sagas.ts @@ -9,6 +9,8 @@ import { all, call, select, put, takeEvery } from 'typed-redux-saga' import { retrieveCollections } from '../cache/collections/utils' +import { FETCH_ACCOUNT_COLLECTIONS } from './actions' + const { fetchCollections, fetchCollectionsSucceeded } = savedCollectionsActions const { getAccountAlbums, getAccountPlaylists } = savedCollectionsSelectors @@ -51,6 +53,10 @@ export function* fetchAllAccountCollections() { ]) } +function* watchFetchAccountCollections() { + yield* takeEvery(FETCH_ACCOUNT_COLLECTIONS, fetchAllAccountCollections) +} + function* watchFetchCollections() { yield* takeEvery( fetchCollections.type, @@ -61,5 +67,5 @@ function* watchFetchCollections() { } export default function sagas() { - return [watchFetchCollections] + return [watchFetchAccountCollections, watchFetchCollections] } diff --git a/packages/web/src/components/add-funds-modal/AddFundsModal.tsx b/packages/web/src/components/add-funds-modal/AddFundsModal.tsx index fdca798b4be..19244ad7f8c 100644 --- a/packages/web/src/components/add-funds-modal/AddFundsModal.tsx +++ b/packages/web/src/components/add-funds-modal/AddFundsModal.tsx @@ -5,17 +5,11 @@ import { buyUSDCActions, PurchaseMethod, DEFAULT_PURCHASE_AMOUNT_CENTS, - PurchaseVendor, - PayExtraPreset, - getExtraAmount, - StringKeys, - usePayExtraPresets + PurchaseVendor } from '@audius/common' import { ModalContent, ModalHeader } from '@audius/stems' import cn from 'classnames' -import { Formik } from 'formik' import { useDispatch } from 'react-redux' -import { toFormikValidationSchema } from 'zod-formik-adapter' import { AddFunds } from 'components/add-funds/AddFunds' import { Text } from 'components/typography' @@ -25,7 +19,6 @@ import { isMobile } from 'utils/clientUtil' import zIndex from 'utils/zIndex' import styles from './AddFundsModal.module.css' -import { AddFundsSchema } from './validation' const messages = { addFunds: 'Add Funds', @@ -40,30 +33,13 @@ export const AddFundsModal = () => { const mobile = isMobile() const [page, setPage] = useState('add-funds') - const presetValues = usePayExtraPresets( - StringKeys.COINFLOW_ADD_FUNDS_PRESET_CENT_AMOUNTS - ) - const initialValues = { - AMOUNT_PRESET: PayExtraPreset.NONE, - CUSTOM_AMOUNT: undefined - } const handleClosed = useCallback(() => { setPage('add-funds') }, [setPage]) const handleContinue = useCallback( - ({ - purchaseMethod, - purchaseVendor, - amountPreset = PayExtraPreset.NONE, - customAmount - }: { - purchaseMethod: PurchaseMethod - purchaseVendor?: PurchaseVendor - amountPreset?: PayExtraPreset - customAmount?: number - }) => { + (purchaseMethod: PurchaseMethod, purchaseVendor?: PurchaseVendor) => { switch (purchaseMethod) { case PurchaseMethod.CRYPTO: setPage('crypto-transfer') @@ -73,14 +49,7 @@ export const AddFundsModal = () => { buyUSDCActions.onrampOpened({ vendor: purchaseVendor || PurchaseVendor.STRIPE, purchaseInfo: { - desiredAmount: - amountPreset !== PayExtraPreset.NONE - ? getExtraAmount({ - amountPreset, - presetValues, - customAmount - }) - : DEFAULT_PURCHASE_AMOUNT_CENTS + desiredAmount: DEFAULT_PURCHASE_AMOUNT_CENTS } }) ) @@ -90,7 +59,7 @@ export const AddFundsModal = () => { throw new Error('Add funds not supported with existing balance') } }, - [dispatch, presetValues] + [setPage, dispatch] ) return ( @@ -122,13 +91,7 @@ export const AddFundsModal = () => { {page === 'add-funds' ? ( - undefined} // Not using formik for submit - > - - + ) : ( setPage('add-funds')} /> )} diff --git a/packages/web/src/components/add-funds-modal/constants.ts b/packages/web/src/components/add-funds-modal/constants.ts deleted file mode 100644 index 60d9b0e2313..00000000000 --- a/packages/web/src/components/add-funds-modal/constants.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const AMOUNT_PRESET = 'amountPreset' -export const CUSTOM_AMOUNT = 'customAmount' - -// Pay between $1 and $100 extra -export const minimumPayExtraAmountCents = 100 -export const maximumPayExtraAmountCents = 10000 - -export const CENTS_TO_USDC_MULTIPLIER = 10000 -export const DEFAULT_PURCHASE_AMOUNT_CENTS = 10 * 100 diff --git a/packages/web/src/components/add-funds-modal/types.ts b/packages/web/src/components/add-funds-modal/types.ts deleted file mode 100644 index 3a09c8c6195..00000000000 --- a/packages/web/src/components/add-funds-modal/types.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** Denotes the 3 preset amounts to show on the form, values are in cents. */ -export type PayExtraAmountPresetValues = { - [PayExtraPreset.LOW]: number - [PayExtraPreset.MEDIUM]: number - [PayExtraPreset.HIGH]: number -} - -export enum PayExtraPreset { - LOW = 'low', - MEDIUM = 'medium', - HIGH = 'high', - CUSTOM = 'custom', - NONE = 'none' -} diff --git a/packages/web/src/components/add-funds-modal/validation.ts b/packages/web/src/components/add-funds-modal/validation.ts deleted file mode 100644 index 4d4e051e40b..00000000000 --- a/packages/web/src/components/add-funds-modal/validation.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { z } from 'zod' - -import { - AMOUNT_PRESET, - CUSTOM_AMOUNT, - maximumPayExtraAmountCents, - minimumPayExtraAmountCents -} from './constants' -import { PayExtraPreset } from './types' - -const messages = { - amountInvalid: 'Please specify an amount between $1 and $100' -} - -const createPurchaseContentSchema = () => { - return z - .object({ - [CUSTOM_AMOUNT]: z - .number({ - required_error: messages.amountInvalid, - invalid_type_error: messages.amountInvalid - }) - .optional(), - [AMOUNT_PRESET]: z.nativeEnum(PayExtraPreset) - }) - .refine( - ({ amountPreset, customAmount }) => { - if (amountPreset !== PayExtraPreset.CUSTOM) return true - return ( - customAmount && - customAmount >= minimumPayExtraAmountCents && - customAmount <= maximumPayExtraAmountCents - ) - }, - { message: messages.amountInvalid, path: [CUSTOM_AMOUNT] } - ) -} - -export const AddFundsSchema = createPurchaseContentSchema() -export type AddFundsValues = z.input diff --git a/packages/web/src/components/add-funds/AddFunds.tsx b/packages/web/src/components/add-funds/AddFunds.tsx index 513cf90f5bc..d032d16a0e0 100644 --- a/packages/web/src/components/add-funds/AddFunds.tsx +++ b/packages/web/src/components/add-funds/AddFunds.tsx @@ -4,9 +4,7 @@ import { PurchaseMethod, PurchaseVendor, useCreateUserbankIfNeeded, - useUSDCBalance, - PayExtraPreset, - CUSTOM_AMOUNT + useUSDCBalance } from '@audius/common' import { USDC } from '@audius/fixed-decimal' import { @@ -19,9 +17,7 @@ import { } from '@audius/harmony' import { BN } from 'bn.js' import cn from 'classnames' -import { useField } from 'formik' -import { AMOUNT_PRESET } from 'components/add-funds-modal/constants' import { PaymentMethod } from 'components/payment-method/PaymentMethod' import { track } from 'services/analytics' import { audiusBackendInstance } from 'services/audius-backend/audius-backend-instance' @@ -37,17 +33,10 @@ const messages = { export const AddFunds = ({ onContinue }: { - onContinue: ({ - purchaseMethod, - purchaseVendor, - amountPreset, - customAmount - }: { - purchaseMethod: PurchaseMethod + onContinue: ( + purchaseMethod: PurchaseMethod, purchaseVendor?: PurchaseVendor - amountPreset?: PayExtraPreset - customAmount?: number - }) => void + ) => void }) => { useCreateUserbankIfNeeded({ recordAnalytics: track, @@ -59,8 +48,6 @@ export const AddFunds = ({ const [selectedPurchaseVendor, setSelectedPurchaseVendor] = useState< PurchaseVendor | undefined >(undefined) - const [{ value: amountPreset }, ,] = useField(AMOUNT_PRESET) - const [{ value: customAmount }, ,] = useField(CUSTOM_AMOUNT) const mobile = isMobile() const { data: balanceBN } = useUSDCBalance({ isPolling: true }) @@ -95,21 +82,14 @@ export const AddFunds = ({