From d2978dbb359827f2190424904e563f08cee10efe Mon Sep 17 00:00:00 2001 From: Ben Goldberg Date: Mon, 8 Aug 2022 20:37:19 -0700 Subject: [PATCH] Profiles: Checkpoint (#3431) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Profiles: improve ens search (#3120) * fetch avatar when searching * seach up to 8 * use ens records enum * check profiles enabled * profiles enabled * Profiles: Log upload image errors (#3173) * Profiles: phase two final review comments (#3176) * https://github.com/rainbow-me/rainbow/pull/2979\#discussion_r841382298 * https://github.com/rainbow-me/rainbow/pull/2979\#discussion_r844576102 * ens records lang * https://github.com/rainbow-me/rainbow/pull/2979\#discussion_r845569875 * https://github.com/rainbow-me/rainbow/pull/2979\#discussion_r845725066 * use getENSRegistrarControllerContract * tweak paddings and register screen * export and remove * Profiles: Only show ENS on-chain data warning once per user (#3170) * Save onchain data disclaimer state * Fixes * Profiles: merge ens phase 2 (#3178) * Refactor postinstall.sh to reduce code duplication and improve error handling (#3132) * Refactor postinstall.sh to reduce code duplication and improve error handling * Clean whitespace * Fix broken line break * Fix possibility to scroll with scroll indicator on unique asset image preview (#3150) Co-authored-by: Jakub Adamczyk * Remove smile emoji from label (#3160) * Profiles: adjust confirm screen in small screens (#3163) * Countdown Timer Component for ENS registration (#3158) * Countdown Timer Component for ENS registration * codeowners + change export * convert components to TS + DS * Fix hourglass animation on android * address PR comments (#3171) * exclude pin creation for isReadOnly mode (#3161) * Update audit-ci allowlist (#3175) Co-authored-by: jinchung * Profiles: Log upload image errors (#3173) * Profiles: phase two final review comments (#3176) * https://github.com/rainbow-me/rainbow/pull/2979\#discussion_r841382298 * https://github.com/rainbow-me/rainbow/pull/2979\#discussion_r844576102 * ens records lang * https://github.com/rainbow-me/rainbow/pull/2979\#discussion_r845569875 * https://github.com/rainbow-me/rainbow/pull/2979\#discussion_r845725066 * use getENSRegistrarControllerContract * tweak paddings and register screen * export and remove Co-authored-by: Tomasz Czajęcki Co-authored-by: Jakub Adamczyk Co-authored-by: Jakub Adamczyk Co-authored-by: Wojtek Witkowski Co-authored-by: moxey.eth Co-authored-by: Taras Perun <48593211+perunt@users.noreply.github.com> Co-authored-by: Bruno Barbieri Co-authored-by: jinchung * Update ProfileSheet header (#3174) * Profiles: Fix keyboard inconsistencies (#3169) * fix keyboard issues * Fixes * fix android crash * Profiles: Fallback to ENS metadata API when OpenSea metadata not available yet (#3148) * Add fallback for ENS metadata * refactor * refactor * Fix * Profiles: Misc fixes (#3193) * fix keyboard issues * Fixes * fix android crash * RNBW-3263 * RNBW-3251 * Fixes * More fixes * Fix * remove hideAvatar * Update confirm sheet ui (#3197) * Profiles: additional records sheet (#3172) * add routes and additional records wip * additional records * default config sheet * add more records * TextEncoder polyfill * content hash validations * adjust height * move validation to one method * move import fast-text-encoding * typo * isPrimaryDisplayRecord * dotsButtonIsSelected * handle additional ens sheet height * lint * fix bad renaming * get fixed values with getENSRegistrationGasLimit * remove expired incomplete ens registrations (#3167) * Profiles: separate data for incomplete profiles by ens name (#3182) * Profiles: several bug fixes (#3190) * RNBW-3264 * RNBW-3263 * RNBW-3241 * RNBW-3223 * do not show empty avatar in pending registrations * RNBW-3224 * fixit * add option to extend any ens name * rename from comments * add recordKeysWithValue * Profiles: ens navigator horizontal swipe (#3205) * enable stack swipe if name available * fix is testing * Profiles: improve gas handling (#3183) * get fixed values with getENSRegistrationGasLimit * usequeries for each tx * working * estimate gas txs * use recoil to store gas validation * commit working * do not update fee on confirm * update records * step gas limit working * edit working * renew working * records * getQueryData refactor * getGasParams * get gas params * Revert "get gas params" This reverts commit b9bbc911fad2c8013581d280ee783581b7f46ff4. * Revert "getGasParams" This reverts commit 03d494848af98c3a91e46bd392f62aeb782f322d. * Revert "getQueryData refactor" This reverts commit f58e05f7fadca0c5d0df7f1d0ad3b8e5192bb875. * handle gas updates in background * use query all around * fix insufficient ETH in commit * remove gas params request * use reverse record from hook * remove recoil * merge * istesting * Profiles: 60 secs wait from COMMIT tx (#3113) * check 60 secs passed on a block level: * ens wait back to 60 secs again * a bit cleanerr * ready to register if IS_TESTING * check tests * add comments * promise all * getBlockMsTimestamp * Drop metadata.ens.domains fallback in favour of The Graph + ImgixImg (#3211) * Profiles: primary ens name explainer sheet (#3191) * ENS Profile zoomable images (#3184) * Update header * Extend ImagePreviewOverlay to work for multiple instances * remove redundant animationProgress * PR comments * Fix * Fixes * Profiles: Make ordering of action buttons match order of fields (#3212) * Make ordering of action buttons match order of fields * oops * Remove lines from gradient (#3222) * Profiles: Intro Screen polishhh (#3221) * Polish * Revert * Profiles: bio hyperlinks (#3218) * get accent color in ens confirm * add hyperlinks * recordsDescription * create own components * handling links correctly * add additional height to profile intro * fix regex * hyper link weight regular * Profiles: set primary from expanded ens (#3194) * add switch * send to primary tx confirmation * with explainer * ENS Registration Steps Progress Component (#3210) * ENS Registration Steps Progress Component * Use accentColor * isAnimatingFill change * add paddings * Revert "isAnimatingFill change" This reverts commit d1408d3eacab9fe57e9fa0577b8842951a6cc5c3. * use magic memo + padding * step Co-authored-by: Esteban Miño * Profiles: intro screen updates (#3219) * use primary name if any * add context menu * lang * magnifyingglass.circle * add missing util * Profiles polish (#3225) * fetch account ens in register ens section * revalidateCollectibleInBackground only for unknown ens * remove staletime * Profiles: merge phase 2 to phase 3 (#3227) * merge * import text * add line to auditci * remove old ens * remove old ens * usePersistentDominantColorFromImage with lowResUrl * Fix ENS NFT regressions (#3230) Co-authored-by: moxey.eth * Profiles: fix unintended side effects of TokenInfoItem changes (#3228) * Fix zoomable images on intro screen (#3231) * Profiles: Choose NFT as cover photo (#3232) * WIP * WIP * WIP * WIP * Fixes * Profiles: Various fixes (#3239) * Fix * Fix * Profiles: improve ens discovery (#3240) * Profiles: don't normalize search input display text (#3226) * Pulsing checkmark when visible for more than 2s (#3251) * Profiles: improve costs hook (#3238) * wip * add memos * revert some changes * more cleanup * sign fix * use step in ens search costs hook * use step in ens search costs hook * Profiles: more fixes (#3252) * more fixes * use accent color when speed up correctly * enable watcher * add ens_domain * rm fix * Fix scroll issues (#3257) * Profiles: final flows e2e (#3248) * Refactor postinstall.sh to reduce code duplication and improve error handling (#3132) * Refactor postinstall.sh to reduce code duplication and improve error handling * Clean whitespace * Fix broken line break * Fix possibility to scroll with scroll indicator on unique asset image preview (#3150) Co-authored-by: Jakub Adamczyk * Remove smile emoji from label (#3160) * Profiles: adjust confirm screen in small screens (#3163) * Countdown Timer Component for ENS registration (#3158) * Countdown Timer Component for ENS registration * codeowners + change export * convert components to TS + DS * Fix hourglass animation on android * address PR comments (#3171) * exclude pin creation for isReadOnly mode (#3161) * Update audit-ci allowlist (#3175) Co-authored-by: jinchung * Profiles: Log upload image errors (#3173) * Fix blank charts (#3107) * Do not rerender chart if it's empty * make linter happy * Add additional memo * Profiles: phase two final review comments (#3176) * https://github.com/rainbow-me/rainbow/pull/2979\#discussion_r841382298 * https://github.com/rainbow-me/rainbow/pull/2979\#discussion_r844576102 * ens records lang * https://github.com/rainbow-me/rainbow/pull/2979\#discussion_r845569875 * https://github.com/rainbow-me/rainbow/pull/2979\#discussion_r845725066 * use getENSRegistrarControllerContract * tweak paddings and register screen * export and remove * Bump moment from 2.29.1 to 2.29.2 (#3177) * Fedora V1 (#3149) * Fix vulnerability in 2.x async (#3186) * update sendable check (#3165) * update sendable check * add erc721 to sendable nft types * Add Fedora branch indicator (#3185) * add another async vuln (#3198) * Bump ios and android version; update CHANGELOG (#3195) * Update audit deps (#3202) * image handling fixes + refactor (#3188) * getFullSIzeUrl + low res size tweaks * handleAndSIgnImages in parsing * better option handling * rm legacy logic * tweak poap to match * rm legacy size props * ts * rm extra logic and use lowResUrls * revert poap change oops * readd for background * lowRes for blurWrapper & color fallback * Fix: android disconnecting messages (#3189) * get meta from route * useeffect * add android specific * handle repeated uris * Fix sky06 color (rgb → rgba) (#3208) * Do not remount rows of RecyclerListView (#3207) * Do not remount rows of RecyclerListView * Bring back assertNever * kidding me? * Update rebase (#3214) * Update rebase.yml (#3216) * Profiles: 60 secs wait from COMMIT tx (#3113) * check 60 secs passed on a block level: * ens wait back to 60 secs again * a bit cleanerr * ready to register if IS_TESTING * check tests * add comments * promise all * getBlockMsTimestamp * Update audit-ci.json (#3223) * @tchayen/fix swap gesture on expanded asset (#3187) * Fix swap gesture on expanded asset * Apply fix the real cause of the issue Co-authored-by: osdnk * add missing util * ignore async vuln which was already patched (#3229) * Revert "Do not remount rows of RecyclerListView (#3207)" This reverts commit 48a540ca4e79028baf366273c23e07da09323a60. * we got an ens (#3234) * wip * save OG image url and fix polygon imgs * add memos * revert some changes * more cleanup * use MD5 through JSI with react-native-quick-md5 (#3224) * Finish Sentry Performance setup (#3181) * sign fix * use step in ens search costs hook * use step in ens search costs hook * sign fix * use step in ens search costs hook * use step in ens search costs hook * REVERT PATCH COMMENT * rm is_testing ui conditions * REVERT RM ALL E2E BUT PROFILES * start from intro screen * divide hook for registration step and other for actions * working locally * lintt * marquee actions * confirming records * logs * tap by text * going to ENS nft * set as primary name from expanded state * remove avatar from flow for bitrise * add renew flow * maybe bitrise? * lint * fix lint * Choose NFT * add discover and taking out avatar from registration flow * add avatar anyways, passing locally * Revert "REVERT PATCH COMMENT" This reverts commit 58dde25d1a76b168af8c7c44f9dabfe3f73835a6. * Revert "REVERT RM ALL E2E BUT PROFILES" This reverts commit 958c78264a77809c6b3c4022f6757ead31d81282. * missing avatar * use accent color when speed up correctly * rollback useSelectImageMenu Co-authored-by: Tomasz Czajęcki Co-authored-by: Jakub Adamczyk Co-authored-by: Jakub Adamczyk Co-authored-by: Wojtek Witkowski Co-authored-by: moxey.eth Co-authored-by: Taras Perun <48593211+perunt@users.noreply.github.com> Co-authored-by: Bruno Barbieri Co-authored-by: jinchung Co-authored-by: Terry Sahaidak Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michał Osadnik Co-authored-by: Skylar Barrera Co-authored-by: Christian Baroni * Profiles: improve registrations fetch (#3259) * query for fetchImages * types * dault to localstorage while loading * put primary ens at begining * fast af * Add ability to remove contact from profile sheet (#3258) * Fix text record field state (#3256) * Profiles: clear current registration name when RegisterENSNavigator unmounts (#3244) * Profiles: tweak isLoading conditions in useENSRegistrationForm (#3250) * Profiles: performance improvements and stuff (#3263) * use images directly from resolved in useENSProfile * useMemo for initial values * disable swipe in ens navigator * REVERT enable ens for watch wallets * Revert "use images directly from resolved in useENSProfile" This reverts commit b9b96a8f770a3753b215c19853392fbe51aac255. * show all records in profile * use coin addresses and memo for values * only fetch records and coin addresses that we support * divide hooks to avoid duplication of calculations and redux calls * query only records in assign records * isloading condition * handle navigation goback * handle labels * Revert "REVERT enable ens for watch wallets" This reverts commit 383e1abcab4f0f57f9abcc38c05883bcfadfe444. * rollback hook isloading change * rename useENSAssignRegistration to useENSModifiedRegistration * ens records * ens records * Profiles: fix NFTs recycler list scroll handler in profiles sheet (#3266) * rm scroll handler * Fix Co-authored-by: moxey.eth * Profiles: Improve watch/unwatch flow (#3265) * Improve watch/unwatch flow * Update WatchButton.tsx * Profiles polish (#3269) * Improve GIF playback speed on ProMotion devices Bumps react-native-fast-image's SDWebImage dependency to include this change: https://github.com/SDWebImage/SDWebImage/pull/3280 There's still an issue where if a GIF is in a sheet and you're dragging the sheet, the speed changes while dragging, but otherwise this fixes the issue * Update TintButton styles * Adjust colors * Remove SearchFab * Clean up avatar, cover styles * Clean up discover * Fix wallet sheet "Watching" badge positioning, improve styles * Design system: add sizing, spacing values * Design system: adjust shadows and text, add text sizes * Clean up profiles * owner = registrant, manager = controller * Clean up copy * Fix ActionButton types * Account for reversed colors in lightGreyTransparent gradient * Polish intro screen, SelectENSSheet * Remove unused ColorModeProvider * Clean up discover search, ENS avatar shadows * Fix send contact list scrollview bounds KeyboardArea height was wrong, was causing watched wallets to be cut off by the keyboard * Fix swap button color regression Caused by https://github.com/rainbow-me/rainbow/pull/2989/files#diff-5408fa5b27b810b3ad4777e8659d9dc65f602f803134286fe1bac59167397c71R314 * Improve profiles status bar handling Fixes most cases where the status bar displays in the wrong color * Polish search component * Adjust gas button fee copy * Clean up ENS search screen * Clean up create profile screen, reorder records * Partially clean up confirm registration sheets * Add more intro screen wallets * Adjust intro screen content layout, fix MaskedView type warning * Improve profiles sheet transitions * Disable shortForm profile preview sheet after transition to longForm, adjust height * Fix Telegram record * Clean up ENS expanded state * Don't goBack() when editing an ENS name from the NFT expanded state * Fix missing NFT expanded state background images lowResUrl is more predictably a standard image file * Remove unused isENS on ZoomableWrapper * Fix ZoomableWrapper status bar handling * Prevent pinch to enter ZoomableWrappers on profiles * Use blurred background image in ImagePreviewOverlay, position overlay properly within sheets * Make sure ZoomableWrapper GestureBlocker covers the screen Fixes a bug where you could dismiss the sheet from the zoomed state of ZoomableWrapper by dragging from certain points * Remove unnecessary zIndex transition Seems to make the animation slightly less smooth, behavior is otherwise the same with or without this being set * Make ZoomableWrapper pan speed consistent Previously wasn't accounting for differing scale factors * Set ZoomableWrapper bounds correctly within sheets * Design system: use hex codes for solid colors * Match disabled step button color to spec * lint * comments Co-authored-by: Esteban Miño * Profiles: last bugs (#3274) * fix wrong accent color in confirm when selecting an image * clear name from confirm register sheet * ens assign records scrollable again * correct label when creating and editing * Fixup design system `Box` usage (#3275) * Fix issue where field would clear when navigating to confirm screen (#3276) * Profiles: ens profile go to NFTs from avatar / cover (#3272) * WIP * revert podfile * handle presss cover and avatar * lint * avatar / cover fade in * smooth * label on create / edit profile * divide usememo Co-authored-by: moxey.eth * Profiles: reorder records and add coins to profile (#3273) * reorder * hhandle addresses ub orifukes/; * Profiles: UI fixes (#3262) * remove search sheet divider if there are no pending registrations * change 'expires in' to 'expires on' when ens expanded state expiry item displays the expiration date instead of duration Co-authored-by: Esteban Miño * Profiles: autopopulate ens name and avatar when adding contact (#3264) * Profiles: more tweaks (#3278) * fix avatar image in previe * show primary name switch only in imported wallets * fix explainer * confirm sheet heights * get images urls on extend and set name * load title label when there's info to show * update e2e * fixes * fix avatar emojis not showing up * profile emoji avatar * Revert "fix avatar emojis not showing up" This reverts commit 897ab142c45ca48690c970dc8504dccea3e4919a. * nft setting is external correctly * images pofile and intro * is external only when address is different than account address * allow ens extension in ens name * handle gas limit for renew and external colors * externalAvatarUrlAtom * reset externalAvatarUrlAtom in search * block set name if external profile * reset external color * fix gas estimation not valid * clear name on confirm for set name and renew * enable profile info section for cover * onpress changes * rm externalAvatarUrlAtom * rm externalAvatarUrlAtom * lint * revert set name calculations * add accent color to hourglass Co-authored-by: Ben Goldberg * lint * Fix flickering keyboard (#3296) * Profiles: fix records updates lag (#3287) * align confirm title center * startRegistration in search before continue to the rest of flows * fix register gas limit calculation wasn't triggering * update query key * rm prev dominant color * set image accent color when there's an image * fix back button comment * Profiles: improve registrations in progress (#3295) * support initial minutes and secods in large countdown clock * use changedRecords not beounced records to estimate gas * display remaining seconds to continue registration * export seconds since commit confirmed * fix profile hook isOwner * Profiles: intro screen jump fix (#3302) * fix jump * use screenOptions * assign records title and accent colors * align contact modal + import wallet modal (#3285) * align contact modal + import wallet modal * image avatar tweak * fix spacing * view profile from send sheet contact * tweak delete contact from send sheet * fix send header input bug * lint * ts * starting again from earlier state * address comments * store ens in contact * tweak updating of ens in contacts * fix flash of ens->contact nickname in send sheet header * add nickname to modal Co-authored-by: Esteban Miño * Profiles: Fix edit profile infinite loading state (#3312) * fix issue where edit profile had infinite loading state * PR review * Profiles: Add better caching for ENS avatar/cover images (#3309) * Add caching to ENS images * PR review * fix merge regression * fix issue where edit profile had infinite loading state * Prefetch images on avatar press * lint + @types/qs * Profiles: update analytics (#3314) * check ens info all wallets * names owned * track properties * tx actions analytics * final event triggers * update number of * Profiles: e2e passing in bitrise (#3320) * passing locally * REVERT * fix lint * dont use context menu * max priority fee * just urgent * discover e2e update * Revert "REVERT" This reverts commit 3c68acc656eb550c0aee5f543aae7d8c9c771920. * revert some changes for testing * fix lint cycle in hooks * lint again * clear keychain on init * fix zoomable wrapper undefined opacity * e2e fixes * send test passing * images in send contacts suggestions * prefetch images on send * profiles enabled deeplink * removing all e2e but deeplinks * use flag value * dont wait for matic * Revert "removing all e2e but deeplinks" This reverts commit eb8fc8875312ff44ce37c5603b5a803aafb676cf. * PR feedback * Fix RAL context values (#3332) * Profiles: Fix small device UI issues (#3346) * Fix undefined opacity animated value * Fix iPhone SE issues * Fix * Profiles: qa fixes phase three (#3347) * update isUnknownOpenSeaENS * fix speed up * getProviderForNetwork * move android stack * rollbacks * Fixes RNBW-3624 * Profiles: Address phase 3 PR review comments (#3338) * Address PR feedback * Update _french.json * Profiles: Convert ENS sheets to TypeScript (#3341) * Convert sheets to typescript * Fix any type * Convert RegisterENSNavigator to TS * Profiles: small phones alignments fix (#3355) * wip * merge * lint * Profiles: android register crash + tweaks (#3357) * fix authentication crash on register * fix my ens names sheet background * reverse record toggle * dismiss keyboard when going to pending regiistration on android * fix android presets * minheight for intro screen * lint * Profiles: send sheet fixes (#3356) * Profiles: Fetch recently registered ENS NFTs (#3349) * Fetch recently registered ENS NFTs * Fix * return empty array * fix * fallback to simplehash (#3334) * Profiles: Improve sheet styling on Android (#3359) * Profiles: assign record sheet fixes (#3358) * fix formatRecordsForTransaction so it doesn't filter out removal of records * micro change * fix bug where uploaded cover photo does not display * Profiles: fix swap estimation + opensea link for new ens (#3362) * Profiles: Fix intro sheet height for small devices (#3364) * Fix sheet height for small devices * prolly don't need context * Profiles: More pixel pushing for small devices (#3365) * Fix incorrect offset calculation * Fix action sheet height for small devices * Profiles: last qa fixes (#3361) * pull records when editing ens * take duration from hook instead of registration params * fix gas estimations when setting records * take external avatar url first to get dominant color * take external avatar url first to get dominant color revert * Fixes 3635 * isUnknownOpenSeaENS optional checks * more contacts * only use initial records * add recipient to send header * isempty rm * Profiles: android Omit isPreview flag for intro screen preview profiles (#3366) * Profiles: disable step indicator android (#3371) * Profiles: change ENSConfirmRenewSheetHeight on android (#3372) * Profiles: last last qa fixes (#3363) * Profiles: Increase ENS fallback time (#3375) * Profiles: add ENS NFT fallback to profile sheet & use `handleAndSignImages` in fallback (#3376) * Use sign images in fallback & add fallback to profile sheet * add .name check Co-authored-by: Esteban Miño * Profiles: handle set primary name different flows (#3374) * @esteban/reverse-record-fix * remove web3Provider in handlers/ens * use reverse record to update name * update wallet avatars on refresh * fetchReverseRecordWithRetry * utils * handle empty account address * rm inprimary name from profile, get it on useEnsProfile instead * fetchensavatars * add logs * logz * logz * more logs * more logs * more logs * fix * fix * fix * fix * fix * add profile flag to load state * lint * rm hook * primary name is set name enabled * remove param from fetchENSProfile * account ens first * fix test * label first than ens again * Remove send button if watching profile from a read-only wallet * Update BTC record up to 42 characters * Update LTC record up to 64 characters * Profiles: more profiles (#3378) * fix all registrations being fetched * fix poap crash * fix reload and change wallet * remove ETH from initialRecords * add gas and value validation for renew * Profiles: records and gas fee panel updates (#3379) * improve records * improve gas updates * timeouts * back to timeout 200 * Fix cover preview overlay opacity (#3386) * profiles: filter non owned ens (#3388) * filter it * ENS_SUGGESTIONS with owner * Profiles: final review comments phase three (#3392) * chaining and remove unused method * get accountAddress from getState.settings * ensSeenOnchainDataDisclaimerKey * getWalletENSAvatars refactor * usePendingRegistrations * toLowerCase * optional chaining * oneliners ftw * timeout and EIP155_FORMATTED_AVATAR_RECORD * opt chaining again * gradients.checkmarkAnimation * selected * remove ownerAddress * use fetchReverseRecord instead of looup in handlers/ens * remove updateRegistrationDuration * rm scrollIndicatorInsets * add afterall back * lint * Profiles: Fix ENS navigator on android (#3405) * Fix ENS navigator on android * android top style Co-authored-by: Esteban Miño * profiles set records estimations tweaks (#3412) * tweaksss * CODEOWNERS * init pr * Fix laggy text record fields (#3422) * Fix issue where quickly watching and unwatching wallets wouldn't unwatch the wallet (#3418) * remvoe comment * profiles: remove hardcoded @ in username records (#3438) * remove$ * end of line * prevent loading placeholder from getting cut off (#3414) * profiles: release bugz (#3426) * RNBW-3722 * RNBW-3741 * better profilesEnabled conditions for hooks * refactors * RNBW-3728 * Rework StepIndicator to stop crashing on Android (#3437) * Profiles: add spellcheck to some record inputs (#3442) * Profiles: improve intro screen load performance (#3423) * Improve intro screen load performance * PR review * fix lint error * revert * more lint * Profiles: Fix SVG NFT avatars & filter out non-image NFTs (#3445) * Fixup imageUrl resolution for NFT avatar/cover * filter nfts * filter poaps and ens * fix blurry avatar/cover & filter out showcase * Fix profile avatar/cover issues (#3454) * Fix issue where expanding an NFT would sometimes produce a duplicate (#3453) * Profiles: Fix stale text record values (#3450) * Fix stale text record values * add key to TextRecordsForm to force a full rerender when the ens name changes Co-authored-by: Ben Goldberg * fix lint issues * Prefetch ENS records before "Edit Profile" (#3458) * Profiles: Open Wallet + Share web link for Profile menu (#3451) * Open Wallet + Share web link for Profile menu * Don’t reload selected wallet + cleanup * Refactor logic with available useWatchWallet hook * Update icon based on Christian’s feedback * Profiles: new testflight version (#3456) * prefetch and return error if no image resolved * remove prefetch * rm throw * patch * check profiles enabled * fix broken merge conflict * Fix ENS fallback when not available in subgraph yet (#3479) * remove advanced section from ens expanded state (#3492) * Fix ENS Registration Intro screen blank section (#3496) * Disable Profiles sheet bounce (#3497) * Fix edit mode default state race condition (#3501) * Profiles: Polish set primary name flows (#3474) * Polish set primary name flows * lint * PR feedback * fix * lint * update explainers (#3515) * use ens avatars in backup sheet (#3514) * Fix issue where sending L2 asset on intro screen profile crashed app (#3517) * disable 'Review' button until new avatar/cover has uploaded to pinata (#3518) * Fix e2e * fix brief blank intro profiles * Update avatars after registration/set records (#3520) * update react-native-pager-view (#3519) * prevent ExpandedAssetSheet from hiding when ENS is chosen (#3524) Co-authored-by: moxey.eth * don't attempt to prefetch ens profile if nft is not an ens (#3527) * Profiles: Limit opensea api requests (#3526) * limit opensea api requests * refactor * comment * Fix empty string crash * Fix vulnerability: * update lockfile * Profiles: ENS Search Sheet improvements (#3551) * parallelize some async operations in useENSSearch * fix searchinput maskelement on android * fix ens search lag * use promise.all * Profiles: Fix duplicate registrations (#3569) * Fix issue where user tries to register same name again while register txn is still pending * Fixes * Fixes * add lang * Profiles: Split up `useENSProfile` into separate hooks (#3592) * Segregate useENSProfile into separate hooks * Fix * More tweaks * fix lint * Fix issue where sending NFT would show error * PR review * fix .eth.eth issue (#3650) * PR review * fix translations merge conflict * fix type issue * Fix hourglass background * Fix lint * Fix ts * Profiles: ENS Send Flow (#3389) * Add ENS send flow * Fix ENS images * Add setAddr to action handlers * fix empty eth address * PR comments * refactor * Profiles: send ens flow gas (#3413) * WIP * wip * add gas speed button to ens send confirmation sheet * calculate cost of sending ens nft * calculate cost of setting ens name to recipient address * wip * Add ENS send flow * refactor gas estimation * include gas cost of clearing profile records * include gas cost of setting controller * fix * fix gas estimation for set name and set owner * rework gas estimation for clear-records and set-address (clearing ETH address still broken) * fix reset ETH record and multicall usage in setrecords gas estimation * remove console logs * remove merge leftovers * clean ens name * use add method from utilities in gasLimits reducer * make null GasSpeedButton parameters optional * estimateENSSetRecordsGasLimit refactor * revert shouldUseMulticallTransaction * get rid of shouldUseMulticall in favor of getTransactionTypeForRecords, which takes into account setAddr in addition to setText * set padded gas flag to true * fixes * use records from send screen * getRapActionTypeForTxType handler Co-authored-by: moxey.eth * PR feedback * ENS send flow e2e (#3467) * lint * e2e * e2e * optional chaining * fixes * owner test commented * uncomment some testS * remove patch * apply patch * repatch * patch * revert * reclaim * downgrade react-native-fast-image to get CI to install deps * PR feedback * uncomment e2e * use reclaim * revert react-native-fast-image downgrade * fix * fix * fix * fix * fix * ownerAddress -> toAddress * fix broken setAddr gas estimate * fix ui bugz * Fixes * update explainer * revert * lint * fix e2e * fix lint * fix lint * fix nonce * fix * PR review * Fix nonce issue * add feature flag to confirmation sheet * Fix double face id * Fix ts Co-authored-by: Ben Goldberg Co-authored-by: Esteban Miño * Profiles: New discover cards (#3537) * fix e2e * Add new discover cards, update layout * Restore PulseIndexSection * Update e2e testID for new profiles card * Lint * Fix ts errors * Fix import ordering * Clean up animated gwei timing function * Update e2e to account for removed search fab * fix e2e ids * Fix e2e * lil refactors & terrys comments * update icon text hierarchy * Get DPI asset from within handlePress * Clean up DPICard shadows * Clean up inline shadows, styles * Make getColorForGwei a worklet * Move gradient colors to theme * fix e2e * Fix e2e * Fix e2e * Fix e2e * Add @1x, @2x discover-profiles-card images * Add e2e coverage for ENS search card & gas card Co-authored-by: moxey.eth Co-authored-by: Christian Baroni <7061887+christianbaroni@users.noreply.github.com> * Profiles: Checkpoint 2 (#3565) * Profiles: Real time records validation (#3495) * real time validations * get rid of leftovers and add more validation error messages * Update src/hooks/useENSRegistrationForm.ts Co-authored-by: moxey.eth * disable 'Review' button if fields are not valid, fix discord regex * remove comment Co-authored-by: moxey.eth * Profiles: ENS Search Sheet improvements (#3551) * parallelize some async operations in useENSSearch * fix searchinput maskelement on android * fix ens search lag * use promise.all * Profiles: Support emoji avatars for ens owners w/o ens avatar (#3525) * support emoji/image avatars for ens profiles without ens avatar * use/edit ens profile depending on whether it exists already or not * merge ens domain filtering logic into useAccountENSDomains * remove cancel if no other options from avatar action sheet * Profiles: Fix nft upload flash when editing profile (#3541) * nft flash * add registration name to useEffect dependency array * fix svgs * Profiles: Remove NFT avatar/cover option if no NFTs (#3575) * remove nft option from selectimagemenu if not applicable * always show context menu if avatar/cover exists Co-authored-by: moxey.eth * Fix * Profiles: Misc. fixes (#3583) * unfurl 0x addresses to ens in discover search * filter out ens that resolve to zero address * toggle containsEmoji for profile record tags * use checksum address * fix merge conflict * Profiles: Improve fetch of ens first tx timestamp (#3605) * prefetch ens first tx timestamp * enable prefetch only if profile sheet is not for intro marquee (means already prefetched) * prefetch ens in search * use fetchENSAddress inside useENSFirstTransactionTimestamp Co-authored-by: moxey.eth * add poaps to profiles (#3666) * Fix ts * Fix merge Co-authored-by: moxey.eth * dynamic sizing of confirm update sheet (#3890) * Flag on * Profiles: remove videos from nft selection (#3894) * remove videos * lint * extract condition * undo unrelated change * Revert "Flag on" This reverts commit 2863c9d06ba0fb5ceadccfde063a2a7ad1b77002. * Profiles: My ENS Names sheet fixes (#3905) * fix placeholder avatar color * fix my ens names sheet height * Fix sheet dismissal issues on Android (#3895) * Reorder search groups to highlight profiles (#3907) * Reorder search groups to highlight profiles * Refactor key mapping * Fix issue where zoomable overlays weren't aware of the scroll view (#3900) * resize + sign cover photo (#3910) * fix nft previews (#3911) Co-authored-by: dev * Profiles: Fix for Android Discover shadows & Max button in send input (#3913) * Fix ‘Max’ button not appearing in input on Android * Fix Discover card shadows clipping on Android * Extra card tweaks for Android * Fix send sheet layout again (#3916) * Fix undefined ENS NFT (#3914) * Fix ENS owner caching issue (#3915) * fix android crash when switching wallets (#3924) * Profiles: Text records validation fixes (#3904) * fix copy text * reset text records error state when switching ens profile * text + validation fixes * turn off search -> renable interactions (#3925) * Fix keyboard not dismissing when registering ENS (#3926) * Profiles: Fix ENS expanded state edit button (#3896) * Fix ENS expanded state edit button * e2e * fix e2e * bump undici (#3920) Co-authored-by: dev * profiles: speedup registration improvements (#3898) * add 5 secs padding and registration costs * disable speed up while 5 secs padding * handle speed up from tx list * bump test wait * fix for now * add delay * handle commit cancel * Fix URL wrapping (#3933) * Better menu for profiles android (#3934) * Better menu for choose NFT (#3936) * Add the most important wallet (#3940) * Add the most important wallet * Update ens-intro-marquee-names.json Co-authored-by: Skylar Barrera * Profiles: Make cover NFTs open NFT expanded state (#3929) * Make cover NFTs open NFT expanded state * use profiles nfts vs accounts * oop * handle both scenarios Co-authored-by: Skylar Barrera * 'More' button menu for android (#3935) * More button menu for android * lol * lol Co-authored-by: Skylar Barrera * Profiles: Profile avatar options (#3897) * fix profile avatar options * bug fix * e2e * move destructive button to bottom * fix e2e * android fix * shuffle emoji text * cover to header * profiles: header + avatar tweaks (#3941) * profiles: profile avatar / names updates after ens transactions (#3919) * update use memo if image changed * images and ens name * use selectors on useAccountProfile * rm network from getAccountProfileInfo * accountProfileSelector * add ens flag to start fetching avatars * fetch ens names before avatars * testsss * fix redundant setting of primary name * src/redux/wallets.ts * push the e2e * override account label only on ens name change Co-authored-by: moxey.eth * profiles: fix da colors (#3946) * Fix marquee list with avatars (#3938) * Fix marquee list with avatars * Fix marquee list with avatars * add height prop to MarqueeList * Update MarqueeList.js * Update MarqueeList.js Co-authored-by: Ben Goldberg * turn flag off * Profiles: make hidden nfts work with profiles (#3953) * make hidden nfts work with profiles * fix e2e * Profiles: Fix Checkpoint E2E when profiles flag is default on (#3930) * fix e2e * Fix undefined ENS NFT (#3914) * Fix ENS owner caching issue (#3915) * fix android crash when switching wallets (#3924) * Profiles: Text records validation fixes (#3904) * fix copy text * reset text records error state when switching ens profile * text + validation fixes * turn off search -> renable interactions (#3925) * Fix keyboard not dismissing when registering ENS (#3926) * turn flag on * fix * Fix e2e once and for all * fix deeplink e2e Co-authored-by: Esteban Miño Co-authored-by: Ben Goldberg Co-authored-by: Skylar Barrera Co-authored-by: pugson * Profiles: Fix ENS NFT video filter (#3952) * revert * Fix ENS NFT filter * Fix & refactor open ENS NFT issues (#3951) * Profiles: Fixes for the navigation (#3932) * Fixes for the navigation * Fix select NFT modal on Android (#3939) * Fxi scrolling experience in NFT select * yarn lock * fix iOS scrolling bug Co-authored-by: Jakub Adamczyk * Revert "Profiles: Fixes for the navigation (#3932)" This reverts commit 9daf039695c2d6d39521fa56c12bc2fe908c4129. * extra bottom padding for ens intro sheet android only (#3959) * ignore savings e2e for now Co-authored-by: Esteban Miño Co-authored-by: moxey.eth Co-authored-by: Tomasz Czajęcki Co-authored-by: Jakub Adamczyk Co-authored-by: Jakub Adamczyk Co-authored-by: Wojtek Witkowski Co-authored-by: Taras Perun <48593211+perunt@users.noreply.github.com> Co-authored-by: Bruno Barbieri Co-authored-by: jinchung Co-authored-by: Terry Sahaidak Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michał Osadnik Co-authored-by: Skylar Barrera Co-authored-by: Christian Baroni Co-authored-by: Christian Baroni <7061887+christianbaroni@users.noreply.github.com> Co-authored-by: dev --- e2e/deeplinks.spec.js | 18 +- e2e/discoverSheetFlow.spec.js | 96 +-- e2e/hardhatTransactionFlow.spec.js | 4 +- e2e/hiddenTokensFlow.spec.js | 2 +- e2e/registerENSFlow.spec.js | 148 ++++- e2e/walletAvatarOptions.spec.js | 133 +++++ package.json | 1 + patches/@ethersproject+providers+5.6.8.patch | 28 + src/apollo/client.ts | 5 + src/apollo/queries.ts | 3 +- src/assets/discover-profiles-card.png | Bin 0 -> 58224 bytes src/assets/discover-profiles-card@2x.png | Bin 0 -> 170370 bytes src/assets/discover-profiles-card@3x.png | Bin 0 -> 317249 bytes .../animations/HourglassAnimation.tsx | 87 +-- src/components/asset-list/AssetListHeader.js | 30 +- .../core/ExternalENSProfileScrollView.tsx | 7 +- .../core/useMemoBriefSectionData.ts | 7 +- .../asset-list/RecyclerAssetList2/index.tsx | 10 +- src/components/buttons/MiniButton.js | 8 +- src/components/callout/Callout.tsx | 39 ++ src/components/change-wallet/AddressRow.js | 10 +- ...blesSendRow.js => CollectiblesSendRow.tsx} | 90 +-- src/components/contacts/ContactRow.js | 10 +- src/components/contacts/ImageAvatar.js | 8 +- src/components/discover-sheet/DPICard.tsx | 168 ++++++ src/components/discover-sheet/DiscoverHome.js | 38 +- .../discover-sheet/DiscoverSearch.js | 26 +- .../discover-sheet/DiscoverSearchContainer.js | 2 +- .../discover-sheet/ENSCreateProfileCard.tsx | 172 ++++++ .../discover-sheet/ENSSearchCard.tsx | 249 ++++++++ src/components/discover-sheet/GasCard.tsx | 361 ++++++++++++ src/components/discover-sheet/ListsSection.js | 9 +- .../discover-sheet/PulseIndexSection.js | 1 + .../discover-sheet/RegisterENSSection.tsx | 117 ---- .../discover-sheet/TopMoversSection.js | 4 +- .../discover-sheet/UniswapPoolsSection.js | 3 +- .../ens-profile/ActionButtons/MoreButton.tsx | 108 ++-- .../ens-profile/ProfileSheetHeader.tsx | 155 ++--- .../ens-profile/RecordTags/RecordTags.tsx | 9 +- .../ConfirmContent/EditContent.tsx | 77 +++ .../ConfirmContent/RegisterContent.tsx | 2 +- .../ConfirmContent/RenewContent.tsx | 6 +- .../WaitCommitmentConfirmationContent.tsx | 5 +- .../IntroMarquee/IntroMarquee.tsx | 37 +- .../PendingRegistrations.tsx | 25 +- .../RegistrationAvatar/RegistrationAvatar.tsx | 63 +- .../RegistrationCover/RegistrationCover.tsx | 77 ++- .../RegistrationReviewRows.tsx | 96 +-- .../SearchInput/SearchInput.tsx | 3 +- .../TextRecordsForm/TextRecordsForm.tsx | 31 +- src/components/ens-registration/index.tsx | 1 + .../expanded-state/ContactProfileState.js | 9 +- .../UniqueTokenExpandedState.tsx | 65 +-- .../asset/ChartExpandedState.js | 1 + .../expanded-state/ens/AdvancedSection.tsx | 32 - src/components/expanded-state/ens/InfoRow.tsx | 71 ++- .../expanded-state/ens/ProfileInfoSection.tsx | 36 +- .../unique-token/ENSBriefTokenInfoRow.tsx | 10 +- .../unique-token/ZoomableWrapper.js | 2 +- src/components/fab/SearchFab.js | 37 -- src/components/fab/index.js | 1 - src/components/fields/CheckboxField.tsx | 70 +++ src/components/header/ProfileHeaderButton.js | 3 +- src/components/icons/svg/ENSCircleIcon.tsx | 38 ++ src/components/images/ImagePreviewOverlay.tsx | 12 +- src/components/inputs/InlineField.tsx | 35 +- src/components/list/MarqueeList.js | 10 +- src/components/send/SendButton.js | 6 +- src/components/send/SendHeader.js | 19 +- .../BackupSection/WalletSelectionView.js | 26 +- .../step-indicator/StepIndicator.tsx | 14 +- .../WalletConnectListItem.js | 10 +- src/config/experimental.ts | 2 +- src/design-system/color/palettes.ts | 11 +- src/design-system/layout/shadow.ts | 33 +- src/design-system/typography/typeHierarchy.ts | 56 +- src/ens-avatar/src/index.ts | 2 +- src/ens-avatar/src/specs/erc1155.ts | 25 +- src/ens-avatar/src/specs/erc721.ts | 14 +- src/entities/ens.ts | 3 + src/entities/transactions/transaction.ts | 4 + src/handlers/ens.ts | 361 ++++++++---- src/handlers/localstorage/ens.ts | 75 +-- src/handlers/opensea-api.ts | 141 ++--- src/handlers/web3.ts | 4 +- src/helpers/accountInfo.ts | 1 - src/helpers/assets.ts | 37 +- src/helpers/buildWalletSections.tsx | 5 +- src/helpers/ens.ts | 209 ++++--- src/hooks/index.ts | 28 +- src/hooks/useAccountENSDomains.ts | 80 +-- src/hooks/useAccountProfile.ts | 61 +- .../useAndroidScrollViewGestureHandler.ts | 2 +- src/hooks/useCollectible.ts | 22 +- src/hooks/useENSAddress.ts | 50 ++ src/hooks/useENSAvatar.ts | 57 ++ src/hooks/useENSCover.ts | 49 ++ src/hooks/useENSFirstTransactionTimestamp.ts | 68 +++ src/hooks/useENSLocalTransactions.ts | 42 ++ src/hooks/useENSModifiedRegistration.ts | 66 ++- src/hooks/useENSOwner.ts | 54 ++ src/hooks/useENSPendingRegistrations.tsx | 17 +- src/hooks/useENSProfile.ts | 116 +++- src/hooks/useENSProfileImages.ts | 50 -- src/hooks/useENSProfileRecords.ts | 39 -- src/hooks/useENSRecordDisplayProperties.tsx | 52 +- src/hooks/useENSRecords.ts | 75 +++ src/hooks/useENSRegistrant.ts | 42 ++ src/hooks/useENSRegistrationActionHandler.ts | 131 ++++- src/hooks/useENSRegistrationCosts.ts | 40 +- src/hooks/useENSRegistrationForm.ts | 103 ++-- src/hooks/useENSRegistrationStepHandler.tsx | 141 +++-- src/hooks/useENSResolveName.ts | 17 - src/hooks/useENSResolver.ts | 47 ++ src/hooks/useENSSearch.ts | 104 +++- src/hooks/useENSUniqueToken.ts | 22 + src/hooks/useExternalWalletSectionsData.ts | 19 +- src/hooks/useFetchHiddenTokens.ts | 39 ++ src/hooks/useFetchUniqueTokens.ts | 65 ++- src/hooks/useFirstTransactionTimestamp.ts | 24 - src/hooks/useImportingWallet.ts | 25 +- src/hooks/useLoadAccountLateData.ts | 8 +- src/hooks/useOnAvatarPress.ts | 226 ++++--- src/hooks/useOpenENSNFTHandler.ts | 39 ++ src/hooks/useSelectImageMenu.tsx | 2 +- src/hooks/useTrackENSProfile.ts | 16 +- src/hooks/useWalletSectionsData.ts | 23 +- src/languages/_english.json | 72 ++- src/languages/_french.json | 70 ++- src/navigation/RegisterENSNavigator.tsx | 10 +- src/navigation/ViewPagerAdapter.js | 4 +- src/navigation/config.js | 10 +- src/parsers/newTransaction.ts | 4 + src/rainbow-fetch/index.ts | 1 + src/raps/actions/ens.ts | 149 ++++- src/raps/common.ts | 31 +- src/raps/registerENS.ts | 121 +++- src/redux/data.ts | 8 + src/redux/ensRegistration.ts | 7 +- src/redux/showcaseTokens.ts | 1 + src/redux/wallets.ts | 47 +- src/references/ens-intro-marquee-names.json | 6 +- src/references/ens/ENSPublicResolver.json | 20 - src/screens/AvatarBuilder.js | 2 +- src/screens/ChangeWalletSheet.js | 4 +- src/screens/ENSAssignRecordsSheet.tsx | 41 +- src/screens/ENSConfirmRegisterSheet.tsx | 32 +- src/screens/ENSIntroSheet.tsx | 94 ++- src/screens/ENSSearchSheet.tsx | 78 ++- src/screens/ExplainSheet.js | 29 +- src/screens/ProfileSheet.tsx | 47 +- src/screens/QRScannerScreen.js | 65 +-- src/screens/SelectENSSheet.tsx | 94 +-- src/screens/SelectUniqueTokenSheet.tsx | 51 +- ...tionSheet.js => SendConfirmationSheet.tsx} | 552 +++++++++++++----- src/screens/SendSheet.js | 364 +++++++----- src/screens/SpeedUpAndCancelSheet.js | 32 +- src/screens/TransactionConfirmationScreen.js | 3 +- src/screens/WalletConnectApprovalSheet.js | 8 +- src/screens/WalletScreen.js | 25 +- src/styles/colors.ts | 14 +- src/utils/ens.ts | 4 +- src/utils/index.ts | 1 + src/utils/uniqueTokens.ts | 57 ++ yarn.lock | 18 + 165 files changed, 5871 insertions(+), 2445 deletions(-) create mode 100644 e2e/walletAvatarOptions.spec.js create mode 100644 patches/@ethersproject+providers+5.6.8.patch create mode 100644 src/assets/discover-profiles-card.png create mode 100644 src/assets/discover-profiles-card@2x.png create mode 100644 src/assets/discover-profiles-card@3x.png create mode 100644 src/components/callout/Callout.tsx rename src/components/coin-row/{CollectiblesSendRow.js => CollectiblesSendRow.tsx} (68%) create mode 100644 src/components/discover-sheet/DPICard.tsx create mode 100644 src/components/discover-sheet/ENSCreateProfileCard.tsx create mode 100644 src/components/discover-sheet/ENSSearchCard.tsx create mode 100644 src/components/discover-sheet/GasCard.tsx delete mode 100644 src/components/discover-sheet/RegisterENSSection.tsx create mode 100644 src/components/ens-registration/ConfirmContent/EditContent.tsx delete mode 100644 src/components/expanded-state/ens/AdvancedSection.tsx delete mode 100644 src/components/fab/SearchFab.js create mode 100644 src/components/fields/CheckboxField.tsx create mode 100644 src/components/icons/svg/ENSCircleIcon.tsx create mode 100644 src/entities/ens.ts create mode 100644 src/hooks/useENSAddress.ts create mode 100644 src/hooks/useENSAvatar.ts create mode 100644 src/hooks/useENSCover.ts create mode 100644 src/hooks/useENSFirstTransactionTimestamp.ts create mode 100644 src/hooks/useENSLocalTransactions.ts create mode 100644 src/hooks/useENSOwner.ts delete mode 100644 src/hooks/useENSProfileImages.ts delete mode 100644 src/hooks/useENSProfileRecords.ts create mode 100644 src/hooks/useENSRecords.ts create mode 100644 src/hooks/useENSRegistrant.ts delete mode 100644 src/hooks/useENSResolveName.ts create mode 100644 src/hooks/useENSResolver.ts create mode 100644 src/hooks/useENSUniqueToken.ts create mode 100644 src/hooks/useFetchHiddenTokens.ts delete mode 100644 src/hooks/useFirstTransactionTimestamp.ts create mode 100644 src/hooks/useOpenENSNFTHandler.ts rename src/screens/{SendConfirmationSheet.js => SendConfirmationSheet.tsx} (50%) create mode 100644 src/utils/uniqueTokens.ts diff --git a/e2e/deeplinks.spec.js b/e2e/deeplinks.spec.js index 2cb8411747a..66bc8e9ec74 100644 --- a/e2e/deeplinks.spec.js +++ b/e2e/deeplinks.spec.js @@ -67,21 +67,25 @@ describe('Deeplinks spec', () => { await Helpers.tapAlertWithButton('OK'); }); - it('should show the Showcase Sheet for rainbow.me universal links with ENS names', async () => { + it('should show the Profile Sheet for rainbow.me universal links with ENS names', async () => { await Helpers.openDeeplinkFromBackground( 'https://rainbow.me/rainbowwallet.eth' ); - await Helpers.checkIfVisible('showcase-sheet', 30000); + await Helpers.checkIfVisible('profile-sheet', 30000); await Helpers.checkIfElementByTextIsVisible('rainbowwallet.eth', 30000); - await Helpers.swipe('showcase-sheet', 'down'); + await Helpers.swipe('profile-sheet', 'down'); }); - it('should show the Showcase Sheet for rainbow.me universal links with 0x addresses', async () => { + + it('should show the Profile Sheet for rainbow.me universal links with 0x addresses', async () => { await Helpers.openDeeplinkFromBackground( 'https://rainbow.me/0xE46aBAf75cFbFF815c0b7FfeD6F02B0760eA27f1' ); - await Helpers.checkIfVisible('showcase-sheet', 30000); - await Helpers.checkIfElementByTextIsVisible('0xE46aBAf7...27f1', 30000); - await Helpers.swipe('showcase-sheet', 'down'); + await Helpers.checkIfVisible('profile-sheet', 30000); + await Helpers.checkIfElementByTextIsVisible( + '0xE46aBAf75cFbFF815c0b7FfeD6F02B0760eA27f1', + 30000 + ); + await Helpers.swipe('profile-sheet', 'down'); }); it('should be able to handle ethereum payments urls for ETH (mainnet)', async () => { diff --git a/e2e/discoverSheetFlow.spec.js b/e2e/discoverSheetFlow.spec.js index d1a4a7f9df7..8cd94c07089 100644 --- a/e2e/discoverSheetFlow.spec.js +++ b/e2e/discoverSheetFlow.spec.js @@ -59,9 +59,15 @@ describe('Discover Sheet Flow', () => { await Helpers.checkIfNotVisible('lists-section'); }); - it('Should open Discover Search on pressing search fab', async () => { + it('Should see the gas card', async () => { + await Helpers.checkIfVisible('gas-button'); + await Helpers.tap('gas-button'); + }); + + it('Should open Discover Search on pressing search input', async () => { await Helpers.swipe('discover-header', 'up'); - await Helpers.waitAndTap('search-fab'); + await Helpers.swipe('discover-home', 'down'); + await Helpers.waitAndTap('discover-search-input'); await Helpers.checkIfVisible('done-button'); }); @@ -97,7 +103,7 @@ describe('Discover Sheet Flow', () => { ); }); - it('Should search and open Showcase modal for rainbowwallet.eth', async () => { + it('Should search and open Profile Sheet for rainbowwallet.eth', async () => { await Helpers.waitAndTap('discover-search-clear-input'); await Helpers.typeText( 'discover-search-input', @@ -113,11 +119,15 @@ describe('Discover Sheet Flow', () => { await Helpers.waitAndTap( 'discover-currency-select-list-contact-row-rainbowwallet.eth' ); - await Helpers.checkIfVisible('showcase-sheet'); + await Helpers.checkIfVisible('profile-sheet'); + }); + + it('Should watch wallet from Profile sheet', async () => { + await Helpers.waitAndTap('profile-sheet-watch-button'); }); - it('Should close showcase and return to Search on swiping down', async () => { - await Helpers.swipe('showcase-header-wrapper', 'down'); + it('Should close profile and return to Search on swiping down', async () => { + await Helpers.swipe('profile-sheet', 'down'); await Helpers.waitAndTap('discover-search-clear-input'); await Helpers.checkIfVisible( 'discover-currency-select-list-exchange-coin-row-ETH-token' @@ -128,22 +138,6 @@ describe('Discover Sheet Flow', () => { await Helpers.waitAndTap('done-button'); }); - it('Top Movers should be swipeable and open expanded states', async () => { - try { - await Helpers.checkIfVisible('top-movers-section'); - await Helpers.waitAndTap('top-gainers-coin-row-0'); - await Helpers.swipe('expanded-state-header', 'down'); - await Helpers.swipe('top-gainers', 'left'); - await Helpers.checkIfNotVisible('top-gainers-coin-row-0'); - await Helpers.waitAndTap('top-losers-coin-row-0'); - await Helpers.swipe('expanded-state-header', 'down'); - await Helpers.swipe('top-losers', 'left'); - await Helpers.checkIfNotVisible('top-losers-coin-row-0'); - } catch (_) { - // zerion did not return any top movers :( - } - }); - it('Should open DPI expanded state on DPI press', async () => { await Helpers.waitAndTap('dpi-button'); await Helpers.checkIfVisible('index-expanded-state'); @@ -162,6 +156,7 @@ describe('Discover Sheet Flow', () => { }); it('Should cycle through token lists', async () => { + await Helpers.swipe('discover-sheet', 'up', 'slow', 0.3); await Helpers.checkIfVisible('lists-section-favorites'); await Helpers.checkIfNotVisible('list-coin-row-Unisocks'); await Helpers.waitAndTap('list-watchlist'); @@ -178,7 +173,7 @@ describe('Discover Sheet Flow', () => { }); it('Should cycle through pools lists', async () => { - await Helpers.swipe('dpi-button', 'up'); + await Helpers.swipe('discover-sheet', 'up', 'slow', 0.3); await Helpers.waitAndTap('pools-list-liquidity'); await Helpers.checkIfVisible('pools-section-liquidity'); await Helpers.waitAndTap('pools-list-annualized_fees'); @@ -189,61 +184,6 @@ describe('Discover Sheet Flow', () => { await Helpers.checkIfVisible('pools-section-oneDayVolumeUSD'); }); - it('Should navigate to the Wallet screen after swiping right', async () => { - await Helpers.swipe('discover-home', 'down', 'slow'); - await Helpers.swipe('discover-sheet', 'right', 'slow'); - await Helpers.checkIfVisible('wallet-screen'); - }); - - it('Should navigate to the Profile screen after swiping right again', async () => { - await Helpers.swipe('wallet-screen', 'right', 'slow'); - await Helpers.checkIfVisible('profile-screen'); - }); - - it('Should navigate to Settings Sheet after tapping Settings Button', async () => { - await Helpers.waitAndTap('settings-button'); - await Helpers.checkIfVisible('settings-sheet'); - }); - - it('Should navigate to Developer Settings after tapping Developer Section', async () => { - await Helpers.waitAndTap('developer-section'); - await Helpers.checkIfVisible('developer-settings-sheet'); - }); - - it('Should make ENS Profiles available', async () => { - await Helpers.swipe('developer-settings-sheet', 'up', 'slow'); - await Helpers.tapByText('ENS Profiles'); - await Helpers.tapByText('Done'); - }); - - it('Should go to Discover screen', async () => { - await Helpers.swipe('profile-screen', 'left', 'slow'); - await Helpers.swipe('wallet-screen', 'left', 'slow'); - await Helpers.checkIfVisible('ens-register-name-banner'); - }); - - it('Should search and open Profile for rainbowwallet.eth', async () => { - await Helpers.waitAndTap('search-fab'); - await Helpers.typeText( - 'discover-search-input', - 'rainbowwallet.eth\n', - true - ); - await Helpers.checkIfVisible( - 'discover-currency-select-list-contact-row-rainbowwallet.eth' - ); - await Helpers.checkIfNotVisible( - 'discover-currency-select-list-exchange-coin-row-ETH' - ); - await Helpers.waitAndTap( - 'discover-currency-select-list-contact-row-rainbowwallet.eth' - ); - }); - - it('Should watch wallet from Profile sheet', async () => { - await Helpers.waitAndTap('profile-sheet-watch-button'); - }); - afterAll(async () => { // Reset the app state await device.clearKeychain(); diff --git a/e2e/hardhatTransactionFlow.spec.js b/e2e/hardhatTransactionFlow.spec.js index 8cc35781941..a6732983a18 100644 --- a/e2e/hardhatTransactionFlow.spec.js +++ b/e2e/hardhatTransactionFlow.spec.js @@ -156,7 +156,7 @@ describe('Hardhat Transaction Flow', () => { await Helpers.checkIfVisible('wallet-screen'); }); - it('Should deposit DAI (via Compound)', async () => { + xit('Should deposit DAI (via Compound)', async () => { await Helpers.tap('Savings-list-header'); await Helpers.waitAndTap('savings-list-row-DAI'); await Helpers.waitAndTap('deposit-action-button'); @@ -171,7 +171,7 @@ describe('Hardhat Transaction Flow', () => { await Helpers.swipe('profile-screen', 'left', 'slow'); }); - it('Should withdraw DAI (via Compound)', async () => { + xit('Should withdraw DAI (via Compound)', async () => { await Helpers.waitAndTap('savings-list-row-DAI'); await Helpers.waitAndTap('withdraw-action-button'); await Helpers.typeText('withdraw-modal-input', '1', true); diff --git a/e2e/hiddenTokensFlow.spec.js b/e2e/hiddenTokensFlow.spec.js index edffcbf034f..1182465ac2c 100644 --- a/e2e/hiddenTokensFlow.spec.js +++ b/e2e/hiddenTokensFlow.spec.js @@ -17,7 +17,7 @@ describe('Hidden tokens flow', () => { it('NFT is hideable', async () => { // open ENS and tap on our ENS NFT await Helpers.swipe('wallet-screen', 'up', 'slow'); - await Helpers.tapByText('ENS'); + await Helpers.tap('token-family-header-ENS'); await Helpers.swipe('wallet-screen', 'up', 'slow'); await Helpers.waitAndTap('wrapped-nft-rainbowtestwallet.eth'); await Helpers.waitAndTap('unique-token-expanded-state-context-menu-button'); diff --git a/e2e/registerENSFlow.spec.js b/e2e/registerENSFlow.spec.js index 369bf668e4f..3319755ec96 100644 --- a/e2e/registerENSFlow.spec.js +++ b/e2e/registerENSFlow.spec.js @@ -6,20 +6,31 @@ import { Contract } from '@ethersproject/contracts'; import * as Helpers from './helpers'; import registrarABI from '@rainbow-me/references/ens/ENSETHRegistrarController.json'; import publicResolverABI from '@rainbow-me/references/ens/ENSPublicResolver.json'; +import registryWithFallbackABI from '@rainbow-me/references/ens/ENSRegistryWithFallback.json'; + const ensETHRegistrarControllerAddress = '0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5'; const ensPublicResolverAddress = '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41'; +const ensRegistryAddress = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'; -const RANDOM_NAME = 'somerandomname321'; +const RANDOM_NAME = 'randomname321'; const RANDOM_NAME_ETH = RANDOM_NAME + '.eth'; const RAINBOW_TEST_WALLET_NAME = 'rainbowtestwallet.eth'; const RAINBOW_TEST_WALLET_ADDRESS = '0x3Cb462CDC5F809aeD0558FBEe151eD5dC3D3f608'; +const RAINBOW_WALLET_NAME = 'rainbowwallet.eth'; +const RAINBOW_WALLET_ADDRESS = '0x7a3d05c70581bD345fe117c06e45f9669205384f'; const RECORD_BIO = 'my bio'; const RECORD_NAME = 'random'; const EIP155_FORMATTED_AVATAR_RECORD = 'eip155:1/erc721:0x06012c8cf97bead5deae237070f9587f8e7a266d/1368227'; +const address = (address, start, finish) => + [ + address.substring(0, start), + address.substring(address.length - finish), + ].join('...'); + const nameIsAvailable = async name => { const provider = Helpers.getProvider(); const registrarContract = new Contract( @@ -31,6 +42,17 @@ const nameIsAvailable = async name => { return !!nameIsAvailable; }; +const getNameOwner = async ensName => { + const provider = Helpers.getProvider(); + const registry = new Contract( + ensRegistryAddress, + registryWithFallbackABI, + provider + ); + const owner = await registry.owner(hash(ensName)); + return owner; +}; + const getRecords = async ensName => { const provider = Helpers.getProvider(); const publicResolver = new Contract( @@ -147,11 +169,6 @@ describe('Register ENS Flow', () => { await Helpers.checkIfVisible('developer-settings-sheet'); }); - it('Should make ENS Profiles available', async () => { - await Helpers.swipe('developer-settings-sheet', 'up', 'slow'); - await Helpers.tapByText('ENS Profiles'); - }); - it('Should show Hardhat Toast after pressing Connect To Hardhat', async () => { await Helpers.waitAndTap('hardhat-section'); await Helpers.checkIfVisible('testnet-toast-Hardhat'); @@ -167,8 +184,14 @@ describe('Register ENS Flow', () => { await Helpers.checkIfVisible('discover-header'); }); - it('Should go to ENS flow pressing the ENS banner', async () => { + it('Should go to ENS search screen by pressing the ENS search banner', async () => { await Helpers.waitAndTap('ens-register-name-banner'); + await Helpers.checkIfVisible('ens-search-input'); + await Helpers.swipe('ens-search-sheet', 'down'); + }); + + it('Should go to ENS flow pressing the ENS banner', async () => { + await Helpers.waitAndTap('ens-create-profile-card'); await Helpers.checkIfVisible('ens-intro-sheet'); }); @@ -224,18 +247,17 @@ describe('Register ENS Flow', () => { }); it('Should go to review registration and start it', async () => { + await Helpers.delay(2000); await Helpers.checkIfVisible(`ens-transaction-action-COMMIT`); await Helpers.waitAndTap(`ens-transaction-action-COMMIT`); + await Helpers.delay(1000); await Helpers.checkIfVisible( `ens-confirm-register-label-WAIT_ENS_COMMITMENT` ); - await Helpers.delay(60000); + await Helpers.delay(65000); }); - it('Should see confirm registration screen and set reverse records', async () => { - await Helpers.checkIfVisible(`ens-reverse-record-switch`); - // set RANDOM_NAME as primary name - await Helpers.waitAndTap('ens-reverse-record-switch'); + it('Should see confirm registration screen', async () => { await Helpers.checkIfVisible(`ens-transaction-action-REGISTER`); await Helpers.waitAndTap(`ens-transaction-action-REGISTER`); }); @@ -260,19 +282,41 @@ describe('Register ENS Flow', () => { await validatePrimaryName(RANDOM_NAME_ETH); }); - it('Should navigate to the Wallet screen and refresh', async () => { + it('Should check new wallet name is the new ENS on profile screen and change wallet screen', async () => { await Helpers.swipe('profile-screen', 'left', 'slow'); await Helpers.checkIfVisible('wallet-screen'); - await Helpers.swipe('wallet-screen', 'down', 'slow'); + await Helpers.checkIfVisible( + `wallet-screen-account-name-${RANDOM_NAME_ETH}` + ); + await Helpers.waitAndTap(`wallet-screen-account-name-${RANDOM_NAME_ETH}`); + await Helpers.checkIfVisible( + `change-wallet-address-row-label-${RANDOM_NAME_ETH}` + ); + await Helpers.swipe('change-wallet-sheet-title', 'down', 'slow'); + await Helpers.swipe('wallet-screen', 'right', 'slow'); + await Helpers.tapByText(`${RANDOM_NAME_ETH}`); + await Helpers.checkIfVisible( + `change-wallet-address-row-label-${RANDOM_NAME_ETH}` + ); + await Helpers.swipe('change-wallet-sheet-title', 'down', 'slow'); + await Helpers.swipe('profile-screen', 'left', 'slow'); }); it('Should open ENS rainbowtestwallet.eth', async () => { await Helpers.swipe('wallet-screen', 'up', 'slow'); - await Helpers.tapByText('ENS'); + await Helpers.tapByText('CryptoKitties'); + await Helpers.swipe('wallet-screen', 'up', 'slow'); + await Helpers.waitAndTap('token-family-header-ENS'); await Helpers.swipe('wallet-screen', 'up', 'slow'); await Helpers.waitAndTap('wrapped-nft-rainbowtestwallet.eth'); }); + it('Should be able to navigate to the Edit screen', async () => { + await Helpers.waitAndTap('edit-action-button'); + await Helpers.checkIfVisible('ens-assign-records-sheet'); + await Helpers.swipe('ens-assign-records-sheet', 'down'); + }); + it('Should use rainbowtestwallet.eth as primary name', async () => { await Helpers.swipe('unique-token-expanded-state', 'up', 'slow'); await Helpers.waitAndTap('ens-reverse-record-switch'); @@ -284,9 +328,23 @@ describe('Register ENS Flow', () => { await validatePrimaryName(RAINBOW_TEST_WALLET_NAME); }); - it('Should navigate to the Wallet screen to renew', async () => { + it('Should check wallet name is the new ENS set as primary on profile screen and change wallet screen', async () => { await Helpers.swipe('profile-screen', 'left', 'slow'); await Helpers.checkIfVisible('wallet-screen'); + await Helpers.checkIfVisible( + `wallet-screen-account-name-${RAINBOW_TEST_WALLET_NAME}` + ); + await Helpers.swipe('wallet-screen', 'right', 'slow'); + await Helpers.tapByText(RAINBOW_TEST_WALLET_NAME); + await Helpers.checkIfVisible( + `change-wallet-address-row-label-${RAINBOW_TEST_WALLET_NAME}` + ); + await Helpers.swipe('change-wallet-sheet-title', 'down', 'slow'); + await Helpers.swipe('profile-screen', 'left', 'slow'); + }); + + it('Should navigate to the Wallet screen to renew', async () => { + await Helpers.checkIfVisible('wallet-screen'); }); it('Should open ENS rainbowtestwallet.eth to renew', async () => { @@ -300,6 +358,64 @@ describe('Register ENS Flow', () => { await Helpers.waitAndTap(`ens-transaction-action-RENEW`); }); + it('Should navigate to the Wallet screen to send ENS from renew tx', async () => { + await Helpers.swipe('profile-screen', 'left', 'slow'); + await Helpers.checkIfVisible('wallet-screen'); + }); + + it('Should open ENS rainbowtestwallet.eth to send ENS', async () => { + await Helpers.swipe('wallet-screen', 'up', 'slow'); + await Helpers.tapByText('CryptoKitties'); + await Helpers.swipe('wallet-screen', 'up', 'slow'); + await Helpers.swipe('wallet-screen', 'up', 'slow'); + await Helpers.waitAndTap('wrapped-nft-rainbowtestwallet.eth'); + await Helpers.waitAndTap('send-action-button'); + }); + + it('Should go to review send ENS', async () => { + await Helpers.typeText('send-asset-form-field', 'rainbowwallet.eth\n'); + await Helpers.waitAndTap('gas-speed-custom'); + await Helpers.waitAndTap('speed-pill-urgent'); + await Helpers.waitAndTap('gas-speed-done-button'); + await Helpers.waitAndTap('send-sheet-confirm-action-button'); + }); + + it('Should press all ENS options', async () => { + await Helpers.waitAndTap('clear-records'); + await Helpers.waitAndTap('set-address'); + await Helpers.waitAndTap('transfer-control'); + await Helpers.tapAndLongPress('send-confirmation-button'); + }); + + it('Should confirm the ENS was sent correctly', async () => { + await Helpers.delay(1000); + const { displayName } = await getRecords(RAINBOW_TEST_WALLET_NAME); + const { address, primaryName } = await resolveName( + RAINBOW_TEST_WALLET_NAME + ); + const owner = await getNameOwner(RAINBOW_TEST_WALLET_NAME); + if (address !== RAINBOW_WALLET_ADDRESS) + throw new Error('Resolved address is wrong'); + if (primaryName !== RAINBOW_WALLET_NAME) + throw new Error('Resolved primary name is wrong'); + if (displayName) throw new Error('me.rainbow.displayName name is wrong'); + if (owner !== RAINBOW_WALLET_ADDRESS) + throw new Error('Owner not set correctly'); + }); + + it('Should check address is the new label on profile screen and change wallet screen', async () => { + const TRUNCATED_ADDRESS = address(RAINBOW_TEST_WALLET_ADDRESS, 4, 4); + await Helpers.swipe('profile-screen', 'left', 'slow'); + await Helpers.checkIfVisible('wallet-screen'); + await Helpers.checkIfVisible( + `wallet-screen-account-name-${TRUNCATED_ADDRESS}` + ); + await Helpers.waitAndTap(`wallet-screen-account-name-${TRUNCATED_ADDRESS}`); + await Helpers.checkIfVisible( + `change-wallet-address-row-address-${RAINBOW_TEST_WALLET_ADDRESS}` + ); + }); + afterAll(async () => { // Reset the app state await device.clearKeychain(); diff --git a/e2e/walletAvatarOptions.spec.js b/e2e/walletAvatarOptions.spec.js new file mode 100644 index 00000000000..40c79c031b1 --- /dev/null +++ b/e2e/walletAvatarOptions.spec.js @@ -0,0 +1,133 @@ +/* eslint-disable no-undef */ +/* eslint-disable jest/expect-expect */ +import * as Helpers from './helpers'; + +const WALLET_AVATAR_COORDS = { x: 210, y: 125 }; +const WALLET_ADDRESS_COORDS = { x: 210, y: 185 }; +const DISMISS_AVATAR_BUILDER_COORDS = { x: 20, y: 90 }; +const RAINBOW_TEST_WALLET = 'rainbowtestwallet.eth'; +const RAINBOW_WALLET = 'rainbowwallet.eth'; +const EMPTY_WALLET = '0x6791da9CCd95405e73d6a1117d02Dc81c4E58775'; + +describe('Wallet avatar options', () => { + it('watch wallet without ens', async () => { + await Helpers.waitAndTap('already-have-wallet-button'); + await Helpers.waitAndTap('watch-address-button'); + await Helpers.typeText('import-sheet-input', EMPTY_WALLET, false); + await Helpers.waitAndTap('import-sheet-button'); + await Helpers.waitAndTap('wallet-info-submit-button'); + await Helpers.swipe('wallet-screen', 'right', 'slow'); + await Helpers.checkIfVisible('profile-screen'); + }); + + it('test watched wallet without ens', async () => { + await Helpers.tapAtPoint('profile-screen', WALLET_AVATAR_COORDS); + await Helpers.checkIfExistsByText('Choose from Library'); + await Helpers.checkIfExistsByText('Pick an Emoji'); + await Helpers.tapByText('Pick an Emoji'); + await Helpers.checkIfVisible('avatar-builder'); + await Helpers.tapAtPoint('avatar-builder', DISMISS_AVATAR_BUILDER_COORDS); + }); + + it('import wallet without ens', async () => { + await Helpers.tapAtPoint('profile-screen', WALLET_ADDRESS_COORDS); + await Helpers.tapByText('􀁍 Create a new wallet'); + await Helpers.waitAndTap('wallet-info-submit-button'); + await Helpers.relaunchApp(); + await Helpers.swipe('wallet-screen', 'right', 'slow'); + await Helpers.checkIfVisible('profile-screen'); + }); + + it('test imported wallet without ens', async () => { + await Helpers.tapAtPoint('profile-screen', WALLET_AVATAR_COORDS); + await Helpers.checkIfExistsByText('Choose from Library'); + await Helpers.checkIfExistsByText('Pick an Emoji'); + await Helpers.checkIfExistsByText('Create your Profile'); + await Helpers.tapByText('Pick an Emoji'); + await Helpers.tapAtPoint('avatar-builder', DISMISS_AVATAR_BUILDER_COORDS); + await Helpers.tapAtPoint('profile-screen', WALLET_AVATAR_COORDS); + await Helpers.tapByText('Create your Profile'); + await Helpers.checkIfVisible('ens-intro-sheet'); + await Helpers.swipe('ens-intro-sheet', 'down', 'slow'); + }); + + it('watch wallet with ens but without ens avatar', async () => { + await Helpers.tapAtPoint('profile-screen', WALLET_ADDRESS_COORDS); + await Helpers.tapByText('􀂍 Add an existing wallet'); + await Helpers.typeText('import-sheet-input', RAINBOW_TEST_WALLET, false); + await Helpers.waitAndTap('import-sheet-button'); + await Helpers.waitAndTap('wallet-info-submit-button'); + await Helpers.relaunchApp(); + await Helpers.swipe('wallet-screen', 'right', 'slow'); + await Helpers.checkIfVisible('profile-screen'); + }); + + it('test watched wallet with ens but without ens avatar', async () => { + await Helpers.tapAtPoint('profile-screen', WALLET_AVATAR_COORDS); + await Helpers.checkIfExistsByText('Choose from Library'); + await Helpers.checkIfExistsByText('Pick an Emoji'); + await Helpers.checkIfExistsByText('View Profile'); + await Helpers.tapByText('Pick an Emoji'); + await Helpers.checkIfVisible('avatar-builder'); + await Helpers.tapAtPoint('avatar-builder', DISMISS_AVATAR_BUILDER_COORDS); + await Helpers.tapAtPoint('profile-screen', WALLET_AVATAR_COORDS); + await Helpers.tapByText('View Profile'); + await Helpers.checkIfVisible('profile-sheet'); + await Helpers.swipe('profile-sheet', 'down', 'slow'); + }); + + it('import wallet with ens but without ens avatar', async () => { + await Helpers.tapAtPoint('profile-screen', WALLET_ADDRESS_COORDS); + await Helpers.tapByText('􀂍 Add an existing wallet'); + await Helpers.typeText('import-sheet-input', process.env.TEST_SEEDS, false); + await Helpers.waitAndTap('import-sheet-button'); + await Helpers.waitAndTap('wallet-info-submit-button'); + await Helpers.relaunchApp(); + await Helpers.swipe('wallet-screen', 'right', 'slow'); + await Helpers.checkIfVisible('profile-screen'); + }); + + it('test imported wallet with ens but without ens avatar', async () => { + await Helpers.tapAtPoint('profile-screen', WALLET_AVATAR_COORDS); + await Helpers.checkIfExistsByText('Choose from Library'); + await Helpers.checkIfExistsByText('Pick an Emoji'); + await Helpers.checkIfExistsByText('View Profile'); + await Helpers.checkIfExistsByText('Edit Profile'); + await Helpers.tapByText('Pick an Emoji'); + await Helpers.checkIfVisible('avatar-builder'); + await Helpers.tapAtPoint('avatar-builder', DISMISS_AVATAR_BUILDER_COORDS); + await Helpers.tapAtPoint('profile-screen', WALLET_AVATAR_COORDS); + await Helpers.tapByText('View Profile'); + await Helpers.checkIfVisible('profile-sheet'); + await Helpers.waitAndSwipe('profile-sheet', 'down'); + await Helpers.tapAtPoint('profile-screen', WALLET_AVATAR_COORDS); + await Helpers.tapByText('Edit Profile'); + await Helpers.checkIfExists('ens-edit-records-sheet'); + await Helpers.swipe('ens-edit-records-sheet', 'down', 'slow'); + }); + + it('import wallet with ens avatar', async () => { + await Helpers.tapAtPoint('profile-screen', WALLET_ADDRESS_COORDS); + await Helpers.tapByText('􀂍 Add an existing wallet'); + await Helpers.typeText('import-sheet-input', RAINBOW_WALLET, false); + await Helpers.waitAndTap('import-sheet-button'); + await Helpers.waitAndTap('wallet-info-submit-button'); + await Helpers.relaunchApp(); + await Helpers.swipe('wallet-screen', 'right', 'slow'); + await Helpers.checkIfVisible('profile-screen'); + }); + + it('test watched wallet with ens avatar', async () => { + await Helpers.tapAtPoint('profile-screen', WALLET_AVATAR_COORDS); + await Helpers.checkIfExistsByText('View Profile'); + await Helpers.tapByText('View Profile'); + await Helpers.checkIfVisible('profile-sheet'); + }); + + // missing case: imported wallet with ens avatar + + afterAll(async () => { + // Reset the app state + await device.clearKeychain(); + }); +}); diff --git a/package.json b/package.json index c593d10a1b7..94e9fa6fab5 100644 --- a/package.json +++ b/package.json @@ -162,6 +162,7 @@ "mnemonist": "0.38.1", "multiformats": "9.6.2", "nanoid": "3.2.0", + "p-queue": "7.2.0", "p-wait-for": "4.1.0", "pako": "2.0.4", "parse-domain": "4.1.0", diff --git a/patches/@ethersproject+providers+5.6.8.patch b/patches/@ethersproject+providers+5.6.8.patch new file mode 100644 index 00000000000..394dca7921d --- /dev/null +++ b/patches/@ethersproject+providers+5.6.8.patch @@ -0,0 +1,28 @@ +diff --git a/node_modules/@ethersproject/providers/.DS_Store b/node_modules/@ethersproject/providers/.DS_Store +new file mode 100644 +index 0000000..10f4d43 +Binary files /dev/null and b/node_modules/@ethersproject/providers/.DS_Store differ +diff --git a/node_modules/@ethersproject/providers/lib/base-provider.js b/node_modules/@ethersproject/providers/lib/base-provider.js +index 9bfac00..896e95b 100644 +--- a/node_modules/@ethersproject/providers/lib/base-provider.js ++++ b/node_modules/@ethersproject/providers/lib/base-provider.js +@@ -880,12 +880,13 @@ var BaseProvider = /** @class */ (function (_super) { + case 1: + if (!(i < urls.length)) return [3 /*break*/, 4]; + url = urls[i]; +- href = url.replace("{sender}", sender).replace("{data}", data); +- json = (url.indexOf("{data}") >= 0) ? null : JSON.stringify({ data: data, sender: sender }); +- return [4 /*yield*/, (0, web_1.fetchJson)({ url: href, errorPassThrough: true }, json, function (value, response) { +- value.status = response.statusCode; +- return value; +- })]; ++ const proxy = `https://cciprp.rainbow.me` ++ const proxyUrl = `${proxy}/proxy?url=${url.replace(`{sender}`, sender).replace(`{data}`, data)}` ++ json = (url.indexOf(`{data}`) >= 0) ? null : JSON.stringify({ data: data, sender: sender }); ++ return [4 /*yield*/, (0, web_1.fetchJson)({ url: proxyUrl, errorPassThrough: true }, json, function (value, response) { ++ value.status = response.statusCode; ++ return value; ++ })]; + case 2: + result = _a.sent(); + if (result.data) { diff --git a/src/apollo/client.ts b/src/apollo/client.ts index d4e3e376f17..f5c1525aeb3 100644 --- a/src/apollo/client.ts +++ b/src/apollo/client.ts @@ -44,6 +44,11 @@ export const blockClient = new ApolloClient({ export const ensClient = new ApolloClient({ ...defaultOptions, cache: new InMemoryCache(), + defaultOptions: { + query: { + fetchPolicy: 'no-cache', + }, + }, link: new HttpLink({ uri: 'https://api.thegraph.com/subgraphs/name/ensdomains/ens', }), diff --git a/src/apollo/queries.ts b/src/apollo/queries.ts index f7776c80a7c..bd191895931 100644 --- a/src/apollo/queries.ts +++ b/src/apollo/queries.ts @@ -1,3 +1,4 @@ +import { AddressZero } from '@ethersproject/constants'; import gql from 'graphql-tag'; export const UNISWAP_PAIR_DATA_QUERY_VOLUME = ( @@ -170,7 +171,7 @@ export const ENS_SUGGESTIONS = gql` query lookup($name: String!, $amount: Int!) { domains( first: $amount - where: { name_starts_with: $name, resolvedAddress_not: null } + where: { name_starts_with: $name, resolvedAddress_not: \"${AddressZero}\" } orderBy: labelName orderDirection: asc ) { diff --git a/src/assets/discover-profiles-card.png b/src/assets/discover-profiles-card.png new file mode 100644 index 0000000000000000000000000000000000000000..cbdef71d8d0c03d896b887168ee3fc4d57bb7f14 GIT binary patch literal 58224 zcmV(`K-0g8P)R(2400044X+uL$L}_zy zY+-pIP%{7kD9^2tJt%}>9Ke5%kKQ`2=#Ihkl#dKYJ_Z(pj}lSmGTeKn_ul(buk-ZY zTbLA!QOTf83|2-N7z~nuLPiv z+)PO01p|X1%}C2GsNraI3gCBEAmIHT0dU)8Ddk+YY_GMiHcSuhgzj!$o7VW7Kq_6Z zfiEC!E7t>&0$P_m<$>4$!fxD5fY=4XS>F$VaMJg4AneY@MnOCP(#}~a5FbD!S<$l< z{&N?gZfw4w9BV~J`}udg^s)yqx~Nv%`E1UU!%8`6Igz)!BeJijcL3sMLi*dq83Z7j zugY!x8HP@^)+gmQc?w4Z5I8Qk&OvCbgNtKjxfTG`KvgCKZwC-GA#@Faxk9GD&;MTi z2-H4lUF-m33rd?>f9`2r+K1{#ILm(n7XoGP`cli^0080BNkl_9v`tcG<_CSu%*_1S$IMJrW=zu*!%TL}49APoC$GGh4fX#%`gJPL&duJ< zX|=x{jdVeO{atqd!DqktwMzlu*dwp~;RE>JTP$D<5JE5m1|amWeDBT4@)%dfM$?~s z-!CBm?(p8 zqb>vH*#gn{$6OV-E_I6_Qx0CgQnCI~2F6d{Txev0ib7Wmf8Z-bWpA^XV}&4;&G zjsub;MMEf)pK)<)2NR4jO02hUT@Y?m!F1WfNbBp#ePl@}86t4ny9pJEi1~5pTba~? z7y<^=VHg;q3|z{%?0LrC{4-W+7?t@{qR?fN?Zf)Z{qfGRid3r38fNd1U%0HP1Q;TD zOG+7RypZWnO9TVP@$wMH2u{YI(}i`q(kK(xrW3;%giut1kPhe7tc=#vhUCbS>dBSW z`F7nY&8ymQNE!{1#trzww*iq5CV(Z^VUi>R5MT%~wEfLb!f99o^CDGoYPm>;dplTfLyRdj3n>|HAc-x{^(c>$&peuHJ*alU!Le|EjU^uKvM2!ui&+%YWSb zS+0c2qLL=3`zLn5VFPRsz)1Awl3!m)xk?Zt;4q2^0fe}PBDO!57_@Xa3&sbI-*w0G z?|%EMT)+5&jqtyTf?@pY?)$%d*U!Ite;1q`bHb?#C5IRT#wal6Qm#?TT$kCVnVlRK zBO`dJ5JK2fFvj%>H$1Y%o*FSGC#QNwKexN}sU3%qm8+R`J|VK}IxZiAmTBasa5O5L zV_#W#tc+NxbYU__o0crE*l_(giWct<=3~afn4;=sRdpBv29PjN+m?ugnTr?$mri{B z`}W~ZcvTEpMt5sVED{SEU+EpFh^-L%uKpc{ZCo|P)ktx68y%$sKXh2Vys4;J1PyK_ZFnT_lM!^$OG#Gil5o?$G`mN zYgO&%bJItDnSWxt&6TJGcUCxeRyr-p2_E|3XKqPWlmH@!4-EYHJNE{!aa&{g_dk6d z621sluU!1M-~ZrmzyINV4}Si5&+d~4157g|Arg^z%QZoqTrf8&3NXfn&9oG__}3VT z^8o>GF$!0Yi!bR9LKh8(=_=Q z#~2|%`AO?pi#$i=r-?S6w!@5;g$M&+#h8lkEHuA+v7D61Oo)6NXGupFh~dY$LTC;0;+0CXaAsMk}W!58d&%E;vksT?12=Y_h$$yS#+6aD+LnF%A8F67er z%oxQCWci|acXOHK*ske<<+wwK_uBiPEO94J52;8=41h>}fLQb-CXxqkz!?4~RE5gN zx_giHb+G`)wUotkX4um? z-Oo;xhVYiXnJ2eSY7qq*Ym50|U2WC; zdGikRS_gZ3AA9OIJW~$j#Gt5*L4e)@Eg^)>fU+WhArCMkGtO9&LzS)3vRX+}D0g=Z zPO`L+am}o28O(LKDcN(H0CR37)O9HVo)4XT=fJz;T9<&&St?J)^AH>05#W5&jM6?_DVn-%j| zgnV7`;}M_D73Ap*#mYoV1{5$uGGdAZvP329U~B`XLy2>KwtsBYa9oCfL&mth6k@8n zsy<&I&ow7~vdNH@AG3W1FQR8da5*wLDdQm%4DUawTAg5Jw@dN~Sp( z141rF)Qd|E0YxG*5u>#LkZ(+Z@ijydHxIf`Aiq?E)G&6(|& z*QKZ3C;+9U5u&>GzDJ)Jedsqj&7YC|y&HtE6OVkv7-eEGdLU(`aCS&qtZvDTvp2o* zgBO4B(xwYSI9&W4m$K=6(Xvd_GA!GsAS)`t_%u3&q6G!n-m}ZHETZ$Nc1Do+i^MG&D5L~*4uVnt z2qG9ej>EjihU@VET!gWok1|XMAaG2#cYA8QKRY>)wbSOj3Izp&4DdKdU|4GmG?AAH z_MeH8c@isQ+~5LsFkwi=EKHn`0MxWa6c($Ht@Ej`lrw_?cb(`zK7fF(I$4 z1(kq@y%YIo`_le*N17yATo>0zm!GZB9XZ6N-`$jnrOR$eb~Vf-2FOL=VxS0@*9+no z1c5~4g3=i1^)U#bDO$Ioc2%sl^v!!e_5NL77RL2+Yp-72BJ|3N4qg-3weQ{)E%N$# zHNv=VZq21jE4i_`X%%-6#!ZXr)(M9I=0RgMB#Rsh08&_H96I4>_o`cBzpbn%%BldHq`O@YAn+d-qG*gkDx2=WSh*>^ji(gFieo zG@9mz!=ss8*4VMX_Xoe<%n#pn<&yTsBm_VtrfMuOl7;43*mSG%ml+iWlpyC6i?e~# zkY(i4x$pF9tm|@N2q8|^k|e2$5(?>J3?hHXBJWVbWc-jh*;7bQ7#FqZC3pe>MRT@1;2}op1CC&wdtE}&@nw7-Zpc!|9OtxSV6;oblI&#iZTN9bGj_dmI-@3osc zxLi0NEvPumm9m%)0YpOzH!iR1=MKX2eeQ>#r+U$zYkI7~7hhNU&}U92TtAl%)yG7S zq2OQ=;R2K>7Q8AF@t9DK-*?yftwuv-I3AX{B<9V!bVEbw=;ln!}J$+zV zh%q`jq1a|DEG1j3lg$;kwhZ4j_oYv@?FnN8Gxp<6Bk$Xwm4;R|ZEp!WAM z6;8vlgn3hKE@*j6V}=VaCU6}qORLnYW{Gg_ta8_dC|8CZcKd)l>@qkDIKzJJ2XEzN zgm1lBzvCk1NcY$+@B1A?=)GV1_p{r2gb!lK{rO*6=Rk1TD;z#CaDww93zPfx?$Lz-Xx@2iWk!6Ol%bg`zl5Fp5RR&XYRXULN=~;J<6sWLD3cXAq=tsK8QHPI#@28! z3|3Tx9vm)8ix(IYnPH%k%#d&?t4B7%7M5(J0E)?OiRQ#rOO=gV@?)}BkuB1BGjcy} zrN(!5y?AI`9?K~yYn3j4W?t1N+R8uNl=ysG`DYr-ZqU_m*Hq;2Sh#yCoHgeW`tFgP z3d%%OE1~murWyjtMaU$~!6;mgcqUs6yST#Oo1(KzA%Jmn>gZHAR~GKXHM`N#!OYhO zeo=`kZqTnf*f*)jB&32W*_=|lhSM(}9xlr6t}$-pFL4L1aOWLl+fMRIk2%;qk_&Io zHoTI9Aehh2vr19W=2vTlqGMOa4E_rk!$%vcB3ya@zf$|^-VJWNB7{+PnA!#K;p8U5 zkc~%_y@U4Wp0C0izo8EGH#3n}XT>?Alc^o6lXA>( zKd?4_&79DEudjRO>cl|C88K;3+9{98^XDx^4E!pU2T$eA43pHXLnajH1&LcQoHi1-6vzig z6s~{xvp3f_76}7=;=Ml`_tI57t2BRI%a``NJZT!ke@$N>b-(=| z*T^GAv8WbN=DHMNatfQC&S=6cO9T^qD)BPWh%2Xq+H$sXfxyj|g$jmoV$JI00xOS+ zTU<&NSs|F{5oOtel8|=los9x=(yUEs7U780!4HlWpWLKr3D^i31e=mdQCwz8-6;!M zQ7xbLa`X@8$mLn(Lx%FO4cJSUUWc74Cr2MUJeld9aE?z+9G(pIOoe(a|_8r>!vya?RUE2t!;rUMM{onlao}CB5afIcb z2OfRHJ3h=?@R2sy-#2%@;R9c1`8>QTsx>3naSV`%N`%KelI3c#W0Np!4J8v1e0&^*~9Vc%hyWm>++*IB!*tX?brj^cHgX4uGJrkud zB^;8oy!M$YWG4&x$;{O7_{8%MNV(BsL_}7wEC-%Xewfs+*oHK9b}O6-h14JvAx5wz z9T^+=<%>^z^T~TQzx?bKpft*5;qj8uUD>{=)XsrkzYdMpwXE3OdEf^xRBfzM-m|{s zjwLbMW=k4$g5jox(Oc%kE^H6YtyIrz)>o`r!361x5*rusVSe?BELa2xy?SLf-NlS4 zRwmP%=UGCoXtOJ$jEk+Hmu#6!O2RfmX&Vbk*%&<3Dee`e*1n9)w?`|j7AQ$^r3uvkK5rwmYVMc-##ghG+1S6LTc zKE)!$MO;(r(mcQV$B%aJ-|jNzGHf}#1p*Yz92aWk&3v|y&$^C7sSAu*e0Tw-46y)5 zpA0rb@w#?`7*T-ku&AN?x#U^q_2O{-^2i5Xuea9OhFLHylYhADx|VJ8Kiti-Z0{XZ-3VC*>_&C@SYbsIeT);kMG}o*N=bI9l*nnJ;jZgyu}^3!kuf%BH&@y zg1c``UO^o?)jhV&IoJy$#d1MnNkwqwEx5E)d@59NnmY}A$PFCbqiTB4h?|;lTici3 zx8$u?=!sd?Tu1Tv-S=EmrVW0E<-ov5uIKnr_RzNC$7$iXm{W>2 zkOGO^5p(tIB-@?oAEE`B+Qc;^C$0T???cZG?>jy*+B-IEGq&F-4B;dIisSfrKHpJM z1s?LYIi=v&{PYLc-Sy3X%FF(>GxPM3+@?8^h>8IqXVCdo%GGltWeR?5AY9tGu-I+I zo0uW&Kba6(u?h)p;fH5O>d186peX2w`|Decpj>hxqqF5-bVr30_i_cx1y@(+y>SB7 z?i0^naD4ds`i6ZtaqsYWpXTsot~KY)Z)$G_fWrs+`F4WmH}8|YJv5{)#sz#l6W4w> zuKa$p+dE2AL;Dbxv{+4}8eV6m0N}Ts$6A+NJ9o}{;hd~+Wt9rD$=0Ea zSuQnf+b!lTe(n;4rl}`VFeX%?pIBvp@Va2gJ|_k9KP+9=;YaNElIW19T?ii&^Ql30qoU9~@o3#+lc zDz3h^Ec(-iCfC*mR!;p@IfQ~^tW>i9n~K4Pw|w!L=ljj(*I#n;M{mAjal@>tXJ6_Wms|Gs z<#z4aFA>t)H`t$-wTAV(2TJ2B-+%Khuf62FdLt6Q_tAs@+HamEjWxZ3EE0l(3wEd& zIF{6i@E^;tAMI)QV?wFd={uBE(DX5Jtdh z*Rt$v!Q!vl23@|euCzo?<(=`Yy?YWi>l=Q9um2I;@-O|m7eeO^4UHB1Obti<$QU<@ z?c_e~&J4@&`RBq}(7*eiBZoKVhK8~u-95)1d3NtT!rq{U7KUSwXC@+g2|t`FOd-fc zRE;s#Y3H6PSIa)}j;0mM0HE@M%bq-ztEpJfIW^M>&mGQJmB?IaPb#m!`PRS;83FdQ zIgzG_%|m+Vfs%RO8PNYes&K<2!#d~3dj>;X=k?Y*`*ki{-#(>sJD0SLx2EN9ck3U& zU)}KnY<~{_=zg8c^Uso@BeWN$r9Ns3J}pALk0{{c2DfVJnHRaX%S3Ld+{g@Rc&t3h zcE9vW>zrAUSoF}o?yb-41%UdxCP^Z2GM?M9{ZG60|77p*w}0O^Ij~y|mDQ?J%h(v* zvJC)!ee7`S(re~+oa2+MBZiA#Ogu4^0vlLSNwN}4(rML6mKq%#IC=od3dR!S{)9+s zNQ;$3O3JiwRMJ9Nk`cy$z}s;>E!0!Q!(5bPEZ22`pdt|jz|&ZQN$`FXwCay2kQSWFDMMBfHuzaE5dX=Uq(%Zw;+;8oIh*)VurzNn#5D_;Z zgtd9R<(ePh$ps$~D?-YeYncHkCrdd+v&fMHQ1SD^pZ2Ej860^aGx$`3X18@da%42F zkxk8|-4?~m+rEml9E|Xcq4d&j2I2QuofI;(aZ2gdyvbzPR9%u>rH3Lz4~givsq z>za;h*x=fXP}JI9p{i0k?~G;a9fib;t?zio>e!!Bx+kq3BlL2i?w|Qp!&v~v)KCl) z*&k)-1n*-MyrEE753OAeX9k-Zc%^^uUkCU7+aBKi!P@rEHq5!LqW*2Q&97}}T2@;= zG`a7{$f3@$gXgx+8np5UOSRl(=XSj1wR$1}9-@uSm#=!qo6h-eN%R`8>;{^GO2U}y zAq6Y-D@8<11cz{O(`Rsd{IcsWE+Z^NfI}e=lW5GVs3s+vlo%ILX61G+>a-^-1s2Uv zW0ILuOwG7lAWb8tyCiCJnH6#Bb!T4G;!^MJ@e=P7!i?(@%v?C~p%K(~bgN@#wmv?* z_3@|u42~tL7l;5m^zKe{>fjjs-M@pI=tep>e|Xu7>!OW*s!qU1EKZ|wD&8u%=EWHD zM-mh7gC$Z@RxvR)d?LSNP?(;t?bTvs%*@-l6hefkA)-ncVE}&M%}@Pxct$D02oo&! zg~^KIQ7w~XnTumP2up}MKq&+e4Tr?o@o684NkyPy?7c_#-k+2tthCRQqqlVVx&Y=1%MXWi+YTPk?=y8ACZPRIN8C{ z2eGaUWZdi@GuIL*K^VN#>M*?lXnu11;q5uQDtSpm;@3NS*4LKa2UzJJy#lN%@BZAV zz(zaI96)&yi--pO(v_U^m6o_!qq{K*N?s-z8L|Y)NoD>yo*SOI>pWpx00mYa7`HUu zJm>I_wnG4Bx*!z(lP&&>uloldYp#kvw}0@$OP61N{q@{lQ&Yp~^~jMUTyJY@tE{Z# z%IjYDx2@?5GwQ; zx-B~SEWa<9KewZ4>Bj8XfSJ!gXfZcd&fOSI*64}y6-yT#>*^^jO|0-%f$A5nKfe7b zfG9*c`O11B4)B*V779vb3s~{00xFga7$9-kJ>DR6I+;B(Z0zgL+Bue+$mY^h)V6@y z$T72oeBr_DPp*ToBmp)lK)Pq*KDIEavr?AIncyOjhQ*P=XSD3!JP#tVbC zvbX#S$Dpn~Ms3r3;5eM8_f3p`cVhBWuU}DJU4{ZlwNORl$Hx#xh@se(7w5OW0{;ar zDXl^n1tt`&G`_w8F`u81}7#Xc<2~$cL zgt>8a(yl90z=OK*CK9be2mu!HAufRgjxiA-f`+**cdV^v$BuCO#$*9}Y)aSww$S+o z*m*G!SR8h^_+FIrDfoDOrE%iBR2I%?$2VO2v5VjQtM+^T&KLs5KK%7BJaTBekfj&C zRf|jyH?)) zkoYG2*s&?MyAD56yv=>orPFI={3U*XrQkgl)-^&%a2g3{8qC*D3Yn8#cr64FB+6hi zD}Dy{pT`X0CM$A08Z#|Ra?oT%RuyF`;|jOah}Ca}GX06m1fK#E>s6S6ATsRPMS3d) zv<74 zpI>`AWB#~LEUvt8{iaLnHwb|M67}V3NQ1M3|6)=w?CDU$8b2Ke=3D1guMr<{(ejPu zYik+e@aN!yX?+ce)}vbq(YA07PJ>~ZFTA`fXqkV}?aQutyAaN>+)`0q-qzZD@df8` zsuezE4YP|r^Dn*~&Vo77jL%=JxJXg_6G$8x*NGO{Kjf-1aq?*+Gr?Pgu**lhW1E&a zlt&-@=j51;k)lw9dm(Y}Q2ifA7v9yo$WoJXN$o#ITOQ7|?AI1;@5xQ3a>Hr6+mP=w z7Ft+0vy)~vUC2yW`J9nS+lIm4wZErt=g}!bhDS-D~>$)U2ilP@eB%(WdH9Qeb(GY5`@`$t%I z@@{<4U{<(NyLWH4Iza#cM=BPty%@v;9bx2w302Sz#(qB%-cDY6lc=J8vODy)qY>V6 zom=ia8t20G-wkWr&Sl(2TPHMO?=48rrzK&3J}vV@VdoCx4ifg?4r>4)?BXLZ0BUMj z{FzTYFnsiB$IAZucYpfGYu_Sd|MPcSg^BEV*RexepWbzhOWv?19(uaxOF!uO(hqyS z{6lMea-w(p@Zl%93?6#owa-1(pU?gJ*pb_x+x(F|dq4Au2g{R<;!dlnw^g0ZT#Mp1 zHr@oeR{ZbTu|z6r{hdIJO%8P<#PnzaBkbA+P!}VHfg;yIKr#1XE^iSR=8A2$Tp0h4 za+-CGeBLPJMONzlxYfW2Ig}N*$`MQ=;gEmjMP&e=*Z=}Ja3c~xm83f2gWyVFv1w7 zg&#~SxT@)W8?Jl@a;}i0$&I@H}$fw*4f7>_i_!$2P{y~|W z6p`rjmSGVF3shMI7+1s%TBDf>#W|H^4fE;eUfQ|mfJkO-+kyW_Xb$H!iJl;RPw}f& zjJG~%_#KSV(mLJD<;{G7Qc7giG7RdtfMnNkDOL`rqwjdQ;^E0TeP*vH|HFOsH=P<(&NtKJP9e+P7)6|}XBIPzLP#>sTRR^ihB?p- zgeD^MgTq|J3(+etBxNOV7I0roKn?-V;sz7hm+FRDOXi>3`;1gdx15*v?ASu5katUB z$DKcC!N5TO}`+q5^O?iq>E**C;1Y7oNUUqXfe z`6~fJ7)F3S2S61q)fe2hk!v?4tvf0V-g13SsmXV?a+%g+Hn(%p3ABZ6VUJ66R>&3h zS-Ml9y28#KbPU2Ses?SEOB0qT7eFW!n>%~`73aSZPR6WS_#ZGEP6n&<`5_#N6-%+9 zFsp5_{aOh_r%(wJlOg6)l~{CeL*YoGq*UBa*@`=jg z{6Yak9yMXcH5@`Ex54t3S=lh0J z7p~J5b;uzN2Xffpwxdl*U&g$#!vsk%;Q26btW)RaJJ9}Jc;AW43sZ=J zdz0ic2_7=xF^q3fa0HwMk~7~(ZRt+!m%}nbZ%u?{zXyu3xM9^y>eoip7mjECynAZF zLi=?KH8Le5PO>DauDOPJERP6g1To|VCcwCk{7zwvn;Bvhxe3fRMYn!;+wTUZD^Ba8 zOV^ez%PIvh3kzZ$6>9PaJN^hKLsiv^iVD8`6^q3<;X(ih`zGEwciw{7>y$kTCa6><|JRxU-6O)Ucv1y|1g?GX#0bo!zaiO=C=Np_4J)?<GiX@ zUftTdCj!l_6+JY=4~N#R%5R?ikt>@G9%U0r&g~F1iVz+B+R1oOXD zS6yuP9;`XgWn$j}Q_c!Pt(2G!({{pT+728NVJbkK&;hx?WZ4DJA%vN*s~9+hUG>Ne zdo09(Zn);-|JU*fE<(TbqIdc?25alE{9<`giW9bA4ILF1A4D!xR#~m-Azt&sfMhMq zjaGgNd1C>n3t}IfcLK(QQgN;*$?z7qRXo)sS>YthJYNjp?PzgbN|`7YheKjP&*KFy zg0O^e_@({8GMK->R0HwLf>~!#*|FdF@Hf+cet?co;UH-;|E5kQl&R6(D?6s3d^; z*igM%$f}CP@V|)#WgW%S`CDCiXl4ZRkElAo>BMXVIG7hnpq|JDm|sdR?(=60;%N!9 z@qy!=ySIiTG4LUHc4Bgh|8SP4v_&Zx2Am9TDZa{oam)XIV)IK7fQIRUSQ-GGlFIo6XhZ5JL3l_|8TQj?uT~mw@!GMa> zsbbIJ8Db!a`MP||?T^^Cd{Y1Lht7>WpVpvfK zA+GB%K2{A6f9O7k<9!tBtWnRU=$fl+>$F>ZEdF5K$wwM5?S%650 z)3{z_F9O zfA3B3T#%UYa*KE}BZ53kD+CE{ z6%_-7F{`SzKYwSCXM>3HtX6SnOK_(lSqO`SxE9e{6zfB>^)8f)Whb`wjXv8q{@DJZ z$Mz0xK0Nlwq0vVUj6B;prc&H2hu{57K77qbaH8DHuLQA*J4wviJcS^JeR}0m{58Ip zcHeTxZ96~lB;VP3bkmdHS^R0>#sF5fCl73U_Py_Y@3w8*^7;Ju_&8S#kH}2Zc(0XtihpJ7-mQtUv1z*B_ngu+g^L?w4|ER`DUdid90 zOIB9H$>^BfHaa>!FgR3{iHWI+$w@)AyybfTz=>BUr&4of&(isT3q=mU^0fH3V3s6O zI-w}AU)&E(w8i2lU?IR;kLJAg%~#B=iYtZGL_U|vPmXY=lq3=>Z*^_cbsb=oJ23{9 zWfNIJgs6!sHBo_xVy4Hi5>cX6O00%a%3O!n3gQi|Kw#J3qlFTVh|v!GuE)F>QKCOTJ*l&qqE`1RO%!*$n%>*=sG zRZ=M_O3mU0I1&PYgHJx)ceJy*x#^lKuH`dYrKALT_8>wRuYNf`IXTPwj+mU_ehzU9 z2#Z);5HE^5$bVX)vgB*N^v1U=k#DZa0l+(|jjuN5xNv=Ac|q)TjmeZRhzWb0O4}p0 zu(w6*S-LCi+#x1WVc%G80D!Q+z7a`pe>V=wHc+ZV=FS2zyT*utvs_$L~Tr6aBa*S{xYJ}woC*Js7nyy z4z6XGMqccgwF(7~fhhos=}I^h5+CaW7@$CU;|GPrXyU-0k(}808QFwlmx>bxM(i*C zDBIab0yG_13iDeItv@xqj}eJPLZq!hd-rRT>zC=aZI4dS@BB8$C~zRH1C_l2a7YYf z?_CfV5s1gO1f~smri;Ci4P3|xzGIcu_UFERHlF2ZMD87``+Jl0QdEqVH-yr4GJCUR zy~|90!b*QKH~FE-kxx&JemXt&fzjdj3=V$PH17Y}H*jU6_&7gVz(Qx>NpadDSi}=1 zsDj%)o~^sSqyD;%YPXc;oTI7kqp9P6==sn0j{XJ!s!Ai}VVsamzJ*}t&Yihjj_=&6 zudn9}%eNVDBPxt@hni#*0Djl~&$|a70|aKqh)}&jt**bmqwc!d9ucq;MPcj-V!i}N zphy0qqkT(EcF&(%T3cTSXTst|^H(feQWQ?C$2yO5Vioktby<>%uksBE03gXqZA0^& zZ{4V2x;UQF1jC}@RL75$vEukCt_chJ@(Gs%B4PETcdT95mNYXdtB|z{88bIUIbjZW z*`~=IT*s!&bqiBKDYZ>n$XMxdYGkn_>yc6biL)zpY{xP5c$t;Yxzx3D8P_zdLh80F z`DUlZK2r(W6afhOKWPO_cyJV#2gHEn#o73cS6~Tb4Z3lrbMaX7&1~ljxruv}w)PMQM`Mkbpc8qQBmr3MBYY_@bw z8&i?Tr)Y-au@G@}!83aI0`QiLh#dPl0oKaPlJ9zFS139Cxmo#8M&-iw0|l}&K#p~%1TdnMN|;oX#3H(iP;eTH1b3mpGQ9|0xG})R zEeA7n=9M8)WF1bv!-2#XemIrMOCWICFBAe`%(Sm9%qw&?V1iuAOp|V06)Kn}A6tiy z8xKBfpvVT`IV=tX!Y5c>uvl>FD<*5g&Zb+22@|875FlJ3tg^=b`cK?*FNCw86tOoe z_7Eg-%F&}oxv@NqxI;-a1OUJ7`PV&z zj{;(5ChtftyRn2`5d(q%ivh)3=4Xn`Phv%ZDlnb$y3-ftaV|79)cx{zf2*jhf*Ej2 zEu0B$Elr$Ebxq^+%3H2CHk_HAFSzK^F4oA)IGF z^p=aSyLho})hsfs3boDb^)jZ`!}qtYHbOX!~x*w3omEJ#^-G~FI-mIl(IH$xM171 zSDd`TS=BL2Syf|Im7I$=CaW)UB^49oeGnpo+y#4bDPBnMjJQ~8Ddq&c6@rq<6`y)P zUb>>UAc>#D0>I${c|Id?DY!6d5@F}8I_6+uA9t|nB4Ota1q#A`*dy7#l7=~-d_StI z_ji&aP*pmAsZm|$!2dNQiPbe(ZL>{D{$HG(1DNzW7suPAoOZ1Bto_=?&hE8s+qP}n ze)JiSKHIi!E(S$%)8sU>*T|bEZ<<=a{?9qdEQ}0^1uFqo@M#cDvW~#eTO6^3_4qwz zkR6^_YPnfmw^+1(zCjy`MX*Vcp=vB^48fQWMnF*=)9424NY@#p4ae1OM>9vXi`ZOfD-V1>jX`~w)uiI$xyP)k)1MUJ5yrtr<-Z zF`Y4A3;s9jrCpUNS|SZ$zWqq^*l z3YQX?5qsp}&BUC5RkwKc_{^Fe8_F zY45cRag5~WT}5%rA$xDQ?xMqTY1?nsBMUDK=l}C(-}4BMJ}k`tpQ$iDuGVJ@Fx0Z( z7-1R`8DS7I&eAZ@Bd44iEX?kaqcKp6FW3`hxB>wlS z-)NA>UMkCe!@TX0`{>v(Y0jwaiqYB99n61(g30f}>0>y-l z07zLot2>T%@L_wNvG>W#b!PJOUI)#aDm9k#I$fD*3Rgh49d(!$?W|HVL($F_$ZJ%z zAKZm-b(f=VJ2hR-iA#ahMO{KWy8R_98~IZBzVXU1J-MP;ZVw_d%BYMghyAy7og7I8 zldytHg*cNa2F^xTGnq=S+`Mya?N-}KBF&)K@>Rn}rjP7^Wo%KyRSY{s2Ehi>P2IF~ z%hpVbfz?hVD509FRJz*R$^{K+EsmquF^*nCcA-_N@L)g-NbI;DP_%%2Q{7bllv9ql z^`;BP#s-A~dYzYK9G)o(lyLK-BC%l@8Vy1JK0)&b9yRtH6r^PgN}~2&+#e=D}ov-cT>m^5)c-vaPy;rh|{7`rcovK-iBHAf5(~R z=p4cIkQ)VWHKX|jLmM<4i5_N4wW3l=qO90O7TuL`_(@L1szdp3#4Z~_7 z@b$+gVro~k|7t;(fiJc|lc=omr$ZabnHMt1QhrNVNn^lJBsdmHTG~6{O|EFn;FV!o)1&PJ78Yj|pP=+}wloeTrA1fi_dybZLi3lq`_@$V8k|_>ItdbZysH4A z@f|}5@xL?!J!JXjspGnqhVrhJb4<%L;34HmQd9^c;!Pc#77&MhQF_l_tG!jfdnR&CCxZ%7F}LLANKK=1NCo?8}5IEci%H?T_#{TRW)AntYOOQLKFxJfRu=R zvhW%JPuV1(O9EFxFpF+*cDroHVaGCCwZ$^q)I0OZN+~6CN8V+P@h?s|n^tZ(_3T3~ zx#swd>(;@hFy|KRi&uF0n%EcUfWEq05nsx6Hu z5(3EqzY7EY0pJMW4AuwxT}*0S9l5hlDqehMV%Mq!xb+f;Lf zEhikLUwlRG(1X(5?Q&Qx47cS8hT9@)$L)?|;i;Lu?mU0l@dsGNoT6zA*%aYugBr%? zop({I$7=l8X_}JFky3k*$q~zn-XP(Kq(qfLEz?wz8B!>PJ$-(uBS@zSFjSNZjeBNE z$jT>%#&H`E64GG{n3edA3;AV|5i%#4DwN8(d?A^1bWIn3CX9b+anP*mhK>!(ZIJtIzjU8d`xL~eZ|-D?V`LtJ<^gdnesL~-{iI%89MWG*_?9(SYRm0k-FN$@ z1G_r_Qb4W0$CAk$*34`&%`T6rtB!5tv)OXK+*esTG_YptrbBk0Iqj(Zjx3c5F~RxE zFR9HckcFC10GT*?{s^Ti0u?6vf5@u|EiI!RzPwmBvc(Qhbv(rkDW-~iK&ce1fr5QS zZz|RTbsCBlDmrWCylpA1rpC&H0oBOn8E?_iJ_r^>FMn|H;j!B zb*7RwA|abU`@jFQh*i9 zlyXdVV5|}~L{|`N?0*Pr!}w%gX;DSWnK*`rQb}v+cyfBHG1P}E-EKDfpFjO6!TPIr zb%JgqG#4tBK&fDS?8Yt2-Aub?V2Q=}CvOx+vG_X^qc#RT5J$!>;3aMWC%##7s%1Re zcD}cLWXG!Et;==h_eq({3)*rwNcr`&P6pjzs5_Z1W}MN{Ra=*@+>?&qm$f5B&<*!78I6K-(N&2<#vpCL-$&;4~>09>aWJrpF7^BN=Q0R#*odHmW5 zNEph7DSqWM_f>uPF5ZC$p@fV{uf9{3W%<4l3C_&^^(G_Off~->5#}2|27`I@Jec7O zHJHgRVF*aFsrUU{S8tm#HD9mjvqv4X)P=gd9+V+vhOIJR-S_-(AT3;lC%6zW@6=8H zfd(9(2lY;+IL}8K6;%v25z-X6T%}T&3?$X^RXba$$x1Jtzb-Lo&XWT;fxYZqIzaqE zgKqPf;>`_mDCKC9iVoq34NBL|vmYD?_RaMAz#i z{@mS4z9!)i+xcg;cd$%-LYyH-TP+LE3Ts85Ue#oGF(ra3HgjyAufU$~laJ&@q!eZo zV?Av_#zEit*B|`7Tmnz)I%R2fSFc>HE~9SC5nCt#tI2e|67WF6aZS?+$b7ZOn|yY! zjqb`fqL>1m&1iMG3W2_IPxrt6_21)*sCejz)w8)NX;{S)K@;~DSfPw}uL4dZK~}O< zVD07F;f~mob2%An!XZnJ8JQCew{oS53pAX|9nbPcOvt*{#P{kvPjvwdPsJ=qV2r@W z!^w2T6$3HJI4Fe}fRbywTkW;fEu>ta#AbN8UVBndr%^( zFLNkOvRAWO)yu8d7Yw6H*|(0@ysN}5HwvsO=-`BtlJbuj*T#j}$%G$$rigxbF0mdx zuDl;c517(HzS>sh`M6xbGfQ%HAF)>=L`-<*?K&zPcCw`Dsx+x=qs0QcXveq}{687? za}_8^CQF=hVn3cUPEN(d5h9EA&0Sp$+g!z~SSb^{v{v1I0*;d`zE_~D z%9j!XTglqsI0^<8Xog;Gvx|~lPV=M7Rj4N&?ZXPwJ>_AZ z`e#iefp(CWUXTw^*JU+rhE41=+E#^OBWC;G|N8F=$@4L1;5}3bN!N8C53#4fh$h?B6VdckM?uz-&>t~97}}MuGKK_{fsbrQVa~je zr9@IlvR?=>Ra74Z_SGi$DA~fwShu-~ZJu?%>$TP#-GOGe2cxNOoZ-eCb=Vq%5uDrJ z!+$Vcsz7705#7r0Y0i-7oT2xiGm6Cg_dZ`EJ5Ns@Y+^rb7Jv%N{9Tdc*_<%Db6IJWS=N$dm=oW zJ4Jz+kNo4pNQ}(z2Fm;jjigC~IFTIE^PJC<94|vD^p%O3)I*bOdFddr($(G$3GeUk zuYX*228FOvrtZo`EK$;oG#ZD_&~{77E;Pw1Yr+^uSx(d3npT8OJQ+6nDfkep4sMmC z0*v`lx?WOfSLw@T$7qyfujiTWN*IdODemMYw#qB^wy=u7|Nf^LJn}5%{e3z)PheKb zhQB4jC5{T#Q))#q!&G}kmE$G1X~bInnt!iO$U_L&b3vIYV0UY>su}Zk%~yc7eCugy zan9CKIs;%bp_|2D7h)7ObHt#-3S)D`Sh*DV4)T0Oru%$~`?PgF3U0dwt%4Hzaa@Gi z7-~nd6*UqVJsV9SNHk(bFcFUs>&-vUJ=fKW1GguTL=ColKJ_`YJ9QExX(jV7h1;w> zfjl-lQ&eQE#3;LnSunm#@-@j?a|Nf!vNTH8w^EsA*(gy-W|Th*zb$iReNKcrjRp@y z$sS$WUtQ^KWVXcpJl)NsBNdQ+7E4019wzx@HRXFunN>{2HZjD?$iWbm#Mv2L38=;hPAgM3yb<@SBt zM3I&LPfCzxSYNP_HvK5%AyGd{%9pwFj1)k6CTwC|;xyp6mL@CLh|cG}`|54(dvvz- z84U|pD#+h{`vZbAp$9aEGJrx73Mjj`+!;xm#47`eQPbz&8T(mF$|fDGx^p`y`B;I< zD7Gk2%bb3NtAYWYJ>wV|>_Dta4k$$ooeZm2*BahE{=4N+i{HnqWFPP|SfYXL*52$ar7=Q~#F zYs<(y#?De%j{^!80+uAK?D|x%zPbz}2&@xWtMavu%%D6{F6HQLbKatT6HW?vqp&J5 zI^;O$+t^RxeurR{B!70FR7v=|JjSkfxijxQ)0R&k$6ZzAK86U&?*4QnfF zg|`F6ehoHK?aq@=$c)0Uo>*=Rw&!u+Xp0!JFra*%r>hP&H{k8S65}Mc`|Tq5+pm9s z(TE3!OkyAeD5J_Gs|W~%E}3MVab8ytiV(wFiH-eJV{E1oZ5phOQGUB7xq1ccvcxO2 zE#ObAMos52Y^%(XQ%Aky;|1#P@9*pD3sMX`^b}9n^TewnabzE(d*G`D3Y2E!m>BV}=4#S#=mD43<}fu-F%! zezH&sliHP8K;-m^z61OX4V8h&(RPOjwTB-D?nO^kqlBo zu}9aoaH_&noFJIYH17px=_@~%5Qw~cvs=AIFs3XS?;%oAFk^T(kuK?bNp2)$*gcG;8J7*oa66h|_|E;yGL$r6G>-#JiQ>q1*v3f}~^=(xWv3 zr{$?+pt?P;-S*Q&C8_2axKe1!rL^8ieUEEnB>z%|K)Aq>A$^%&uG}R;lmnp*yergL zB876Yh}Fy4YT-84?zO40}Jdu#6q&DCGxe z`v@EPVX**3A?rKnj2?YjX5l$Z*Mml?JX?>jIHX-cKx35+pgJj$F;4|T9u1O?LznDsay19 zPRVrGaMMY<7Z3Py76Kk6sajkJSJ#3JuSr7c3|6nF^?K6U(;pp83j7ai+s;ud%{vCO zv4)!xQW}A?Tn90c!7M}YTKA2=lav;_;aZ=PGhSco(_qx^Pm~AaL0Zwo^hXn#WmQy6 zKnUli0Ao5xbCDN^V1mwJu@h1#pvNz)#q-AC#K)c*Tbiv0HB3TB)z@+&JF4P1HW{R(5M@pvA)$a=0>FDBB_)Vauo>g-dvLJ?=)c z>nHwt@K66a{-3xP6LPPj7-z+y5>{_%-F(k=_ubOEb-X#rn@DAVjzib%?5wp^V4ZtM zQ5e0J$A>98FOhUY%BKzc{YLqR|l9b!0Q3+0)|4Cy;Yl=-mzol%}rljh?UFc zh8udVJw3qQ@_X8gE3)8Q6abQ>LqSuw>=Nral$!h?&uo!S((;L@av}m-&)0gba&&V& zEqOT-G$y6A2=f|J3LIs92{V5P1lBFxv9e$xI}OHyA-rU{z)Gq@7*>Viq7ynw4#({u%`LZkb*%y;IypWrf+<-^rJ7A73{nrEA}G))i3s5t-&x| z)KqX;))GfuB~50{*geX}x=da}h0#As?LPsp7h_53rns@!_)SQmT5AaVlGqAlQ?#0k zdjzT)L_Nc3HQC{%wXF|)`p3g~74v=dzzw>kW;#2|?|IYxl}0#mOEVpqm3^B^sjqzV z|4%=8{Z-doMWfG9^M2+lPY7Y(=Fm2LLS>Q@2dqaJ-CYsJ-jKsaz37+!`}wc_`}4RT zNfuzSzmsEkAq~pdkj!dii^q0R`_l2oq(-hyvnOUbLFo1S+dFN5?vl*YdUUkudmcc> z9Ch_`Niqxzat=jIL0rhu*?OxTee3uCJhrr^E{8pL-)(#EyPee>)NXDURpMZhK{gB+ zsxW8tf+=gsSUWcx9z7O{GLXAJJ^kyMnO`;<$=Dd(dFLHL5NvI2{q?W^zxUqvUw!qR z>I^nIn%;3oTrNumAZZ}Enx}IIG8j-R3)IkU2zL6-e{JYERhMk0;$@tTuc{J@25ba` zIt@J@X$m|KB{H5V2Fw!MSx(6)9V)M^{fcbV5GvQ>3&1$4?SK4?u3L7KjeCh83K(o&!;P~5fX!+8&v$3q@& z4~n~Mp{4F_7HMN`+}2%2{x>t3`=5}6miPO9zaK8MXLdHp?cJHrocY{y&fUA2UG}uT zulG7=$jXc~t;tBsyUetm?9`pI?e->6Ys_Q&dbPJP4>|~k%nYPLvj6`SlM?l)`e!Ln zV#x?avzBJMM1u3{{A;L0GyG2=VVoE7J4r~nM@{VngI!TiU?o3qP#1) zvA?Z9@ZE1a!;!dpmIUyuYbEtTw#AF*opHt) zR;#u5Wg=dF`HeGXJZdzi46GiAWy7LIqkuz1Ks7yyi*v{^+G+)pz(TPypNkCSy&8`B z;eQm7A~RAC)UmXR^Q?Ae2_8RA%KcQOsU`x+hA%zI03ix;t_=wkP)16V#uzdfT?TE; zq!|sVR+f~IT3px)15cJq8v+yXpOLJnXzr>~Z<0kQ@kNRs_StLtRbOkNKU{bFv>B6A zOA3w92m6os_8<2Cc%BXV!+F$YGwCx)g=9&UAgZclr%v9LAri{Wgk*~dWU@=|{gvPY z@IMDmTNJmRArM-83qVj+CzbE|a!evB2 z1Wj}Cgpf#(G(EUDbHea6n>lLH^ZtO~3kB!RrjxFwM4N|{R!3d4u|Cad8aX5t5Ex6; zc&<@;C?qHxvkf?M2tWMvbX$r=6eVD#7{~E$C}01>&Tl~lkrPOo1T~aUC`?>^ZhxXG z6Gxms`fNnCQhcZ=AP4qO7`Cda^2HaPf8xK@>F9N=w$dEY=2SZ_~k^vFm(DCMXf7|>&%diN?n%dv}b+a!N z`QyAF?qx5}fl$|eRL4a|W8}WQI?S-~c;uUJo~WsL^I!kE;)^fdU$SJ0nql!h{OPA3 z`t7%8U|_^z#NK`S_6`O>DYy2AWOYHnNoq21482SecxK2@ANqe`*g!n&i-w>N^mPGK zxeqG^IOkaM$a>%1vCksChkM(xvc>?Ov_TPPX(W9}JYz`AiLPr)lyU|AJG>2_b{&4P zv+4_P`=MwuCIH|yitxJc7y#=0a1#YS0mtjv# z`d8(GD^Y`!Ve1Pv)T)0sKyUoOf4-9YK@?SG^)T1tBuk|M^hr-wd=oZWWpI7*Kg+NP zbgWL_^##QTll)o2pwJi`~UU%9~hQ3ndi=%a?bg4Z(Vx9 z-KbedV=Bk2kb=n%msIS3Zt*lB1LQJdV(aK%6!$f8YL=Seyrh zq<{U_k9eB*$jmN76>r^Z?pWn@$_$I+9t6}@?Em%qUEiu7hLVwFxIV1>vsJ%VS&vAN zJRN*UHUthAnC{+Px&PNsH-CamG7t>M;&B)N_4Rd6KlNl?ZLM1VQbWUPHgDQ^c|*$>|kBf@@)s)>^rZr+Z;_3Nm0d25Us47jM`o0NuXe ztbAYM=wZI$DCIw{XWLRTy%~ceX}K|~E71B~*TLsIkACkDv?WA2i-^STFc!`5pY5@5 zxEc=KRLDsrRFxEDa^OW7AlEvT>#=rkL3rnB!hv)qN0m+-lFbXE z*cs_s6yEEyU0eo0TNAG)g3yng&k*DZekAA6gQ&VB37>95`L)as8~{I17-e{cRf_gweYGjAMRebHOX zZ+L&r@H?M+e($gEZ2ze*&l-;UuzCWjvlTy+D-hMDDDYIW5!{EwjzdDn0lw`(vSn}L z`0nV@9g!nDB8PUystyqC)h2ys`k=5QEu1*wsd}xm{4-y$jgzSp5 z!~uZ(v1r8DxLxaSqGNut7teA(ZsdO#L{V<4EEmjL@&N=n-5xtn0?GT?z$WVX&#msiNA^?zLHyo{YiLSL` ztX=+lGI6Rx2XI)f+&js{^O=IUzI;7m*uKk454L6H0dv8$%ST$q$Gf=Ri%$0vsbmGg z9J5_E!Cw`=813|QC_Z1CJ9jjzOSmD&Sc6^K{`Z5 zdHpdT^v2qny`R_Yy5yyAW?XxJG8SEMzU{qt$5wVUT=emymn^j|S(3fJdhey5KJIrO z8977TTz7EV)%O^zb^ve=Dm(wl_uo3WW_M#H^oF)Egs4`bc$^Nagi)z0CaWXtdEP*{ z4A5dA+6;hNt3hnhi%mMAkxkZ9p&HRu9&g+qJ-RzybBGR|u&4VR8DX*7TlrdN^)J3y zKmZK&;v2n`0)$>ERT#3{O(=Cs1gygXg3Kl2<`tSB-`7EJARo44=x4~F3$xe}#i${B z^az{DK>xP3P2EZgjDw20DOmm(m6e9{x%6l9VvR zA~*AU97yoL2P)C54Q|G1+PE~DGS4NbYtUe)5dlqeH8gjD3KYZS73E%f-pti|YJS4-Q3(XbL43^ z&Arvmr;EMUi{XL-zW&Cfj=SoNYgw&c2uJcs8}z|RNH&=~rD)mKmKK141KLFq$-c6o z9s~q+kiQ%TfwAduHP&JOx3T+Z{fQ1&uUs-bo>+033&pPX@*I$4A-~awM=3=zE^@a^Uju;Vs@4bt+Z~x{`^jSpcN-aO^ zZ1+lsP$!efoNL+J1%2?{!42ciyUv`Jx#hzb&zPzE`~9VfM7INQ-8Fej&d=Pj-QDc( znl@MS>@#J_q+rd=EuJ(76qr)er(bo?)2ly$J}?~hQ&En<#c*(^dX1%s-A37`U?HG| zE-m5KfJX~19co0E4m$PFp(on((5e@j==gEbTa{=$7_UFX1nO<6z7%WFztMB(jn0np zXn*G>0e=djcXAo-5Z3Z|i8VS~NzAZn9X88 z=+FTw-mKJ|@12$>g8G4!SH!{cV@-%;K?as$jm8NR%k~^=uC8wqMIpiQ?Oh&$;|dBg ztX31Bj2)}2((34VA}&y5oF@`I73XQ4(VU&ZuGnF`FgZ`7|OG z!hpbpHB$zk)*XG$aS@s>G&=tp$;RVxnx+zo1TxUCh{a-Y3FsZPEBm1=%c4#sId1*> zbzgq*`NoYK`z2+L=aD}vcM-eu3XO>}q#^22$4<1n57#%r{~?xdKPYFJD%u=&71c#x z2poxa>=>)c+9DBLE*Gd!Sy_3*4L7{;#v90cegFOU@4fe4BqQ`Iyj*_yTea{yq* zbjQ2P#@u~ZNjxC{0fPsd?!T`D01`=|c+yO!tv4HeKE1SjZ^`lVjr2pPPR zrmCudBr4(oxX~C-lw~pxMTm#WzAX>FTBN47mw3Tzs z$g6L1r2DjfU-hz%qrdo)Q9&J4gwyLW)D^Y~wVu)Yh9IgXQ9}Ij1D&gl1O-?tfDGh5 zr4U+{peaz0o~}joT2mhyRAd}8!j|FGA{$X{N(u`K(C5)l(b;K&&J_2P&>O8{!eZx!k@aFBr^b4j7`- z6HHN2%CTB^Fq|}+G}{r$0_@!QQ^-?~xkXia1y|J)1qrf$vf{w6I9jVO4s@KbrC3Z2 z$I&x7;#s57xMcd}gAKzbXU&+OxA6M;w@fUVK0SNRtei6^=T2{LZx;a~p!&XvnRv{{?%mk38~7QBl#WufB@L1WtzT6yb)3!otE^ZnHKfLx>SW2{D)m2a+)#X-GGlvuN7lT9FJl zCx%^~qBGN~5Ifuh33$MyEuIOJ1Nc?>gt#<;*x5#I?LFBgFcF@PAcL}75y^rIUfM7! z-DINq{fB)sCOeEq8cDI6ceqB6uo~<}ubToDsH{|x%bG~J%U z#{#Y1h7#wPP`J?o$+PAZ?b_d*m1)XI(RH}P^^Kn9s+A>EZUsRq<%$@FBmgG>Dv)mm zuApmI^)46~wW8}#+ws!W)B}DWq@)0V$z+~0X1>a=GJy)M&CMpA)~MASb~ONdDxRze zhiY1Hf8uq2Fe-|e{zXKfh*(V~lg(yJOH0#gwQ^wy*{jXY&atJW6ciL@W@QaTCDCXM z?_h~UH6Pd`hc$G15aq$CVgLHyHh=%gpR|zI4X1`JhpPouO?*`qL0lETN9scl?;rsY zKtTbOkwN9>=QcNY0>H-~fBf2Ouf6!~#RRY3YGmZ##I)Z3o|Z=lJVy*4_U=`NNM^KKak8haahY^zq6-gcbN?OT}K) znhx)3I3xY-zSdM4nJRPNN zJe|Yo$1#=*Y^iq@5L0!8BMMQM27E|W&_kzzXlMB*F;L5Q9n}%7j#Pi_fP4Sz9f8ic zB3R>G7rdt~1tBV>utWjVG0{5^hY)}IKnJ~nRDzn>cPDi&=z|y1=Y3eImE?kK#I0Zd z)P%xgkzG?CT-Kb1jsk`%(qQj03z`J`gJ`f>SS?qg6ja3&NE{O-Un@kn8+(dE!S@?n zmppRxjqloEV4x46*6vQi$)E}DA_yF^hZ{Z*cz-0aANVdda!PW+N9!u@`>bMAX-1TFG&Z;U zBzS$uarSm~b$UE*zu)Wkc{Pk^)>Eaq_VL5AXH6)HXU{nMyi2mjp0j4l0T=+$SX@+q zA20-vU3kE9^{%RVIAyfgSJ&*{`6n&tbDz31+t}e&ZqZmLmn+sNBDx4&ZW_C>PA64T z!Z;jcQ4tf6ree??=-FqVRmaAvfkyJ9k3PEcnw#cd{@~Le?Wu(+rV-bq7EcelTS}aU z!9_)b3kwfc9i2P(g6wQvUYcye9dS0+DowL$0)yBe#~ zQmnZUSajM1q+^_S{@>3!`~K;t-RkvEa=XWrlwOpPwTRKqNpYUTveQQoAGZ75r;dL8 z%Arr5+xOwWs=s}G<>S}Ae!-X@AHNAjea(97Z_U0g=nV#uJ0}pTH4fC`EQW{|G1s!9 zP6Ijyv@GZu&}&3JBkCAYONm+#=%{fNZMnd1zAQ~}azxytA^bYRqbECcL>m=v6nr(B zM2p?wgF5fwx4ZmpGS8}lZX{r$?WO(T8dW%QO9?$fYV5Y&ZY%~CF*_M+QU?%C>rO)++15U&gW#BqX~Y>s4P!_|KZ!u8L`dbx5X__jiUvLBH3XWumBLthK3$ zf#jfcGvTlaN#W#jK`b!7rjv)`DJJAggkibaC%B=m6C@-^A{pZoTrkP%IGe-it`m;D zCyrd2W=dt%k}L*hUi_$nOWI;FQRr^4e=`HU5wTZ=?MLOwD+2Xd!hDCn)O9>s(SYnK zsr?Bi6_$cEXS>gcY{H;JzXw`6-L)*O5e;d)+L^5tjt(M+5Ng>2aK!>CL6q~;vHHIM z!napao)93MJ5eah72o`>t&1AM7%6SA{O&8Wjd}_O#>;mW-t|g7;@P6&LZp`BUjltV zN2@qC8^^h0l^gp>q5yjj*Fk>}d7h?-5ye^VWJ|iktf2^BEJ>1NX<=4=4ws+h7*>#R zDA?r<#xm1vmXs8|-q_UKMsZP%m<)zOI-o>?XlSV)T~an?*4(eY`v$RbnpL}UTRq7M zZ5`p1+;N`zc7czHpruIZQH4~>=&Po`YTIl6+>OO&%dkOXJT6zNPR!29sA=wW2cjYu z8&RBFl9$=s-et90kjLqb@nBB#bIC|^tAWv)C}S8RCn4Eozn704>`XOt(YQ$H)6Sk; zu&>t1=~6EmDohz_ud6;9_B3Uu8UY|AK;7}C>=cn@sD?K8MTMdBCp%dUQYOTst z%LskIcoRLfGYGY=(R*qNN4c^ruv!r`0IJAjDbosg5g5d?Jn%duc|e4lMw^yo`!{zI z709LKNrEFpJ}wH0sKBzk%_h34#3LUPqnD=X&8(6%f)_o)Ik`O>EEM250UV!mV2cKN z!yAxV+R(VSR3%Z?bfuANwOfP_9+I-f_| z&6Qe9YTk+C4F`@i-h0n&Ev;>_#NmEjQ95=U%z~WvE6B3|I9x=Dj@WnjIP?dBOM2b@ z6tjLagL%=d+Szy9$1Dg z!a}-~ch~GXr2;J^Xi2?YuPw?-Ia1rqT2j-^(l|%MDKs_bSFb1NtjZH=cuvPcAY|Djtn z^;__=f#jMx6#%d^s$w1Jb``$P68s9;2PRJ z>M$-rwV0kDg3n9+p*XA{Kzlo1U%&2>OD<7|7Cijelk?9w-O;uE-CIw444l6vXbxi3C@^Mm&-x$EXRPd$9Wn=f7S;!_uV_sKoqeth@J?;lxD-p>s2&*qM)Jgsd1wF?9HU&HTKb zPhYAQ$5d_6o7e<~2AY<(3^0gr8HsjTkFd>V#=;;{n^^a16xYiEp3 z9ID|vJX5Tpj+y%(x^Crrd$K1#^xU7c3HLrX=Jsymz7L62&#gf}m80!G!??zF^xH_Q z5&ED)+%JdgDq*uq4R+;DK*gKM0m9S}Ig$*!zseKQ6(NRon9ZqqgJx^a zmz+GOooQcv{lo6v`vKYPt?&4 zAoo#_gU#8Be2(fMdZ%m!V1mmB#qBT0PxOFTkm-3o98}Ved*lsS1*`!?(`9TQ#{TXKX~814Lf%HzHQs`HEX^c z;Bl9T#~2YoJ=q{N97;unk}v==OesTVFTClKElWS!dig8g=bmwC$>OE4%;Cx0(d`;% zB6}nhj&EVpqiKWdIMdGN;MTg%CtrK#g?B%_`nJ0lU3AIJnKK`GCTb}` zLlF+jcv)Ip>|!9K0l${;Fk&Ypo3&JzMd$Oach_z9DUmt~u!Wbb#@{VYuqC8>d@T(F z0|j8=@YpHiQ%VbsFffD!AkZ4%<1litWz;YWNI=|*;}iPAE4ssCMEwopB^qR#=DND= z{^0R?-+2A)wyrKN5qH|GSs7_vz65P8MlG*&)TBw1a`SRg%P+_qJE}~!5znHdfk3#m zr6ZY)O_?$dO}*6B9rtKY3*IU*0$DdyAGW` za}r8YCMoja-@=!VcV?#9|3v8!1c9J>Y8K-OiV4Ltl6pu{k2KYVK&)B-#SWfP)h zw?+CdQ6ORc-s)AmD%`~Sq zn3Aq6QYa)6`5@RR9YHWa3WPub$%!OMkesyaeo_REwlOJ}X9vG)ViQEqvXBCu1j?vm z;t~49ceJBalaj4httN*p5lFz1T*_Ls1deZMs_CC(aR3%_MN3#e%9<@4mfzg&tdW?M z(Hm&J5qg7(FFuVNtQsWC8_|Si;`pN9QsOwR_j@csy2K zUX_*U%qz&NK34nNHg~Fp8d+k1J|L4WQRpeJDk${;l?5jOIa)9 z^$Zd8`K^O9XhBS%g2e(rR=7V;HXc9jcDrx5`sx&lLhbpSniWaJ&M{l2pTjdQA* z((6svU;kuoZYlJE!O-K(@;oKe1dh-EMZz1XT&zDD-q&=ruB(OB8vGs2sQKE_wk7E9 zL`K->4s^5xI$KdghTM>iJ{IWgHqgf+0pHm3uKVQE#{K)f0C4-QMYrDXd~NqX<-3oc4*H=F!)N1)PjPQv+np)(-HEfUWi^RHkYFD&B4@_we`xu0Pq*Y1CBncMV1H=%FO@my zr`{>4xyX4ELCga#oaiM%z=0!Deoz66y&D_%3q0z7!mOWS&0Wum} zU4I@E9uI~P&o-ZEiHAcg*6scD*By_)^>e`8o%hw{4uS|alPf zi;bgb6y6%3JtvPYKv4l_3=LlObNH-0S8*Cs-T=#Y)Im``Adu9unu4_*NB*&3%+v|P zDVp7Xt!IBJ%j5C*gW6aEH-@9Ym<97o9kC;A@ zAV|obb`F&3;M5Qm!%{7KQdR#)kb6o9ab%HrUVq?PB57$UvaQwWFvDUX((#8P7NZ{V ztlJk#O-(~YELTrP@kKBIUBo#7K5!FIm;B(Z0j+b*)#JaqZaYVDT1*_#ffX(CBY@|5CaA6PW(`Gx7j1{E--%t=G?hn5Xf z7kGH|%89e4j_vI7>9ra(QAiL_R9t-BMD3-E&OBp$35ak~2m;S@JlY5XYRE+2EJF7t z8jHi90%aELU-w(2sd7@jIme;}0Fn=rT%b`qrd~G<06cyQsRZT6^vEWJI6BnE4n>DJ zLIWoQUVVV$q~4$|VhE_m-CY*NzHV%|thngH1q-(C+jswErxj#6p88;gFBDxcb$DNr z`eenY(^Wcm=yT#GfaA1fLJ`*-*AbD%VIHs<9di8nIL!p?Q-%+a=4GE2?F%N z&W7?y`NL)p8gup4SO4cf|LK#&#a{N(H)r1Q6iPWmu@DA>0m;gJ(|>1P|M&TKKR5fv zht9tLh3__HU3>k3H{PoI39^b$`v+T$64cxEt> z5lCc&5*fi{Muw%uE`Rej36o z_!;m~&}Gp3wb4NRFYcf>nn)&-=+8@kPToo{vY~X57nBYQXh@+y2;m5tu(~0=&E$0* zs1**xhKB^Bqipn$3$H<~ z(#s7|Dn6;u^a6P4X^pu?GF zT{s>=p;xW06Gtk4?W$XyYDi=|4Ja95=f0*$C{cL`)t?QqG#QK}%a7J{_X#yVnUwm5 zY-AxQE2tAE^Q6njWBqfWIK%!eK{3(K7_ks1lMH(w7=UK9?8tFTakbIy~Gqz49 zSs#sb*dx_CnPkzSR(1$Flq9rpGT;hVs3*8nQeP3!2H7;IV7F6ftI-(JvZlZO(dR#} zUOi=aApl%=?vy9q`>o&7B-2L~Z#}ku|A|U)WdJ})%#D6){_vu2|8)ZZ=z0flB@#kM zN35|i+S(dzYmGKFMLN6UNlv60Lh4}mhNe(MP==Hs`&_ZYx|;pJRBSukTDNA`q2kz?_O=FJ3>e z{L^DwSDo|smxs++L;xIjxAptal)ESQi$u26O}U>m>N818eWeXiQ>)*=MXgNC4snMN zcgEw+NZc7pIzyZ@$~hyP!_S%5kcIDq)+dI5q=9CErn_~6?(hQ&Xi8h^pofNNh%iKm zA^arxXv$;KhLY8Rmi-};=i)~2B6!mPj@S+33x}c}wz6Uy?dvoW_#kPT2N# zlWZg_>^jfVg?<|%SuRe*0%YF-B#G{!!;rd%HWbhn0%x7{#&_#o277L2Ylp>bbU0FE zhAk;6E-orSmzCVCm}ey*o<&=Hv~1{*(IbZsE-lK<%@oDt`rRFeYSbi4i1LF85K^BA zY19yuJ;?&%q$Eq^W~MTFZB|AqqtnXkAtF!=gEDI;mShDZoJOM)xI`?^7*3;je+fZnwnh^Ez4?X(jQ40O&0l^Rt=xb zH;8b0ua?#ue1hkYgzk$ot=jWTF3vKgC8NP3bxpoNz|KSno@Zl752!o3;p_be55Mrn zvfq|3@7TNAVzlv5?+JghrmYI#6iK#sVV8zx55dQgO((psT#%-&v*b)mZE0&mhS{i{ zGDribhMqMn@XF_SM-HC+i|DLc(X#JT&Q~LZ8lX1iiypoH))li({~Mw}0LXM$#}CazMr~6k^u`xYUH97E7k;?qI|!tJFLiLN zQzLRk7tQMM1OdQoO~c}s+nqq%8j0}o-_OgZr8U?2wzjBLNrzA;QsH&AM4IS(OS@zG z1y_upmu|L~oq1t2XEds)01|RWOg!(&_bkqAkYLU(4rYzM>86`@?%at=+n}-+AmQ@Y ze#jXyAtk5ijN6}D^u)6H_dE{(?KS1|&UD`Q%bPzP*$llQXemM@`i?7p#rBzmL97n! zt#ECwbZ3@!02 zshg4sqK}3s10fcI6yecnf|^9AVVyS~<>XKvLhO~2fFPfH>MFeGZF@Qoy@9f6gQ)VB z-Kv=|l+dxDLgkn5et*tnx$SxJap(=C82o(EvpxZcOn`(jKbPt(^vcv|Z9lTv0*ouo)H`g7*aEFqew zrp1$KLl;jtPw)wUnPg9#Xcjakd183!Fi)1ltp0Mf8clO^i)mG=Q1OK^f%Ieo2{r-{bRy==q(Zt!~uX}j@Jr0;bI3=q90MIA*{`leYf8Fu>OMgcU>pD^Ww>ygOyRYQImH+w2uW#>fJ_ZUznv`|d zKCG%*7DJ+s`OcK3EK}SX$ZYW0YTZ<2XRy4psoG!J%2oPxZ82*wVUHvo3EmzPY>k5T zL$HL!prjj61T9a9Q6fVUdWM+@ln`TxZl0x3V|_+8oU9GCRRpAo57l}X1STtd?E${w zr=;`!2Fdmtbd`JBys3}BEH~u0LT?~vcG5py9(Q;5(e!asdeLZ%%W4bjGM$ve3VmP& zJEm79mP>I(NFxq96IBfR^oP~{U~t!#jWtIPZ`r(|tFr_7upFK8)gIq->pI`v+_`pN z=Vz;Wcot1|A$mp4E=6i1Vtr<1^RK%*U)$J;KK$KIzb7aHKq%-5hy9=c=er2B9T&J) zS1gtLjD)X~O8d!Ztg)`~czw(0p?UcQ*(5^~B$0?j|MSz1qix>7#n}YSWIF8E&6<>B zw+157W35%Gy7r7hR%@Y;*1H2-GB?*cXF_3XYp}x??F__u0INZhY1ikcn^G;>jEsz) z$`tCw0whV_YDj?rfSp;b&yZ%;81-5tHC5NvX*8_1WO9i^GiFehh9;5X|HyrJeE#n1 z^XJch=hf$~y=dX=0ybY4JwM%-rXwAqCGL%*mJBD8(c~$SY^yh>>)1!~Ndxb@b&jL2 zqnVlE^!q%>NVl6>8){BAys9;8Rd1_yh(F=kZ~pxV+5Fw{^#!-xXnucJ+y0gy0Ax-7 zN6|U&PBk>nBsN2D*h|kvhbG(n=#UPgaB4su!1dncj5PF$yJs*6FaQb*N7qNj{qw_> z+mF=$y!p`l``^3oveO4x%~t~m9D_DyA*Qt&E1NsFA2~i^)T~g5YiWscede#?v7S*f z=mpt}=pie-1f;|r{gUik7u@^Sl6xPVdsX%JwFE%^m}yyM z!M9>{&AVS6{LjbxqTcB8jdhjHfo6BuprOrrCLHG+7TxsW&eLa%167qERq`PMSdCWs zeN04so!QPn`}nb=^K-KY4=U1|%n42~9N#iChhZ36RF)cWrrK}5@&bp|Bnm<#67~B1 zK!DMxpEi1e#->4yF))mlIVF;{Fwr6_N4cs;q!UJi4*H~4-Ka0`nArRx@5)+Y(fia{Z4(&^$@*xewo;t%40BKf}(kU!| z^}HaY*nnZk0n3bW;hox%Z+ixmLP}&xY;tv@=BkSl?w5~jszhxlz@4*H_j}bkQt60T=_Nh2BO-@ zm)th$?8^|1!YSa;iZ7e@t-9^jB9Ks76{xNb-ah>Tv&H}tI3ulIb6c&p;CdZ$qbSO1 zF%B)v9zQ5|OkUd94BHGVy_k(H*8N-s0pdm^_m?FY78`2U{&w*grA6JqLag}LMK~%3lo;4^5r-qb4rN)eG z=!2#Ws}8;MlC&5fAZW}G4}dh9AC_4 z5UT5TceyK?5F?Q=irFH`ZnYHg>_;DdaP{9m+0+4pMix%g1pEdQZw==b{_}(V z0N|2$CzgX|+P4gxQN04foe;~$d*lwgdQ&ZA1kail2C+h24 z0E0L+xuC3UxXoq~y!Da#?Q$e$U--ibv~6f<3;2B}l?y0}fc{oRz>DE!rP*KrT=(=_ z`m!5|(&}se-VO>-gx37F+jP7e`k*+t0v$S)_$}NamZ7s8HXwT4yVZ|G8ci}IL!;AC z&=Rp;LG)`s~yJ9BS+Tt+c_C#iy-e2_{vj56cP^YfOS9?|E=NIFZciQDg3u+F5kQP z!xsh*Hm_aRx$NEJmtVQ(g(un`n|;$AGnRk^&dQ23(ZA@0LspfOfu<VC=vAtDYSQcVI5Nb> zz#J#=hgU!J!YixxS1N0rB%7L=KKl3*)V6Nh1^{!?;L}Ff`|lfll{?dfzy;Y*U>NWM z5eb%OGDO8WR4Z0;XG@AQv0`v?o{cBtiR4++M^2tF?6xH{OP$t3wQX?$hG(bzwypf| z;p+MtS8IE0$KkdkUc=X0DqebT_wEfR{&n@Z&mWj|*=a??3R2D*pZomv6LTCUs<3*pCS_3H3LvWH@YyI%yw;J%7#`RM4O#k0l@%}q}^Qs46Z@0)&Jw{`2@ z!%w}u?6$k^{oB0{KKbHXKdsvI?&m+Wc|sNwDlDE>6{w4O;~@SOO_rjlq;0Th%B(%! zQeAi4<@UC=b@W=g1H@oBT2WOS^n3JEPV^H%1w?UX8Vn3XZ$_(~)ec;z zl@kJS*~$IOAOMOo)8y_EvcH833aVC)G8z-85`JoGw`xyOBq-?OI&#ZKg9-~CdE<)L z)*^9MRfHuDF-!NK$gTr`BRXfpwj=ZN(_h`;ns>_s0I=}Bf3Dg1KJ)>8;I?#=ES`cu z=zY5aBI(e;0LZeWzJKvQ4uAd5{vSVrzYg?MtDd|2s@RH1gl|;I=#(oO0`+j?Y3;EJwM%1oRv|Q?Yz`NP2@#z@zF3@ zAJauSQk+j!Q~4-w_0xOS^~F8c)JvQt%euQ3B~@LuNKn z!!X&Y13I%Q895i-_tc~X7r+4M3Mb#+*|xFH3x6FV5Jj1(glsE{q6DlXO4V9*QK>XK zotCD_&FwDsnwzUfTb)G~CZf;KK%@2P2C<*}Ug zwWj(JujEbaKmey&z=NZgK z6tHPPgqvnFXQkSTGaZ+tutSpsc)=HC+BpMa*o0uadM-Yn$nEkOVhnE$Xdpq;|F#Nah9IHT?2;Q0ssUC`~ z@d2L%A{M10K8=`2CYhE_wzh>v^y;>0G0Ea7uRNjz8Mt?%dg(_wj+@koYz8REN;B{Z zg(E{y)?LxFguo?NH6?du&7Jhx*0QS~O(T;RXC_^pEw8=#_Q(-qXV0E}^>sH0F;6@Y z27p;le)Zn+t)s_H>TrkSu^2<~{qiiQ$yJruBvk3Cgz`KJ2+xU2c@BUwN#i;>eM5m75EH3Jr^|e$~Ai-?9l&=88!} zir~M6Swjs=H{@8g|IN?pwaAZ_Cuc}PEi0;OKNH=G0YwQam6?!httQ*MQ0^)8hQXS$ z=-H1pJI*-sv2Q>AZD)(iAEP{g;7LQ9jARhe$*@@qSe+geL^|CAxf1CaJPttuMUPd* zO^WjR2=QzK3=D_SddJKQcYpiNXSXjpzGqW19)p1q4+jta`uV%p%x>PX;^Ip#nRD(u zPYUrx{noQTd|>W>?pj{4wHL#t>m)_E&uZMVDSzeoMGgm*K+_b(z=>)hI?kue0%n3n=ZL%$)KXEZn$cTchezZFXCmACI3s3?ZRcTG^X&- z%RHm+_c~6SeaYXaoOkbxi~ccX$phmT-9LEdwZ)@mfk^y$WdMpK6~BQ>qtxrZH%a$(jceX(vOjvm7 z`R}f)n{ww%O<5a*$(^-_p|ubIMn>C<&KOs*^H)%?@{NCaI-Az6eb?h@`@<8BMk87* zJa+n}hUZabTjB{4Vp)cMo!LMv{Oaa!XTEXkmvvjZ+1%5C!nQuVY2KRGA3bqkTTA)g z&blg8sld}*-*)t1<@%q0e0=GLOXoLl`eDJ`IXB*TV@Y>td-04JGcLaPVzjXNi0R{> z-1zC`pFM$>NQmYe6}eODUM~c~t=XWrn~kH>Os8pR zh!U{`w!xlg&U6RZ*uL;7y2sYI3fAiUJdn-t2y>}6AbN=<}`O9~l{qMIX4|c|uJ=po! zBJZ<{A}-+K&~c(}-IwoK*t!X2-pOj&SMA0^snjKW?ve61Vt$p+MWl*yY&r~#a~3T= zZPx5_r=$%*Yy5ujGiqPF^7FDUx8A+jdEVm1ciehYb!`PmU^UvumKZyKUw_*pKVSAh zcPmQ^49}%O!nKQsT)TKMDBw(^vD@EUtLphGLE*OrbH>2QA+2tkh;z{h&Bxh9SD7uq zx12b8=DAuf%m=#W7CSB;KW%*OMHYPqNC-y)EuHm)i_f*1hkC==(!>;pd{~eWAxx~j zxM%E0{3N9?4oN8eBPW+kLC)-{KgdPeBiR`_DR#Rj!nK-GxS^%CRC`l0G5Wm4zwKGY z7#Mt={ud-GHZ*s-M0F@1k+2oDQQ5eXJW0_6K?tYNUxuM%NnUkA%V)Sku_~Wp(vU*< z>)6rYSGQ(g{I9sNx}kZQ@~MIvj!7X^@n-mKtv<@I{E#6))6m}D@ZeLI><-t?fANhg zo_}}C7w^FU$R0Lo*44M2_mAgx)*b@C9YkVqtec+LJQBOW#p9>nyK?)W8y0=H<++_Z zzrXN8ozqG694d~hgciUXxekEXLu_b=6lgM@Wq{sTcWm!D@7}-8RdLq?_fDTaT~a)Z zs5qORCL8yx|NZ&9e|+MmuOGPVqnj6ee&+>0JaXNZ_nzrIb^r~_Q%^lLbm-8&>otuW zITB?q#H`F^pFQD^g^?S}Ybmj>ii|y392KWJN_IC=$+$umg&kWR%j$LA(eiU`Is>9j zj&}15CuozRmJ``7Fvi3XKsUFdhP_(G-hDojz@bKvNt)y#nhdl@l^R6yIi`BRRpoLh zflV6qMDD(4V~zu|`$)Z~4FGmZt7Gs_Z@hK3VG+Z3%uJ zQK{QRk8U6;YCAz7#o$z&Spt9}L9c;a2aL-jhvwO@KKI3?H{BVF@bBDz`w+(%J2_sf zHSRpHZS(Gp8+QH}iG?^WZZaj@-YSxyDVp3@Kes zrrHy2@K-<(1oBX1L*Z4x^omtFJfDL+Swwm87qRn1K;4@BF*s$MF>a_@p_98Q-#;)d zC|_B(X)=Y}ZfI#a@#-g!F8udf(=WSGiz*Cf=bwG&BOqbj`>(8g^BL%ah|e=9Jr5`j z2r)*|06=S5KA8jwgC=&^n7CCZxh`FuRj(==_+>#Y_GdWuYSIMPy6 z(H4;}N*O|Y*VWY(jYe6Ph5n$?XcjJ9m{yRrbotA{96e@){-?VB2pr-I3fK1J2Geq5 zXbo~{wVGizeId;NM@M)jDQX0f6-6da7*Fs)=^G&>h6Pb#TR{he$P)x=97!fgDoOHO z#FG#N0S9lXR{z6N9f7@BQbY|F-RjpFrNZJ72N~6f91j>QLzwm9)~19M^JH0cgJ$qp zL2HO5dEJpMvDIHDwl8;RNwiB8jMmS)fz|<+h>#5FEX)%@BM6F?5=&5YQezh6*jS_$ zC?lotDe4K96_sZMc~4+6IsGs-&t|6TWc$KPE*&>+{E%T~j7E!iwrEhP+%!5pTDQNg zr5ONtPH1g$A)~&w8d0sonSnocqM>QUZ$F(lUi-p5Q|`KA1Sp7`XNaojJ1l2F_eR8m z3rUcKge%XV2?Im^QHBA#nHZc!rciA~8Lib7^)$t34C094V}AU6bHZZ-fI!H9tUkH- zaOGFOzVYJwPp#Wp74R3OSO>@WE~u z%I0L)GT;=D3?!TDgc14T!Cf2QdHv;=pZn+AZ@%&3i!Z(Q>Z>b$`H^UE6(foNl4Qe? z&{vUCmaIPJ3B)*6D5zcCitRedy$zs`jXv`{^dm3;CJrggNlhWuLX+BIlx%$BgJiHw zA{2^#{{8Fo{{E!FVuL=IbKRY0hjaC^7oj%}FaKtG=@@XdA-k0^qpiE5?ZlxyAKrDP zyQKkCIQO2XKK=eRQS259a&i*bHB_FrMF6F^l+3P};mB?)TxyX;gZFkW|FUw!}2sh%{q{8oNpbvPw-qf@-&SgMl%3?%W%e-bm^h zX|!j zYP#&q9n1bzr98FRF1?FiaLbOP9S^_s;po}t``!NMr|HFTup<)EJ0RkMT|a!iaqG`D z!_u098G*0~BqXaFoX+O)H~Nu%8Qd*-<3JLM7pCf()Gu6rnG~g*;Dlj}NEt!FtU2@k zcGtcCc=(aBVPzlz73F4F&A~&5D=Vv-8jcG*hbFs9OG`vP`Rp@KmscEk_UR{POYnPK z=mSTN96=wBL?WX`k6-rB^Uj=_4GNNm(E^(xD%I=7oD? zffDN}3XD4Bz!DDDQ zOd7W2tXYfB9GmIP_+={{;zz%?HtX#TPRN->l05{YyZ}yqA!Lv^CAph_7Ee51d$QQN zge&gd?Fk2y2kZDOO_(Nlf+SH?axm!UyR%$)Qi!$hb|=Hhze=(qT9sCO|Hi|K2<<&V zMBPLpLhu~ma-3YESxA5N*V^On+T%s4vit6Rm~mYMXHqEkbu=>>^Sl!+MlIGKJ5ED zm!lLwM6ZEB09`_xH*a3FXc0}*(-5XlN8u^*Gzgxd4hDmq(S*z8` z^!XxUvDk?dCswUmbdi<;f4g#k%op6Df~A%m1Wc4@cU)Wj{Piv$Z2^m?P&VzJp$ zv^pI~pcy7NFMr&GiIb*GK?2dRp+iv=ZFY9{s8J*BR@0M@J$T`gC3*?t#*ITC7(a0m z`mj!?XIa*6Gnfsmk|%9X8pnW0$QQ8O5#>t2Gjk+*k0NBol|4u}f5GHI1?hiuU`fz0 z;4l*z=0uLEY4MD(-|Km9SyM*F>FJKVPItrVpZ1L(e6C#E2JN@d0&XKHcHb;%S<^G#2b|--W1cKBFRmrB z`*Z~w4j2FoO))f0;#&!R?8cWG0)Bc^-->`pwV9`n7zC$)?+$gLL|f<$ly{5X9I@z{ z3-5huprvX}dfnNRM#z7a#16IUSynbNq6sGP)Y-*1h-2 zvZWW^bH-(0@`Baf%$Qqg^QM1&e&xU4FPl9-y{Po!Ctm&O`G-ISTY7d{noFw{vA2ag zq>fd`S*%!RlzlQG-;&5Tm5m>b!~wJh_zN$*fU0m&6m`ZKXQ1E^06@+x;@@zj8;F3= z34l1aZ1`}bD#*GmQe%6KQC;qPc|I=HuX`M06g zCRBL~WzZfa^wGS*X>-p%SafD`!os1YW#jZ@tbV_d3Fy}n2HjL~ z!33B?N|Z$rq(ZgOZ73=gfi+`N>8#kjBhdDQX`;MTZCZA*9?`1;%(Ggp4nZ$LsUBxX zU#`rsAVEHgK8YGl)4iowW6U}SR1-)&w(86&&Pxrx^HrOh8*a%S6OEf4DOqVLxhA3*0D@s}eN(y5-+rj7O2b<9I>&<3 zE(H|~I`g@w&nHRY+zW@LrdddY9(`Y;QM7}f2_&2BMZu)Zypv0&o>n#sdV`Ph-o4() z!4OUqMPq`7hGdxPE~c?PS&(O2cxG{-ljIpwjB(IXSvMjELK`34>E|Ons4%KvG^t@I zDWYDiZSK18!NA83hT2~zb) zrV36}dpnroFyF}shUR6QK4vJK4E`90b%S1s2{BHjHKqX=)?qQ8H+_u7sKfK3>hnl( zWMx~F8s>r?zmg_|F-dQ~Jc~oP1nGIaaLlB$?|SUCk&_G4vLUS*01oIgML}eOR(p0H zNU-M=nCz+QIIt@hoIR{8hv=a%m42a6iaLj?$CexP;D#%%nK*pR+O=zk4I5TkT3S<6 z^XQ|GB9|7c1mnk#zv!ZictJoxc}O2XHD?j+>XgeBDGZY*Pk!;m7aJQJ@4ox)E3UX= z>eQ)Irc6Oj?)~@Q|NK+W{O$Usy>jE^&Zz_qRTUP+8CPPd)L9-XQz1U(OrS_2R|{-B z$tQS$2M4fL(Ah*2;uujhh@w>_Qbf`rl1`Dx6hyOVN+FP6gY=&?8&3$aM3gosWldJ< z2ZsEHYK8=rxcf! zrKULmU|d>zjxh)dX0x-A6iZ4)9h7Ghkq~jEY5ARQ29tM1=^z34&PsCHL2>gmPY96v zNvVp&H#Ii2wYIQ9m^%S2=Rk&4crSJziI6CGhK^ajb1z8zj6!DEI+q6CSc zVQ4;?yzi4kopcsnBAsFgULbHRp)pvO<=#qg2~c2&wKeJNv#Bl(h~Mw?;73li56dEs zmj|8f1|hx5^ocbRgkQ#csO7*^$cW1drs z;A)q)*g#(t4^Y*0M~{{-uh{!t<@V2yuGvwyx2a)ObN#BOnpI6zYnm$7G#*-ATUmX0 z`@TaPckJJ^bKlnOhngzAxvs292ANUiagx|xrc`A-5in=qmDY?L$*G-W- z%i8iw&m2kz{_`W>>T)=pgGY?AopZ_V_us&}0mh~WmfU>N+|uj*cKfk%cK^QZE7xv* z?~8?VQr4MCu#hB5lUeK;7df3evE3+dX0|o;w8S`B$Ju0EJ#8 z+mT|v|K>#_2d8VvK$)}l{!5Aay3V(M;>pyhLBY}N68&Mwx8zpV?sV8PrS->x@nl@D zGa{1Rd2qc^HxxwCYyyNtGVTkyT;A4T*y9d0*oV5Z2bp?YSG19Nom4u6OdP+W$QrV% zi?sw<+(46H^G44HfP^m*Ee{hs{J($_$xv+=8IwvTFPd;3cA^QB(Xe#!ge)sA8zYc3 z2tv28ZKS!sk9Gfg4KulrAv(%|PfDdP#5}fr_C#%AXOv<+K~4IG%{$c3)LNECB&+;- ztmP_ntdF9G7VN6o~ce+fxwR7U!8Qa*omGudyRK{{c zK4?G^&8|#!OWLCy6WTmalr{;intw>KhswINDwq?Bb!V0Col!3isJ@qV&QG zFKphtnR{tlnwxLF?3ycf?-vjjxjlxw&^**JBqwrKF$yMn5@Z2_VeD)o^1it15z00l znOO9}G zH2i$Tz+m_-6GSpXscJbtIPJE*TbEz6t&^5U?|AX}kwV7gZag{6#Nh=W8w<&alY#rRbG_#z5@W+E2V_k3H zeS1=CdwdWzq1tABaq0AzHcwqQl4v@DdEi^{cwVxC%BmlhO*mGI#dC znp+>P@6mzPpQs(fS?gTnNK`##UHlL%P5D!?GY9@9nkN?uL+PePVQ(Z9B+%%&4OmD> zVVU5Vg&fd`t{eP9)1k-}oASYz-!Zp$Caey6Q^Gy7ZnvAqxOk{+(csYayD!gl_CPrp z5vwXM1^9=6E{N-i*o7V|H|z zUELO6wzGa3~p7eN?T=}2pNB=rLdF0rc ze|cSjTt55n!l%f|1&%nHSqCSWJfrlSj zx_Y_v_T=J)Lk~aoSX-hUH`{=O6K4`W%j4^k-@Kt^b-Qx5q4Co8WAEN+ZI0W48MQY? zU%qf(Er3@iYseUp+1`qs_Ote=m-!3QYl-=!3JOMQr;B}6r?O%GBMUr$}aO- zRxerty=fK7B_=$JC&N`NVI)VOySOlo0mC(>+7ngj&&=#dG$z9_yH$Bp)11{f39-a% z-rjV(%jpCHi(Ue-2`$DRFLsy5M> zNTgylsYJ3V&QonFUYCm1H^v)N$)?8IbgC|$s%@xOcf+?SgbGs$nD_{+Jup#t_jLFp zDy21PTbD#^PR?hqYFKxwl#x&XXCqx1SN{Sr#o9VOxPu|*uzR+(w*TMu|FBzJmVTGn zip@`^z^CAGl9~*jg<)8<5WO8hV=+oVBt4(!kYvoiu)nppEg?c}sa(!+NPA70&gHrL z+FCwus|>4*U#1M$D>w%jVVYu4S{*3O*Y9Yxb%hSDu@p<|44dp4b#0bHGtF9X` zDu#%RNR1FBCz=Z8qeB+%C6Gj3Cf4p$+kzSWeJx$d5Jy|vYmOXELAS4?KD}k}ntN`% z&Fyk&ZJQVn$iN;EZ3R3PBXX}TYDtT%7f(7XB5KTmzi|PtHxdlS!#pGsiA7>zEpdzs zMx!A$#lo?8C?4Y}9E%1cVQ;|aa5>Cccvo0##?Vk0s9^OWOQezn1Qs2p>OZLB_cskU}8@kqU-`OPw^#GJ)fO6`vyDJ*oEEh>QpJOwD% zT?D|LaoCG+uNw(vt`$YqaIRzDi+omx!WtE7TN5fX4FoU@^iu){K#1nX*rnSR-hb1kbxUV9)kl*7HK=ud z^n@gnpg+#_sj*Tn7dUxH=E~6Evc=1nE!o&tH+!nS!)>)uLaVo;x#E~+ga6)f*Fc{jgZ`XrQKeGSo%Q$ZO1OC^6sj zI=WER+ye&=Fl}#gc7YgMVUzMSsT8P`msy1iA@XF7$(*w)9!4y}ay3LzE@GGuBaq^h zb3iWGkiSYVm?YYv={$!UC#gRqRnzC%tGVW;eh)(|R2ct`k(Kn3@B zsNhdDJ)6%>=kwF~X|C`BCNkm3Yocb4gAW+2z>Jx07}|tZGMxP+l23t$vr1wDK&& zVsS_kEAt#fSC19Eq`s5zcrf+k$oSY~ZuHe`u2|ycs@^S!qF79^fZOM^gX@DxG9oE4 zoUoKY0fi!Z-6qj~>g^sBd`=h@JSC&x#R_w#8I16Dnrhen4%!^pf)Y>Tm3Dg=tYS5zU zpi0^a{sABcKHzQ2XW4YNq?p}+FvL=(ma^Giq9zQ1z<^&LAHxhk$`uG}A#274j(!*Z z9GT!eL4Xv16P~#6gPvE`6$$NQ)oFht!^5NJN4W5#W*+qgeTt$ak~JKH0bp>JKRTq< z6S_v3oqs>svhHM|Nk!KzvL12bbKj9=q^ihKcK z`StoT$?pF%y@oP(X5wW*+}G>t{l2C|{9mlxkp07dq3WxDYWm?ylkBt1NzJe_nZB3% zmEI=SZ*Fy0Zli0zUZx+w-<~l2r^GoFGJ%FnO|pxVag@s~x#o|BMAa6N`-eckWe9g$ zztC&z%CNY#soncJbNb}8bL0F$VGHZ z-dqyOEa@=7`@W6PeG_`Zo6uMikwl`A6E209yjBw*2-3XvaClbob(w07$r_hMeQg%= z>wCNp&P_j@_gQc_T_k(XpMUsg{S&&=UMfk|%wlPp(_#;5kq6DB@0g-Q6zRSB*JwNjsjqPgL`eQ)c zvOp*Fc&}#a-2upfQ=ZC!HejGm^G(74)+sYXjmS<~VX4kif45Hm>6}*VnGF859tz2dO zej8AkYQUTgnGU;v*9v0_!xE=(VlRhpcirP=hGjm-%VP2;-EBN5R@^~xjRD)Uq0TwS zfvQ10W*qx8$<&Hr5llf0iW9DXa&NpFZx^H@7=IrEU0Tn1zE$)I9QSB(8y(DO)IWaw zDB|A%6DP3=h=NLKhr!*HKDXydZ^(YBPMUoAoE#E&Z>SfwCFV)9S7M%p{ne7 z?13l#Fy{t8log)MjJ@~HIdKUA90o%`20J&YPSC|B(RCGLo_|X zYTk9pEYD3h4qw)md37Cn*rvb_Q$Y6{L#q90D9He9qExr^vGTY4?1SD7oQE~HE|f5@ zmqNl=m|en0e$Ss0uqU=+YD*HyH_8#;uX z;e!xhNcPydpa%On<`t?uN1bDmZH{9XOt%$7lIF-1$5#3IEHG>x(}x<^RP&@8hw_6X zdzl4EJZzg_16nD6|IkNUjgML|g<(;oLjup^Iiw;|Wl07g_<1brC5o`cYSlesZ~9Ie z6Ul!3LV!UA5Yh7%yKINB4XYJgfoTM=VW|2#{}5lK`!#7Mdh!;DK10*eB#V?mWHoe!WL@7|v0+$j-}jvvE#bIY2brfU)58)}$Q1euzgg4!qT`M9sMI8KOdX$fJQemy)Q2Q&cbuV)(;0 z-)WzSlVmGloTKUApMr_sh-6V7BD%l+`iqPbr%7`8{6F72C)<%7Vk1LnhNWd1Bak(r zlH(3~z5sC55Cle0NuZLr-1mEcM@Q*z0<)J#@gRfAC*ysc^Dw!!3@vo~=wIVm<>GvwgQ^_Nc9CjsF$+V`$OufeulM)C<`ss!lIYqp`B{#t2IX<$S$ig~?XH=wVtrW%mJ9BiR68G3WdeK>Aaxp`H(Y;!cmMtO-&jDq{>@@JS3#`x zav!)nuBZ`)M^i8pgKbzG98E_+l3EZWgtibO+FATSh%K;i+W;u2aaQVlmtFho z?L4=k7oo$=*xrzGz=a0RL$=VnR0u}E2w76R9Hjvq#JM|!@l4q>Qn74sSi`Rk)I5uZ zZHvf)|bND<(+kT-}n8o!_3Yy)MO*5Mnz&bJ0V@mtczLni+Lb|tk2O~l;@7H_I zKq2bre1NnjaVFZIKY#YS1n!uh9z259>%N{jebBRz_+hkMsiL7FAK~fHE8~VAK;(G_ zgfMNc07TExWN_goSAPdq)Z5rBM9yJiljhDtYx8L$@l;F5oX#AA6ME7%mGy}h;Vq?z z*!CIfO9`dhUT8x% z7T)*zRHegGggkw^mcTYH<*sjM28#gEL!hG-!m|NkUHdcq3gG}TCMM9vI_Zlr8)I?V z1mM4Z{=!SjaQVn=r4O6&<;boVMl)APw9_v9szGyMT{X}i@BgoeuvU=ljMt5@|0h2unfa+Lec&+( z6HEAVD@5j=659$*I3n`V>#pBDI@OL&?5gE`SHJJDFoNtlT3ieYkBm)JWmO?uSfQ{uy3$J60l z-Jpu)iP2{bo#Nq!$ThL1MIs~JD~tq1eZ9U+2(TagAG^fG#tWujzf7`Ft$@F9d37N_gt1ostpMhi2pAQQ7XBA7VD!!a*U6A-HFYEOWhe-BTT)!ntzSWaHKyc5&+4WQSW;hmFCDH9kLC86 z8H7>QWCfkt_wK1+%}ofy$JnAa43~>EW7-DQA{rV&Tm0JMDXPR|sW5d*^qW8yDIb45 zP}hGSVKXnp3xgJonX+|xAdXt$l}<`vV?`-F)b*&S2CRyq(GaK$lVe#nv2d0|1`JGD zk##V#Z&K@h%gVVN*x8ZE-{Q))I}aHOjHp+>xJ)3W00v=_hM9D0eVhdYJxM&>*Mz1N zx+z7L_R3-iMAd+q6rQFbnNZ>Vj9_(8s&q!VI03!}Ssv-##v}ZYN}mmr%~;o!FE*JY z=NcxYdt<24pK1QUbdI+MEdhU81jf}&h7pq3K_MHhTc_5xBeA#j1liHypspXD>sw1E+BP8}lP`=c z3rz;&)JUK)1ROrwK9f^?Fv_F#zyYdfdS37H*QHbmT7T(>C;*IyqrKaMt9n+#ZPe)K zcB!chdH^lh*H12pVy#3dKQ)#d^2Z$~obb2OtLHSTTu`sE5JYODc3lRa%Z#bO^Afr7 zi5#{zWmM0vv4Jyd(8|GNK?wGWwz@WEiiPBvgU}nM;)8qC$eQb3x@S}C<(43i4hnLN zBx(;iSXousuT2jVHh=)6>FSF8&$U4@S$DG%v@aAjv$c}RyiXhi@gk|~&GKU_tp(JE zDU;0v)AeC~I6$_p?b}9DK^IzNN8tM}ow6#2pb1y!Y2BKOnc4~E2e;)>oMMAFp&5X| zif{-NH@uSVrgHH?IN&Y?b3Z|6-*>vN0390D{zs}WXiqf9*F9@AW_S=0Q#DXT2(iYV zGT(k_xnK>l>5_J%EMjAAWYstq4;9Xe52)em#4L}-$iDY?Qjo=~Apuq*{;7#=TGz1B zCU6;xyg%wR#oK!*4-(GV#2zu?_F93+ylw8FJ3H0sK{6pEC>HY8mx3Wl;{>cN1(GOk zR5j1wg zT&B!yW*@L(!9+-jeE5O_bv7xYPix4RgPwi_Khqfwdj~n6)A4F)kP2MTUYb<9-*25f z1(o~#ecyd&AkgYF>Kq`;IcgGJP=xvfLAhRda81)gPi^JKYe1HPfS%@kL6r1vd>#i!~7ADjj%7qd$WGG|+VZf8^UfgoZ9ymPrPu-D8b&y_hp zW}4|_JH!Ajm+8(G!W&;}RbMR^yl7yKwTAQ=wl}~GT12Y1-Ea_U*)UATiyXwio10n8 zRI11Bq9Q*@i$z=8nwtB1Cj|aS|G^4*`iCCBUa8t6o+a_oq?$#hS2;*yEWn3Y17s~4 zdekyJ1IRANh%-k?v0!)GA~Tys$Yhz9x!CoQ{{-sFHHb?uc0$H^U}U#X3E$_6O>D9L zi?w%(YC7gQ8R7+K#H(M(WTVOjjPJCscQ{TeGRx@^-d5dcNPWA5OvGfpiWlGNAQ7Ph zyidETNL}2Ie4NhGX=3tv2dSVl`rA2KSD6w}g}LkNDaZyrlB_>8zW#3STGX4zQ7B~k zzyB-#O;UH?VJ>Xell}NgYHD&6RZ-8`%hV>wfP|9~J|;Z$>cFD_!Ap}Jk`Nit7g_)4 z`Q+yQ<_i4-*yEQh5JS_IR^$WvPo+F`_!Jk2%Ko&>KndbEtYpBx(TiKvhr~SfmqR$> zO%{*o5I0$WY1a5R+5XX+!(V57&F?Qh#Y6bZ1bNA}yls2`B1rR{kz^R~Q0-=9&A-WW z2KyQ?zNPLx zuHoSV0)k7Wc|a_FxO5n;0$dvRQSmTF01OdueDR7Fbx@xv&^5B-z_aBYwT0TV}}bhv=zu8p4{EPS!> z1(}C4zF7ET;fsYYf&gI8Q}`kX7QP5#m^&;%;EXSv+T`fN-$?j6JB&~CP_|f|@g-K( z%B2~Qxy}!9#us>4Sev-xc!G@2_-fA#3>PyXAcH2zxrpDtU(a9Sbp9^9zPfpkf$s0` zwif=57D7Qg?bbRW0R-@RY&qa*MThC|pv{#a$Kw$(?SwBF*utkv5Ei~z_#)$rg)bJq zSok6Uknu$jEPN3J3tt2oU*vBL$oP_J8DEGrIm}n5V^+qO{4mCsV{XP*psWnY_>x5W z&5W;9%Vk{&E%JEA7sB&#Sx#_rxbY5mZSVxa5+_*Gb|qA#NY)RXAk#6v)`rY1e39|R z!WTOuu<-R5sLT#BzLbS8?zyw@MGy}I@3GJPGlYe&{S}P-FrLD8rt4A%zMU^^!%}w* z3|DfZ?ghF|1PBPSPIBbNkDegy3qEz1@rtO!z+&%s335p9MV3hOxrw@oW-hgoH2GlW zS}t`I!s6=%a4nuTO)ESpp-?_bZ+;uPNbOUp9|k+CC3-(!35Y}Y1T&RrzKEfftLmdC zE6jl>NJ-0LP`oyoFYmh4_Uo!2s91WD_V<%`-8`@BQZl}{ub=c(ABn)@Ey3k*ILuF2 zic>sd9g>Bw9CO}exrRWX&1FfeO;#YEF5uGzEKL%|(hXN&QxJ!o_|Z%XtiQOkA9(kKCgWLC8ZPmn@mI!&c{YIKwCPe?>J z4_Z6dUN(sz{*uJHG+cD|i&NDqQa|BR)u`d)FK*}%Hu{XH zj?P9P2r|A1R;eakfdm;}IHxO@ni#+OL$@&ndM z3{;G#)E7oR7pg((jytSrf&jtHH+0nS#h*)aI%J1C#@2NKG8-9REPRpiMaCBsq@#HU zD`K_#2LX2%4`9#fxZK68Et&_g?T9~aJ_A_LBvGB$AGh}=b9l|#y(wHy7D))VaFa= zH(`9a3}k#M+T$xo&Gt@;pGl(UGXnJZDufGVdeF0 zJ-%?p*RValtaXns9lgg_->!g0#up1;&%^j)k1xI;ru`d*Sok92i@UO9d;!}9EDWE^x3VcwOp}xmtoB{J=i$P?(rlCXKst8`8km zQPbPb7nxM|OU2v@i(yKxnz$BqwWb@T1=n1(9p~-6QHy%EQ>yA$Pjww}>Z8F9^%Dh? z;*8l&-6Vt>B-_T@PO0R&l1R}60TV}}G+Evqq~&{p?` zOS_W*l8ity0zugDBih((1dFB-d_l})0Lu=9J_F;6bQt1!aA>r-o2C}NGhNVCNwCLw z#dH(ty^d$I(7M2=rUhN(bzh=A>KoArVEX(p(^ZYq+z32Du(Wd2&sVn9M{TJ%2M0zNwuXk&D_tC#$N1?NPep zg#lI21W88B{r&YKhA+MtKwtcjfgyl}o0I?9bx+nE-!MU0_~O2Ps8jv@0tArVYT16K z343MQZnXrVW{n3FdlcVVTFM^dXo1O8M$B!TmbG_~Xb2j{SrRlhNo%JmL-^f)mR5~Z~s5c%?ZaE%dI#sZ+^l*{#i6P zocE9V{Q8GJ+I2m2{qcHa=hr+z#+TIht@|^+96`4q<4YdO_{zt}L#I~I z&Ek{HOV5>%qc!8JY(#&?m%3IxJ`amtkih{dOLO3iuWV?0@D_}U{bQ;+K}TzNKo2** zb1;}+z)n_xLiiuXUw!k1v;++0Z?{%*X86V~@1INA;|m81U-Lhn!B-u!#}^r2+}I}q^daMMPnKZei{QfrEZy)&2ytlN&j%;8 zVC)Wu;`|M~#~04Xs*_ZJ(5V>IAxeQ}RuAF%1OU^AcfET+f|Rvx8GSe-4wv@7W8fT7 zi1FpB)@93yjIZv|^A2Em{~mZo+@xK5vZ8X(!}yG^vG-(ir-n_C+&mz9$;i&>%3F}J z$5#Q|*C*p^fF@}ye39|h34}uJxATwUQf?0BZqYu1zhs~6i<-Y=MdWZe%;+zF$@Fa+6CcVod+23|b!^nOVb+?U+!xW|I zp&Jx|I-Z3zNG0Na$Nj`4P?i2*;fsYY7QV)MPL}PQ`OUp53Yu;^NP)x-9#wn%#T;Z#!aNj|vau&irI0jum@r(rQ(HPtfB1Btglw^s4f+Q?%cQ=y#R3l&&6{e#|9r&d#J{KB*Z*FE7o?ve zoKA2tFC8E`>xXXPI^cV%m5aj3Wm(?c=lHSr4P#v4<};`BIlpckImbF+J{>+|KdvymeAWcfZ>t^a#ou3K-Q5@qGxQnM(9jYo8muqeBRMZ{+ziERAVgucbiimH@-3*#)WU#@Kgp(l>NvZ_os%>Jt&_j1K}#oDC*X)XWp7 z#TzjEy1xAT+%ZJ(J4P;T$N0*DGrp?Qcp^6*V0`(B_Hn?gA|n)$b6_N{I?EAZeCdFF z0~Jl2h*UFA9PEx^9t?ZtJ4TdFxF?&pv736!sZs*T`^43Cf%kospOe+Y9Ke5%kKQ`2=#Ihkl#dKYJ_Z(pj}lSmGTeKn_ul(buk-ZY zTbLA!QOTf83|2-N7z~nuLPiv z+)PO01p|X1%}C2GsNraI3gCBEAmIHT0dU)8Ddk+YY_GMiHcSuhgzj!$o7VW7Kq_6Z zfiEC!E7t>&0$P_m<$>4$!fxD5fY=4XS>F$VaMJg4AneY@MnOCP(#}~a5FbD!S<$l< z{&N?gZfw4w9BV~J`}udg^s)yqx~Nv%`E1UU!%8`6Igz)!BeJijcL3sMLi*dq83Z7j zugY!x8HP@^)+gmQc?w4Z5I8Qk&OvCbgNtKjxfTG`KvgCKZwC-GA#@Faxk9GD&;MTi z2-H4lUF-m33rd?>f9`2r+K1{#ILm(n7XoGP`cli^00NigNklPjzsapE1{@TuFGxG?3Usqq1 zO6}6|E$KAS_r6|(V)IYlxB18KM#`7pb^oV4a{1Q1?vvhIhaf0OG7}+%08U%AnZRo6 zsn5ONo$tOHS%|m(ZQs{F|64!+%pd}{J&pRE*;o1gC!a>lOd!VA>$g4jDKA2VItmHe zCqKNszi%NXC4E9tS*a2%TU7->6nB#vzzh(BIq-eM)WNI{<8&61nQP;tqvwW*1SkgT zSLNvwso%^4sU{sSgqS#1wDv(t`|Q!ih4Y@%WH6BiO&6NM5M<;GGf$cphB9hNGYl&i z?E(=AjbfB;%xVJ36qHO55t^RM=LNHrp*n|5lOa9Lj=l^^or%AY&zYrCLCv0t1eigs zNia($0ZLU>!x@mI61}lmINP#C(pfx6cV%Vr>S# z96Mi^l7wI>S+H1G2AWR;v^u2Cw~`iu6Qian?7&MkX03bM}#%aZbe zZZtH#Zi$8|Y+W>Ucp5$OHJG~!LO?AB#Gxq_MHu?JW~!yv+Dl-7)h1)tbN_-%z=Clb zEC-P5D&)Eg0Gj9PUc*7k32$h_u18@qHmrSwWfonpv2);O$Z(&H^H$ECHlbD1`{tka zA;YR=_br#F1Becc4o3%{UijuG^gRqILKl@eJpjDA(;TS-*o|XVbR0;?K#Z7mU%U^L zFc1_c#2{h%ZHzH@4mj?kA3JpNO|D>?v4Kx zP7e+b?Ag0_-+_0(@58;b7ry5gM{&7yY2NzcS(Z!$q<0EZnPm1o-!SyJQ?itCC$ns= zW6y55&1!NbhNmp*4?Jffwa$$HZHhdIJM!Y1GVPp z>E_u1L0a#EyLR<1(1ZYi)g%HbS%w^jp$ugZ;rMP8&BDB85b%fhU)6i)s*MHDc6>i{ z9mjQL-C;9dnzwd+_uTo@Hr(w%O%p7$cZbd-yE&1!RAA(b<#Nw-5+H!m8Wb&6SVBro z6RPeQaR4k^dE;x}x39SYw+0tN|NZArKK5?+H+2J3BUyfT`{BDUxfeRvdi0ugb2eOi z!^mf)W#H^c zaP)MLt!7S-b$w5{*tB1A=!H9kQFEo|NsZWc!gD?ba(UnOeLIZy-gSOHJ7LO}_tfVC z&_r3#M6KwCVdiqVxcGD{DM3|xDp3*T@%~nk#slJgItc+zJt7ma22fTqwO*J&Dj`IW zOwQ8kJ3QKOq?%P6On_fB?Rj0*m3_@cUCy*zAX$^7NrZ0bHhhHa*1XV@xHL*S=IHuO zbqB;yFu`*I3Rsg;wO~MF_DOqLokkYkLFmdQQ%Hb{WYQ6tIuME1m!wV+k-4@EGmcgr z8mi1xof*2SO(j?^XR0ixdW`4+|92r3>VXi;lFF2ebxTE3b*O|?C7C?r)E=v13IP9) z5M;*H)sje&ixRbtjabrD$Mm|W+IRsQM(2HrFget*he5h77WYyGSoaKA%s~_DvaBA;P=k&gA5de%1h-mUn=4`Jr10t?DQ$<>f zDoJWj>6NOhgltu!Ac0$nZ=XMPj}5oKn2S{i5`Nf32UYvRp5cMWr(Sz(%dD;*0QX(K z?n$@3yQS*@jJ!5ydf0I5Ei>Ly6bXz5@(x!&X#J$i=ITd3xe`kqF6}rXiIFh$9^hYN^qZG?5`QQcP=i)d|$;T+@C| zg=^U1b=zyd{rlMO{yzT8KaEE1tKNU+nr&oRMbnHevXVp!LZjJqUAJjFP%zjJf~cof ztJmuFdZPhu`|HwY&k4s}zTV283dL}M!@{_sT(s@LvqRqrXP2~DWsTa+G-ZObM!9Vm zPn4fza59i6N6ZWYL@Je>P>>}tscs1-H9&PjfIw~!NJ5$AJJyBbE-L{n9k5a`6Vlv z^@5JeqJjE3J{tx88J^p4Raj;*3SBdBQKA8@k*=2`}QBizesdQ z$CDfY^QslgqxXZK_mnH6`9y)sB`a2X_dxba$Z=PCaa4&4nz#edMLjGL+OH}ME?8o9 zUyvKx8dL{^z~{gTT{rN8zzzJS(|UJx1s_{lu#J2v-?d0HOLpUA{rq9KS$CbL>)O8S zcyYMC>v?W7s#V`@GNCemX?<+iXpTLg@7%1uqLdO$VaQPg5<%3m^c}l)Zm>FdWHz6j z-)(9_0MvPRouo%?W<8T`nBes6vhR{Gh*_md!OT&tenj6i+)6yMd*+{ZUO!wN^8$a* z@JURKr8!>U0*nlQI`{`CaDK4;yDO*9uH^Di++$hg_^APV*I3XDxv3YFb}}h73CXN#kyEj7yfcyBKNJv~2?Ng}kBbQcW*sSp+l!du)8% zGR+_iO~Yuttpr8nbfM0HB0wi^9M4yD)6xbhcfMe0hG6E<4&6rJHABx1?y~wmJ5JOpBeY9Q1tog+DYHr9T$Os%Dlw&|q;f9-nPOHOYOiv!5fB6|YK0^Usbvrm z2Mg2e-}u$tTW&kO+--OY4HxQD9?Tb*dWFeEAGz5`k z2BfA;w4tpaeBt;=W61(G)iP6Mp?Ups|A^9oE|~h4T>mrXQks2xPz{jEh9@pOnS2tN zR|-s=o=ODF0ts-OE3r9-ge0h5Zgpl}eR6V**9u0^4Dhe>4gSg>p7sV!K3Vh$U9cPmM zAi$-Q9+G{kEPZ{w3`Fz6<)RZukP3+GMFn z_10|Q1c3ux>z=o=e&Kv@;oLeLxzFlS=m(*cEM?S6CdYFHL1<(MsYCME`E$Fjt;`vk zIcx1H#}XV$5T<~Lm^n$5F0D%5N1kqqs2N|Ix1bzuSM{ zkIk_aYc^qO9=3Y@j|cvAk0lRiy0ucid)yw|adO+v)7x8T_q01a92r*4nDdSYKW^u# zKi&DBJslf#bhOUWph^o_rbuAYeFA4xZI9)tx>$lLz5)|cv zHM_qOc;Zyn)}+idI36R=;*@Ozbiua(fs{hg$`^9I{S~khTG4DecGKey{Q|F{)~YUa zlYp81WdP-(NldsXwWeEddIZ+2l5Un9&pUVi1c>{47a%2TP?=%oQpc(MC`09pNxm77 zc_Bj#-7r;ws2z!?lYmI-352AsI^NdIc-T(c%xa%m?qik^8Z|Bio&(Y>O%KB`Xxc*4 zg{~7zA&A{3b37Hvs%%Li#Dt60b=|axIcpd*q3f3Qkf5 ziR8Hmsqccyv}tVuXN+K)WXV{(Ov}K)e&^VrrfZ^5$hTH%qZwnW4^v#vladShJQWJ{ zdZSVbq%pk_C z$L(*s2`T?h-dn&|avb^NUEPBZ1y8t=meISW-8C~&9CJ8!z{#DRT$mXgxZvgDki+0G z$IKYB*R;%9!3v-BaIm}o{bok}AL(herXS^MYezjjH9hnCGf-3Yt*VWiwpfR;{JesaE}o!47}_8M(=ZM2Ba& zs;ZQz4Md^L#t0Gy(N!=F#ZX}DLmQe}((2Bw9f#)|m!3A^5hNA(*-^7nwU6vQjPu}Q z2Dtoz5O)P|`SApGPgs&`dWrW!s&Pd_*V;sL8xY13`Zpg94(T9%BV(O10r^s|%4y=mmt|Mu?xII4Te%C@eVUrNceH<+p!eP8W7dqdu%uBj$q z3KVheKZXRS4RXy}x{p1!sW0#S=K6h$bK2mpYSzU69CFT|pYRddSn?|se@~h1xi_Bx zxbpDK-Xbj&e3wzNuWqKJ7c-U*w@8aGzJY2vnOoGvmOxt&9|>4oX!D+mtLHQ9^duF>+T9H8|&sZz|Nta@A|-M z1o(G9&m)9qF-!o`4K_{pkV;vkL&Vihh(|~fOs0&u(&P%E)fS=)DSE!iL1Kp3=xp0 zU=Zt(Hs=|F(ORURBl$ukghaY;xKYlNJf?h%M=myLfHv#n0tHK|H+D*4N!xc@f4P0q zwo&cp28=OICX+&pQd%gM2q6j62!Rmkbdoa`+Uzjkxa&(O!C|l$=RCj1n&_?aZsgfi zOAsKKQ|`MpbL{eFHKk$xJY2Rg%MX$M0Eqj@fHX?8K4LL9z64+Ma7I~82!Kz$FCm@d z@<|^c2rxx~HIoW?0-zACU`mLp5Jm)JKTu?uV1$t9QhLt6Z`W_>Q-kL(XwnGInR-h& zRT8F<)yTpIHc(OzZG%Q*Hf5Jlq$r3A+4`wyHwIy;&P2Ip6GQ{e0){aeuU82#;2jXJ7LRNG&iAKqC2gb`N zMvd7nLNw7B8{Z?ArfP+f8B>jmX=*eAB9Wws^wI*z#6+QwQcp_evSu!A2F3*q%}Grt zO%T*ZKt&(X1A&J~LIoM&Fy&2RG5c9~`AUm-JT%0_%wib59MsgPW8~>SZSrQ>lVMq_0VngnSfo`G-sE0m7mome98 zcdg)g3YAmZV?VIGb7@iQp>rEzV-^qT&L%{i4%CBSF~c4UVG9JY2n~o6CVC0fy7>Ht zg^RJm5b~niLPag}+b(aqIA0mv>+Z6tr4VHnV_|#uXlc{7k*z%GwYIxP?h zjZNntawM9KxNVdx^bN0SefIGOD!W&=q}$um?JMRP+Xif%;I0>$`I2!%PGCH9Fi>6{6|L73d-03=;A zIDV`^vE#w1%rrPj7m%ULPe71L1CO8tf~DF495K<5Mhi@cV8)odctCEA*6Tr@am5(o zhQ~NbHCRn%K~Y`X;+~HQ2}YD-G2kp3F8Hd(5P6kR&=L(@RY_eQi8@uJ-WV{4`Bpxb zK_b8iArfH_O#$N)E)h}Ou?sxH6RVw4c|Cx$ekRrTEMH_6%cW$}s7(Q7uIn;hBe5hH z8-|Vn+m5R$il$-4IL4UI2AhC@GY%a2NGrpH9R?N z{c7vLH&0qlAaZ5J=LB=Kx;3*gZ=cmO^Q(w0Y`S^p7)~!hiVEN$>|;y;dss9GRe;vc znQ%=kF~Wio0v=uBVccz`oT>&A>k+6RoFE1qbA%BN9!-&=AmQy?nth0=pgCz};}TQR zPU}`aQD-X zL|F?u!mI_uaYc4vTSg2b8uJRNxvj|^sJR7YMB^z`mWpiscVVJCjnGUR7(~++-zS*e zNJWZ}LTSabTphhAYH!QE`Sllv2p>t0Ke0h9`|tj_U(TPjnB@+e1wlJlEfF-Q3G(W7Uid!>zq`gvwmMV&=`^3Qt144q-1e_?y;AFmDdb{?} z@8Iy~3Q4D0rHo_Etbwu3Ec0E=X(d0Plz|~o{K`VF?~+Bu^-DVTJhitqmq|D4D2VU^ zpC(jboQbg!XPb5xCoI3w@WDpFY1J8?*zjQD}p(a$m)*Q zpWb-Mr?*~oR?F$HY}>n`xZ%$HlKIB*YqEQGRn#i`{f;U_e14jEK0mTkFS+a+_fPas zdP-nI>uykW!jG6|3|V?Qnc@r~9DZ@keA7tHM)cv>wI~Rhn_nWLAlY6+#Ksg^{F^5g z^s-V2FqlJ;Eg)`1iY%J0$g<-C^n&vJpS&YC|0o3T45YulZ%=9eX#9R#q`_qwU#zyN zF*pW?#_q!{hx%@1y!TNA@WC&AmLup{keddf`0EYNJ^BPi354*$u66j*b$0>hC#+j| z;<^Jy2K@ZbkKeN)zkjS0K9Apd#mN~{d*H$QuiyLdtnxBuOE(yxIHr}yeC@y?4$lDq zxnpR}HLXgSgH)p4IsUSi4PyUIg3*eEIymRB9PpY0sbu8ZY#Dko=wJ;_^h*As1 zRtVyZ0{0OGPy@HCV4#3WkeEwP=sE#7u)-=ydb{d@vomBtjwG$K7(rl>l>+jA9N~$l z$3F3JD!J0Y!9d(bb;Rpd;Q)%G9a?X9#f?9RYIP9AU(G^s!cBj2RGw6vS16RE!LQG{RJcDhegI#vbtzb-6n-f5)xg zPP26;4SqNk3u2%mu{Ms|f9@OqgQ~y!kj~XP4KPBwGOHGg5x-&Y_@aia0^CQ~^_ijK z&s>|ekF5eceUvT~QiH8P}OS)D8ut1k(1ek)ksxVD3;R;47AO!*8 zTrl{7W1C{ciD-xpXq}G$$1XpqsWM$_gMh>&@UXA`DJNPI zVT?h7uYv`Lg+Um4v1I7_f}VLZI`e_4)gIdESMoxO?P*J*Ix3VW?BRYXTu=38-Q41? zS%*TrBDT6Ve@PZuLPNy?Lo|!h>K}nUIZS`L^QkXWz=}pa2i57ak%EgP@uWfK86~B# z!&W*QAr$$(pG+pwdV9hd*3%Oe&-HoqPK_I7CB@u*7!y^|W*B@T1XlFC zi#asq3ac%YmL^D;ylSBrNP4m>x8z8`zOiPZwy4A1J8V4>MG%q@@Bl2`U>rghR#6$% z5$C+~+!Nk;;c379;~&0z-4Bjkw?3QAe&*w!>{-0-$$%*360s>3A@A!I7xadh&M z)RGpp(ZGl%p50r0#~IDPdnA9_>g>kes-lrebEyodvQd4xL?y zOD{{zyz;L__`dtEPc<(}HZAPq`kmur4;AtdQ&LixpOs)|C0RS3=94xX<|UgJ5G`3A z-z{P!k2`sl;I8?tC`Qjc_Jq)7|9R(Jc^sv}aO-`WZn=N+&fd{EL^o{dzvccdPjBuI zb>EZLEjZ`o6-OL0@1TR5DK3q=Qmd2N$V^~?bo&Ntx^8kFPZ1W!^+>D~kHq6vKahW8 zCViN$EpqO;u3aq`CMP|PhejuN?;F~-yKnRMy&JdedTz(Qt$X+H8W`R?G&VRkF_5Oi>lX)D0uBN``5thg7*PTg zgCBq~IEP#h@VK92N|gnOAzh-*=wgq0A<>;hFNBVJ)0;kg=RJatyzruUzw6PzKfGJ; z;8^*hV;9Bw(2mjN&3jk14uq>RckF>^UQ;rZJfeG2>?ru$wQcbMU)Oy8+wvb2eBZvU zuiSREV3EbPb`D8$%cnX8KXvJxhlq#2ON=a%=!;q-p5<6x<#7nDTumz!y{t6;@b3hl zc38tJj_b-M)tTqs)P$ETnZIVC*m`K2EhLLu??(rqi6{Oh1dds(2;q7y7P1*JRNLy> zQ0E`hdFd)mFnUpxB3|_3nCVyqKGlk}|ClW-Y!FqI9r5Rd3_K21x3o#-s{=6jur;vF z85?rON35}-YJSqm7kRZxInvT8W?Ob&Kjg>J#DrkQt~3~GB4ai#?`b=H5hhAglX+b8 z=vDJJez5zg%Ud>|mg!3?oI1YmG0nFw@7jIMnfP@lwdIB)M$eC4J+C78l$<@cfaKMYDL2gT)1AgWX~<~tBt`8S z9B!b+L7${Dhi4r8jEyUi16fY!!AS;$EnC&I?2w*B!Vs&O1UhB~IeKMe@EaN{J+)=v zsm=Z2!&Mkxh1$BacP#c5CyAqhQNFvM?;l6Akwh+KvJ7NuR1P5VBsGrbQktg31&!K5 zHxn{fNZ2DNekILtSw{&y>#de@JBrBn+_Ao0BYU?E@7XpnxCe9E)ZCnFZOyfH<~rsz zw$IBpw>7r3H|E+hjZF>BEls(W#^#)ez-`E6lBS76!{16QtR z!Sa(%5o|PM3#=5fT5=|=}dReKw>oLKL z+qE@|8?&jpxk@HbOG~yhC$=tW(}bkew-FtHs(l-UKxfLGeJD*ALz!*0p$=V`IkH0$ z{9;rh)sxxEAhK!1&@15viCB!QYf+@EHp5yVLw4nQLWOR;&mDNi$&dJ@l3TUhitSZx zT6G-PW0k6+=?IA0oN+BzYtKPaH=470bE7h^P48}lgvl5%glehK6NyYpUDAqAn{Quv zNWLMflXQ!gn{Or>FFL-bwKc;y5`$3NqdsK-pehUyF?8GYs*YWDJ)fg|g@UH)Nj+(* zxrT(Iz=}gUOanWG@p9kx{HBNUkKTm0-aZeF&z@+Q^Z3UR(G3~UDx*ijG%cYqR@QEa zjEGzsi)tJ6<%`h^r$l3W*l@P>7yt2yv!yRS{T*g18N4xj$0r}_-GS=J_Pj?Q{O6?? zUEDt~fC*W@cI~-mpYz`TyoOGex*MD87A7eKDG7R{}{48wqc;+6unQM&~!{^BB^2pqXiA>G2P~Q%aY9mU$8o< z6I8P4mLcb#ea>&6DR1&R!*W!0_jlw-;nAe=Y3^TniFS6$K)e@Wj`hmOnc#w>8&uXb*MeUHZgv+l2M^c6mJZB)!`by(No^3Jei%vtSDPoG2P|T#F=|s^0fB;XAZHgl? z)Xo`)=X2_^^_jyJ0^J?Tu0Bc#IS}$S4hjl83C6QpPZ?vFV4^6Rs&dY~vcLa{N@f|T z8a`%zd;RRxS0IzCv#RFNQ#n9^M1_4fkG=1Qc^cM6pyH`1F9krQrWHgMPJt?1RJk zcdhPTq+_mPgn>aQ$q#Ey3x){UhSxOAlcwXaAQ|`!S3GXHn&pG#DIN=PJ6kP!n1+(f z=LlWffKJ}~EN)**YPMLaD@?E+NEXcQOleyl{SPB~ohEuR@*)l=NoI)Vvxa}7(5VoCuTsQUG4~jt|7CRR7H89b z@0_#GT7AeWRNcw@ccRLRgtR`i$9;H@d)%?d2uAh*?|%+CE_!K{R(zRvF&3^o{cAd& z7MgKEGPMCw^=bYrsMmpbyE@87HhIN7tysS0QrDxlSUTLNgn5?hY3ZzM*?LoBb+V|s zjB>>)6^W*ys-q-LObGRS=6Yg@0TbXVAy^>{As`CT6@tMufV$L2%yV4Nc9=&ad_L!j zLXe_xvDN2{!`P%dTBw-1N)(JR(Fw`6B+aA-Ld+LJm{R1}dAnGEq5hUbPurhd7Pscw=JLbd{p;(` zTc)SgiAi_O8~&rTZbkb3d&mF#&!gLRRul}yDs4-UMh&iB-dcftGCg}F(a{rK_1 zUpJMW8FF}lG^lQeBRh?hhOay(YrFh64^5nZ($Ud-O&aoJS&U=g?66y2oh)b~+|@;Q zZI_0ue@Vh9j+2ZL)b+@3dck#`cz#e2Bt*=j$v%jr?3cn4c`B_kTrfrq)l+P@#$4@) zajT6y0FI$r1xa^hOzA+XriFPBIf_Mxae1xOQ_LD$Cjqf4rWNtn@@hb1K&-}?BSsl@ z{m{uxeIesp@A`*xXClFNv1z*sbZG=kC;9yvBq@vN|6I|jAJ(QNZT8ZSN^QH~e@ z15PneKt2X8n3JH;f`NHLWu}H|HcsMcs!xcUbFDUD@?Z>ef*`u!(M(bidW+_M4!Pfd zc-Q?;59%WLc~?HMcOp4ZOb(V(f`=={SXCdbCWcFip<-g7VC*ZJy@lk?d}3!lxpTtY zGnp8wDg~Fgob#yQ!9$hm3I&HhbUyM=w=3AnD~hiZ-%x2%WeMUZRH_lK0Kyz&PJm)m z`+=O(;N8a%?pPUvzDD;a28ixiE*PV@6-*G1F3>P$JhOZ*NBpD1Pv1N6=%d5WL}3TF z7GCx4eS+=3?!Rx-h~UGXyGZam_k8}DHGdQQcJ0dR@7^kSylA1>=-EAEp}>Lo!A*k) zNG#}v0zVk}-Pc<_DtLQDQS)GW zz-SopEUk-;nuq>Z(yg2AZ`@VhtQ-f2y$vOn@VncJzQN~8!ET1Hq^)n zdTEr?9Ef1C(hnJrun*Tl5iVmR#4`MY2?4CgZ|?d6IT?-WZUP56FOj;BWrdYE$PHqZd7_AB?|I*UY<+Og*oMmT#YaSJb2DAM&#eg+FZ?e{OK&V~-Wy_<_xDyk`5M3H+PacU*fx>wliz@~6u? zfA;G377Uw5+*ci$RSC2t$;VD<`rT_gPg|VGs<5`h@EH2RW0Us`IB)y&@K^88m-JSW zY6FO;sFzn`49N!4AsluF?QkAf9~x|bsnmZr6)Z6ZZ&>Ty^lw%@fBAeDO(%ga`1hSD z$=@6>(eox*CV-TOv6?I0Y@Nl+t{&d>l7F*aKHTKJD1FLLArgW-SB8>y_GBHO`PZCq%)a zy*8{Rk-&+xwq{X^V3^xBJU6-*#ls;bu2=ka&STL?Q6nBH9t{Y=I^)55EU8LM4`>O_ z7@`O~Kt90~2*nD+3JQX~6rNC6Qmt`9qlV&X#I5S2%yDS-9pdB*Rps1CIBJyrM~U3( zYk;tHJ;?{bDJuP2BkZv%pWj~{sd#F|Qwp{+X{mV&k5{#kiauJ=MytxWCHmEpt(IMI zed2N8tp3WWFuuj{Pt_FpZaYz@Y;s&zJX56!wWh+F8aHbeSr!F*Q4T%;CD_NiD*tG0mle6 z<6(bdzgMYvw&hf9$F{7pr2e^g-!z#yk&s40DE=2h8J)yOxmgxX+l+XpJdih*9eJ%(DH8?UyR7u7?81itq zx^!jNvX$M zJ)8S3^E|g&cC3o;d5lud8RIOhQpI2m31fp-{#JboOB~7hzHCo2XenBj(5Q`@0CTi+NlyDy-iowU=VeA88SYb#5 zWDu6nSW=}a%})qcY8V5nDgB6pIDiXqbqQ_oaRIAqGFi}#pZnyTN&Guc@e!VmBf)9J zzMY`&RPFy9Dco4~0w5~>OU1pZ=D!vh}wMWOPQNf*_XM1Q>MB23n6 zOvZj4t*WSEnW|@~o;cN%#!@v<1lI#CR-p*{N)V_76spaTbhq9iSoTbiF~MAOG6_v^(ei{1Rj?EYSB*rnYfjP=saba{ zS#CJ8uFjlHaT)mmM?{%xTc<$Pe{s6tdjO){HhC)$>`9dYdZu6(s{ z0Hdt(+9Qq+-EqfW@%p#_<1c^T8~qNSp`y+1*;w9h(JPM1c4ZVaod!m{V-_ZUa7p{R zF5?UL=J(q?Uu8o5ef29Z;Bs!`7-M;hPS5O!WZitJu$rdOWuxwwTn&MF5oGp2th)k1 zI~Ol*<6)883c-DrQqQ9fd7c>Atis|JSrE&Glam3nwXg55%r82&!lHcn)WzmbX zI$7O$!d4ACM~8Kjmg6%F81joLDiN@Gmo&v&WE#vk|6xF4-d z*Q!Ec&$cG^?;G5{v46)#-*r6K=LmC-C_}za1LVD;YYG7$`wsXPcPrd;Frs1^6rOU# zUexz$Oeqh_hep|^823a6V2%l3T<;iOQ=mfcQ)BqmS zi=@TUTszG{U#A!HIYGNT;Pq=#-}*#*CQ&Js%dY2f&gxa+_~5eNcHJ4_P8fqA`BQLE z+_y7B4jKf<<6LK&jjVtG()5eeW~40);R4bHG!4jtAQqp8!%cy{hJ&7{YOAvHMC5T) zQ}r--DAZ+%j}?Yg1PP21geNr`sD{+gd|h?Z%X6Rfa35Dyj((Ko3xS={V1B6CcxPkc zvn{E0DdQaz-er@pnJN_X>DLLItg@e^X{l2F#85f!^C5=5Je+@X|M?WwZHJ{ePgfPH+__Vz`=7()l2IvRnxp{XKNCs11Ox6 zAU7BC1OxXWTnbzPWO4|mY=oRe?p&F_C%Rp^G)Y7-7>La@Kmv!;H`G=2DDwUjl5aESJNw+kVMphOnL+` z1KEOWi*V+m?pVn=1@lJLM=vWuV*TXhlL6Dz6T#| zX~_v*v1*mz2OoJ-bQa6O1jdFEyUU@DKWd)rhvudismSUs0t8Zp#T;a&Bf&8r0Mr(8 zW^2+AHISkj7;tU1j&$tOa^7~)ZhPKi-kRIJ?4p8qH(?EY^oFT>}NakQXqbRU{pc+%=Q|`F6PVQ6JgWdg; zLVsO8ziDAtCX?0(Q9O@U9N+O+#r7SSQ=j{;qY+RQ%qX`l*Q&b3LSg9Xe`=V=L9((E zke)~bOwHc3uYj1zP=W@hZdqJgVuYiylE<%m!HVqoW-DJ}w!%C-Qx$U+Zz6#W|Gw={ z-q61R#Yh1-Ce<|TIb)vVGWNHM)o3(oiXJ{yEYEJswC@}rJ!|=)wON~uGN#$JWa$+@ z`tIrP`HxI{+vG79-LtFu&jqnuy0M}NIIvT>=Lw*V5mAOC{%)w0) zROB?6gWq|U*K!0Fyf>wb;^!Snw_n30l6Y=gn80`K2_V&Nr5H|rl^Tf`d z|KOJ2{^XXieUFaqeb_D!hl;Fz?qb2DC0TFevKMp>zv}$qSDiQZcYE|N<>L+XmAC7k ze1jgIQ+aI1Sns3dd~b8lQ6kyx>n}OwWB-1}xhJoDe!c}n6rO9N2&C0jn!G@+fehl4 zsw_-bkb<>rQx3(kvHakbO1YF^-1pq#`0)Nc+xm8F9v>1@Tco~EjD%{KLfG+q+wm-i z7Asz<=2P+|& zR(V>Z8Qo87eoAr86ssJ)=rW5u$doq5M;N5KGL;1lZllgIdPT-a1749zeYC26rK{=NF_t zE3G&(A+Pj;L)?SQIq*;>Zk(924&U;!*% z67p+az52gDyf)->FIXk}p}RX!WQT+a_8Zs!aPgu=f=@s5EW!Wvy`PBAQmIlKs zB6gQUz2?L5lu_6;Dv?c*55zzd`&P(1-tIo(Z`|K7Xw z7v(elG;#Qy6AK~5IgT6g#q z!O@2_#+U^fE@hN6%8+Pq(NSAt%=bKxGRt!O+7_l^yt{X_r`0@kSzCK+MpKCpvmMW+ z$o5gO>QKiuRh~+yjZKX%$CD1()>jyva3+e?LeU)>9EI^-j$+@$ff-rR45F&>SBFFl zNL3a9NAns@og^-w(zvbp|*7*|W>cR^9Q0Uo!lP<`oThEa^__Sod~~54`Q} zue+R~7&WWf!=>>rFIYY5IX{~i!|ASEDtdHK>RVpriQWe*u69UAcT*yAbj0Js{_Fmm z?`hND@UtIuAAVTax$^w;f4spK_SfBm)*buow;$hVs<Ow~KE52Wq!NHv5o^$(By(F|%LB%UGwqPt*58b2It#r211YG%=1aW^`!Xh50u zBNUQJk)o|J5Lt*Wh!-1}ONot#?I5RG8Xf4}zjyce=x9y5QdFWR6g`186Ns*uX)V)e zW}C%QOE+nmW+mN%Q#rvT-9l2$YPN+W8?l+ug+MZ$%r@wWq;4iuRmGTumA7flQRE9^ zf;bAzv0}9roa*@$VIbrHpb-hDi1lzL@0esFfw=Uh$(f2~TTkV-y!sqyZ75F`ij$K? zv2uYfAOnPPWFONLWYr1WN8AVSFgU?7)MXiH04Cu{m8CV7)@j=C zO_gfY(R4Jqdf{uK;rxpFfJ2*gOcScznW-#mQI|End1d<_3dQ%XY0fuiuPA7{Xk;Lf zAXDJzaO~fjh1Y=nNWr=bP(!?!GsmRjXF*3#d}5#N~b85BuBN+Cs@PN{Jl>FYl@ib=bljdRYh}1bw1Ao#;x- zJDi8}5~be?d*WZIvZF3;0zl|-{h7!Dcf(d@1=2$N4Zp(RmPg&%eELNbeYS%C3rF z_n!RfWo;`Kw&xrEJ$cgi{7Zfcue!bIwNE5Z z*x|M9%Ugv~)fw)U?*j?MG}DdKE;M=HA~}Cj3qYL5gXoy7;S+uzm`AKZFOYm^vTwt$ zD`UH`=>uk*Vg#HdJTNPR`<}V##!pTR-v+f`KdB0SVlx!?Fu@N$qulnS zCRwb8{S%XX9I-eb+vEs7B<-OCQ2c!2zXE^f>l=2w?~6O%cg@7D51<$|bRIhIkXMRY z2?Lw{(ZBH@Z@c>BFJ1fgS6_ZwI6Ghc<&S=O_l;M5{DZ+5%E#XQosYizM|a)wL;&ti zbgn#q-s%ez4c!N=UAMV7@uf{qeCfFjZy1I*?k5-ik)Hbg%41Kkq;Jj$3Y5JForbbJu?Jnd#QXTD!ByM`$X9T0)#S z2$rW1c`2v1hhAM7fg=IaiBu|)&K%JD4G=%HY+1#^z|KvU?J`WTq5)9>liE_z5MWSL z#Y~!+CNrB$H+Q6(+mekrGo97Uq^g@(QE?PouL$9(>IojBGyv)g@)@IoqbF0ij77}{ z{nT+?p8+V^td^KLEV6_UUDslt54Fz7oC(q>D?b*2$w`a`h`|mx!ilvU44AXltKb9W zC8!6#rc-3ed~?l;q<|cpd{pM#lNv5Nr|GyO)2+GYw$_}cs={7tIkqs_0sxO4u?w0m zX*?>8b&q<2&OAQxx|4D(8TCN5y9CFH&zWpN*7gN}T9}vG8AqDPKhlCIpFC1bc1omd zFd_(*j(ZF!gTVQYeT-|0EKw;@I5G%IDJ-M%{%!t~x0m|=G~9Mu{}K1^IPK|eM^uVW zb6lkq2nmhm3?r^9XD`aNC)8_p<}X~?dh!wTuD10*`;mYo;7CXG6}tbAMtYUz+&WhJ z;Ar`J*BjA^Qd69Dp^$nFhTWdBy0Voe&6TCON>^sg)c(>tahQg`l&&33n!RsyNn4MS zHEQPAtV&Y~GB9WysT2{4uuGuE03PBVFc0wJBfv~VLG}?00|%?p^u?vGZF;yvY_1`i z*`_&-vYauyP!;_A71~C*x4<*Yy5h}9b zP=~%uimdcCwF`4B$SYFbwZWn;TcDgoxf0iCm73S$dMxFWDv6V-Urti-TYZACd|NQFW|n@eDj1 zliPOCKRh_t+dtwIE66Eh&mzos96MhwmP$nrVLR1vD?RetrSty2DS?25UuRr!02p8h zRVJ%m(W0&!=&FD*1`G-i0-^B&D@1f40vrGwkJfgh-1a<|`qW2=dWy%!Myu=B&7arV zsOp->m`{=A`bEpHc&O~MO(l9mrTZu3b$1zO?#&!CY%CckO=UG(NzL1lS@-0^D+W&d z?D&FnY}cQbL5Op#XnHa&F)C9ysh~sJiX_XbKGUC&0MT0Fxb=`Q(F-K%RrYWF>%PZ- zJ-qF%;?UE@fv1PJ+};29_0`@xzt*$j%C?2^(wHftEHl&Xnz1yQx@z9S?o4}ELzm@6 zSlFbIsko)Z&21Bv;aGZU@3x&AhnI9WoOZ%FU;o0qHLFn_EqUby*F9UZk-B5z93@lEtc2WJ5{GVSmxXp`_%3A1Sh-*xv?JQNvd76Ps~y4;TFK zv&yYcYLdlj*grYJ#u1P6aZR>h2^grSX{QudWOL06oRPdcJkH7%iV;zCMN1Mb={sfL zEo%le=dy`pG}mqOIt8aPDI|#bV`CzCu~4np;R96BlZuwWL_KKaQI&dQjy>);`#nD7 z!9a!gjj@3-MxCNKE(b+XO`;`GNS4j>1r$`PW5D{WmHtZEQxh%C^99Gh27@{XRM#0q zoztx~F|;|Cdq6x(bwn{k59Hc96UZS}RXL1Vk8JqAJ`WOSpCZvw9SNkMEU6uHdCDm_&Iz((X9RY=! zn;JRip67?BK`E7oFhOlGvno~~IOjFX67oRtG2=Zb)~**u(*(H84hM-N!|o|iU6f*8 zf-q)4lOYx)76*9pU|7<#f^@Fn9^yU+k06I&SHUi+>9PzoU=x%0=hxABH(1y2_a4@~ zKbr1OQ}p|3c0%e@#kiyuhg#S40|29pJ=gxJ*`L~Q%Un|+aNAzC8_a)7p3fb+_d-fN<{?y>- z?%(zA#J{-_a)6|+zmuy^w`uJiSSYd$x}Vl-7+I5Woq<9L@dN`1b}<+1BknWfgo~uz zI$&84TIymr+5q9KPRnJ6I1iWMO77TsGUV%DoS?SQnvC4>U3Iv4kc(Jfq<_V)K6z(L z&`Gypp_fLMZ3_6XrPuCp-QJ1Kc|WOBR|GDn)&nh@S9!^otQO$`l_s{wU(v~_m2 zMfyjsHWX)c)}cH~lw_0O7f}ZxL6}5iOSM0){l|L&rtD^sj}lul*+c_7Od)>1DR(B2m;_3!sl&>g2f84 zs#R;E#2hMm0>cL&5;&d$VoZp6A*{fsTnKrTc|HdqKq+y#<@l@CbejncWAJ_IdE9bo z$!1kg49T7;YP%O-?j81K*JvtPUcOM6EEWsJl5lwy3Z;T@e~nFSADDPjIqZqVNj4bq z6fvr9;P4d&G3Z4MfFxDf2nQsQ8PQ*eCwasPBYnn+=tYrR9V!k#J-X|T(LHw-N1pPW ziBN65vVFlvyOt-gB34y^x3(?l)XnRbADPzG?+orTQk^$E{aaHF)elZN9V_o$n(Me= zbOGn$R&uUosKXUzleyXj-+eB{!@&-vi#@49oB zH@(ubx3p8&6_7n;n`I4REIC!akYeuynUf^uLW}*C^M?Xlc62Flv|`QT6^AV1Qt)37 zS`d#=tuXY(&6q@(^iaX4HhD z5JAbLI`ZxxJh!+3C5{Yxxr|5xiC>X!A&4fN0LYSeLBgOh;7r6LK*I#rFuU|d3We;W%tOZAKLfa zxGnk{G;-}pi$@*)k$>*!RiJF0_qb*_Z*D^?}z2^aA4kG2`E3R^M5k?ASAG zZ`*&vL)-6uWak5q^)~Mq{-jm7C*!|6!IOwdPzOWsnUcTasAQArHR&{~v$VnzglilW zh7=!zO9UgAa2IorgNL+LC*W+$6kNErkT$XSt;cJOL0ZCN;rY_K6EuJ%)wa_Q4wUpP zr#fC1>`x`q|L($13;t~TyYGIwU$6iYg#E&j8uq;Ub+5nbs;jQJ;)<|8m&+{;$Z?z< zF%qDHuwOt6Ldi=nz4WSpp4wXwWki_wnz2 z>bD{9*kg#Tmwoa_C!KiQd1s&Ti4R^a`R;(e_l;|A|LgTaoka+p^0vb^Dg;R+ga~KFOU`{t#~{Hm#~Z)N!=p$Opg3kk*P#}RzV1R3yrdvK+t7@ zdHb_@=Fq)EN-yZEc<7BKTXg2a}d;jJwnT6erW;*OKR3l@|mjZ*6{>J_85j{GJQQ2kb zrZ!{osiDdm89;)$50DGCUdyc4Yj^DR)5E8Zv~%%5LzPegBoP`Mh3$C6cqk(aIP##E%wIt| zmLnI1mP_Pd>=EK(>gwP60s0O@%fJhK_+-|5gCnjZ`jrIz3RLfXy8nxh4~|)0gAQLg zrl�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Ⓢ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
&73y_nCbMkLGFm_N&i3|Ew*?mRk=! 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
2Us3-zgr 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
- -
)} {familyDescription ? ( diff --git a/src/components/expanded-state/asset/ChartExpandedState.js b/src/components/expanded-state/asset/ChartExpandedState.js index 2547b5519f7..d757b64fa42 100644 --- a/src/components/expanded-state/asset/ChartExpandedState.js +++ b/src/components/expanded-state/asset/ChartExpandedState.js @@ -468,6 +468,7 @@ export default function ChartExpandedState({ asset }) { forceShowAll={delayedMorePoolsVisible} hideIfEmpty initialPageAmount={3} + marginTop={24} token={asset?.address} /> )} diff --git a/src/components/expanded-state/ens/AdvancedSection.tsx b/src/components/expanded-state/ens/AdvancedSection.tsx deleted file mode 100644 index 4ea68a79d0c..00000000000 --- a/src/components/expanded-state/ens/AdvancedSection.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import lang from 'i18n-js'; -import { upperFirst } from 'lodash'; -import React from 'react'; -import InfoRow, { InfoRowSkeleton } from './InfoRow'; -import { Stack } from '@rainbow-me/design-system'; - -export default function AdvancedSection({ - isLoading, - resolver, -}: { - isLoading?: boolean; - resolver?: { address?: string; type?: string }; -}) { - return ( - - {isLoading ? ( - - ) : ( - <> - {resolver && ( - - )} - - )} - - ); -} diff --git a/src/components/expanded-state/ens/InfoRow.tsx b/src/components/expanded-state/ens/InfoRow.tsx index eff7b38286b..ec3420e362b 100644 --- a/src/components/expanded-state/ens/InfoRow.tsx +++ b/src/components/expanded-state/ens/InfoRow.tsx @@ -1,11 +1,19 @@ import React, { Fragment, useCallback, useState } from 'react'; import { Switch } from 'react-native-gesture-handler'; +import { useSelector } from 'react-redux'; import { useNavigation } from '../../../navigation/Navigation'; import { useTheme } from '../../../theme/ThemeContext'; import { ShimmerAnimation } from '../../animations'; import ButtonPressAnimation from '../../animations/ButtonPressAnimation'; import { Icon } from '../../icons'; import { ImagePreviewOverlayTarget } from '../../images/ImagePreviewOverlay'; +import { + useAccountSettings, + useENSAddress, + useFetchUniqueTokens, + useOpenENSNFTHandler, +} from '@/hooks'; +import { AppState } from '@/redux/store'; import { Bleed, Box, @@ -59,10 +67,12 @@ export function InfoRowSkeleton() { } export default function InfoRow({ + ensName, explainSheetType, icon = undefined, isImage = false, label, + url, wrapValue = children => children, value = undefined, switchValue, @@ -70,10 +80,12 @@ export default function InfoRow({ useAccentColor, onSwitchChange, }: { + ensName?: string; explainSheetType?: string; icon?: string; isImage?: boolean; label: string; + url?: string; wrapValue?: (children: React.ReactNode) => React.ReactNode; value?: string; switchValue?: boolean; @@ -116,16 +128,7 @@ export default function InfoRow({ {wrapValue( isImage ? ( - <> - {value && ( - - - - )} - + ) : ( - {icon && ( + {icon ? ( - )} - {value && ( + ) : null} + {value ? ( {value} - )} + ) : null} {isSwitch && ( ); } + +function ImageValue({ + ensName, + url, + value, +}: { + ensName?: string; + url?: string; + value?: string; +}) { + const { accountAddress } = useAccountSettings(); + + const { data: address } = useENSAddress(ensName || ''); + + const uniqueTokensAccount = useSelector( + ({ uniqueTokens }: AppState) => uniqueTokens.uniqueTokens + ); + const { data: uniqueTokensProfile } = useFetchUniqueTokens({ + address: address ?? '', + // Don't want to refetch tokens if we already have them. + staleTime: Infinity, + }); + const isSelf = address === accountAddress; + const uniqueTokens = isSelf ? uniqueTokensAccount : uniqueTokensProfile; + + const { onPress } = useOpenENSNFTHandler({ uniqueTokens, value }); + const enableZoomOnPress = !onPress; + + if (!url) return null; + return ( + + + + ); +} diff --git a/src/components/expanded-state/ens/ProfileInfoSection.tsx b/src/components/expanded-state/ens/ProfileInfoSection.tsx index 2903234bec2..56ced872d25 100644 --- a/src/components/expanded-state/ens/ProfileInfoSection.tsx +++ b/src/components/expanded-state/ens/ProfileInfoSection.tsx @@ -8,13 +8,12 @@ import { ENS_RECORDS } from '@rainbow-me/helpers/ens'; import { useENSRecordDisplayProperties } from '@rainbow-me/hooks'; const omitRecordKeys = [ENS_RECORDS.avatar]; -const topRecordKeys = [ENS_RECORDS.cover, ENS_RECORDS.description]; +const topRecordKeys = [ENS_RECORDS.header, ENS_RECORDS.description]; -const imageKeyMap = { - [ENS_RECORDS.avatar]: 'avatarUrl', - [ENS_RECORDS.cover]: 'coverUrl', -} as { - [key: string]: 'avatarUrl' | 'coverUrl'; +type ImageSource = { imageUrl?: string | null }; +type ENSImages = { + avatar?: ImageSource; + header?: ImageSource; }; export default function ProfileInfoSection({ @@ -28,23 +27,16 @@ export default function ProfileInfoSection({ allowEdit?: boolean; coinAddresses?: { [key: string]: string }; ensName?: string; - images?: { - avatarUrl?: string | null; - coverUrl?: string | null; - }; + images?: ENSImages; isLoading?: boolean; records?: Partial; }) { const recordsArray = useMemo( () => - Object.entries(records || {}) - .filter(([key]) => !omitRecordKeys.includes(key as ENS_RECORDS)) - .map(([key, value]) => - images?.[imageKeyMap[key]] - ? [key, images[imageKeyMap[key]] as string] - : [key, value] - ), - [images, records] + Object.entries(records || {}).filter( + ([key]) => !omitRecordKeys.includes(key as ENS_RECORDS) + ), + [records] ); const [topRecords, otherRecords] = useMemo(() => { @@ -77,6 +69,7 @@ export default function ProfileInfoSection({ !isImageValue ? ( diff --git a/src/components/expanded-state/unique-token/ENSBriefTokenInfoRow.tsx b/src/components/expanded-state/unique-token/ENSBriefTokenInfoRow.tsx index 5ed14e99004..0f585af20fe 100644 --- a/src/components/expanded-state/unique-token/ENSBriefTokenInfoRow.tsx +++ b/src/components/expanded-state/unique-token/ENSBriefTokenInfoRow.tsx @@ -6,9 +6,10 @@ import { InteractionManager } from 'react-native'; import { ENSConfirmRenewSheetHeight } from '../../../screens/ENSConfirmRegisterSheet'; import { ButtonPressAnimation } from '../../animations'; import { TokenInfoItem, TokenInfoValue } from '../../token-info'; +import { PROFILES, useExperimentalFlag } from '@rainbow-me/config'; import { Column, Columns, Inset } from '@rainbow-me/design-system'; import { REGISTRATION_MODES } from '@rainbow-me/helpers/ens'; -import { useENSProfile, useENSRegistration } from '@rainbow-me/hooks'; +import { useENSAvatar, useENSRegistration } from '@rainbow-me/hooks'; import Routes from '@rainbow-me/routes'; import { useTheme } from '@rainbow-me/theme'; @@ -30,7 +31,8 @@ export default function ENSBriefTokenInfoRow({ const { colors } = useTheme(); const { navigate } = useNavigation(); const { startRegistration } = useENSRegistration(); - const { data } = useENSProfile(ensName); + const profilesEnabled = useExperimentalFlag(PROFILES); + const { data: avatar } = useENSAvatar(ensName, { enabled: profilesEnabled }); const [showExpiryDistance, setShowExpiryDistance] = useState(true); const handlePressExpiryDate = useCallback(() => { setShowExpiryDistance(x => !x); @@ -44,7 +46,7 @@ export default function ENSBriefTokenInfoRow({ ensName: cleanENSName, externalAvatarUrl, longFormHeight: - ENSConfirmRenewSheetHeight + (data?.images?.avatarUrl ? 70 : 0), + ENSConfirmRenewSheetHeight + (avatar?.imageUrl ? 70 : 0), mode: REGISTRATION_MODES.RENEW, }); }); @@ -53,7 +55,7 @@ export default function ENSBriefTokenInfoRow({ startRegistration, navigate, externalAvatarUrl, - data?.images?.avatarUrl, + avatar?.imageUrl, ]); return ( diff --git a/src/components/expanded-state/unique-token/ZoomableWrapper.js b/src/components/expanded-state/unique-token/ZoomableWrapper.js index 21583a1bff7..3f48246d6d4 100644 --- a/src/components/expanded-state/unique-token/ZoomableWrapper.js +++ b/src/components/expanded-state/unique-token/ZoomableWrapper.js @@ -101,7 +101,7 @@ export const ZoomableWrapper = ({ // eslint-disable-next-line react-hooks/rules-of-hooks const yDisplacement = givenYDisplacement || useSharedValue(0); - const { height: deviceHeight, width: deviceWidth } = useDimensions(); + let { height: deviceHeight, width: deviceWidth } = useDimensions(); let deviceHeightWithMaybeHiddenStatusBar = deviceHeight; if (!hideStatusBar) { diff --git a/src/components/fab/SearchFab.js b/src/components/fab/SearchFab.js deleted file mode 100644 index d3cc5a2f524..00000000000 --- a/src/components/fab/SearchFab.js +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { lightModeThemeColors } from '../../styles/colors'; -import { useTheme } from '../../theme/ThemeContext'; -import { magicMemo } from '../../utils'; -import { Text } from '../text'; -import FloatingActionButton from './FloatingActionButton'; - -const FabShadow = [ - [0, 10, 30, lightModeThemeColors.shadow, 0.6], - [0, 5, 15, lightModeThemeColors.blueGreyDark, 1], -]; - -const SearchFab = ({ disabled, ...props }) => { - const { colors, isDarkMode } = useTheme(); - - return ( - - - 􀊫 - - - ); -}; - -export default magicMemo(SearchFab, ['disabled']); diff --git a/src/components/fab/index.js b/src/components/fab/index.js index 46573d81c2e..d1272a983c1 100644 --- a/src/components/fab/index.js +++ b/src/components/fab/index.js @@ -6,4 +6,3 @@ export { FloatingActionButtonSize, } from './FloatingActionButton'; export { default as SendFab } from './SendFab'; -export { default as SearchFab } from './SearchFab'; diff --git a/src/components/fields/CheckboxField.tsx b/src/components/fields/CheckboxField.tsx new file mode 100644 index 00000000000..7e4eb967944 --- /dev/null +++ b/src/components/fields/CheckboxField.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { ButtonPressAnimation } from '../animations'; +import { + AccentColorProvider, + Box, + Inline, + Inset, + Text, + useForegroundColor, +} from '@rainbow-me/design-system'; + +export default function CheckboxField({ + color: customColor, + isChecked, + label, + onPress, + testID, +}: { + color: string; + isChecked: boolean; + label: string; + onPress: () => void; + testID: string; +}) { + const secondary15 = useForegroundColor('secondary15'); + const action = useForegroundColor('action'); + + return ( + + + + {isChecked && ( + + + + 􀆅 + + + + )} + + + + + {label} + + + + + + ); +} diff --git a/src/components/header/ProfileHeaderButton.js b/src/components/header/ProfileHeaderButton.js index 4559c2cc277..26e73474c73 100644 --- a/src/components/header/ProfileHeaderButton.js +++ b/src/components/header/ProfileHeaderButton.js @@ -9,6 +9,7 @@ import { useAccountProfile, useRequests } from '@rainbow-me/hooks'; import Routes from '@rainbow-me/routes'; export default function ProfileHeaderButton() { + const { colors } = useTheme(); const { navigate } = useNavigation(); const { pendingRequestCount } = useRequests(); const { accountSymbol, accountColor, accountImage } = useAccountProfile(); @@ -21,8 +22,6 @@ export default function ProfileHeaderButton() { navigate, ]); - const { colors } = useTheme(); - return ( ( + + + + + + + + + + + +); + +export default ENSCircleIcon; diff --git a/src/components/images/ImagePreviewOverlay.tsx b/src/components/images/ImagePreviewOverlay.tsx index e5f623d8eab..515375a1bdb 100644 --- a/src/components/images/ImagePreviewOverlay.tsx +++ b/src/components/images/ImagePreviewOverlay.tsx @@ -107,6 +107,7 @@ const yOffsetAtom = atomFamily({ const ImageOverlayConfigContext = createContext<{ enableZoom: boolean; useBackgroundOverlay?: boolean; + yPosition?: SharedValue; }>({ enableZoom: false, }); @@ -145,7 +146,7 @@ export default function ImagePreviewOverlay({ return ( {children} {enableZoom && ( @@ -433,7 +434,9 @@ export function ImagePreviewOverlayTarget({ uri?: never; } )) { - const { enableZoom: enableZoom_ } = useContext(ImageOverlayConfigContext); + const { enableZoom: enableZoom_, yPosition } = useContext( + ImageOverlayConfigContext + ); const enableZoom = enableZoom_ && imageUrl; const id = useMemo(() => uniqueId(), []); @@ -525,7 +528,8 @@ export function ImagePreviewOverlayTarget({ const xOffset = args[4]; const yOffset = args[5]; typeof xOffset === 'number' && setXOffset(xOffset); - typeof yOffset === 'number' && setYOffset(yOffset - topOffset); + typeof yOffset === 'number' && + setYOffset(yOffset - topOffset + (yPosition?.value ?? 0)); hasMounted.current = true; }); } @@ -533,7 +537,7 @@ export function ImagePreviewOverlayTarget({ android ? 500 : 0 ); }, - [aspectRatio, setWidth, setXOffset, setYOffset, topOffset] + [aspectRatio, setWidth, setXOffset, setYOffset, topOffset, yPosition?.value] ); const children = useMemo(() => { diff --git a/src/components/inputs/InlineField.tsx b/src/components/inputs/InlineField.tsx index 79da295a461..420821a103b 100644 --- a/src/components/inputs/InlineField.tsx +++ b/src/components/inputs/InlineField.tsx @@ -28,6 +28,7 @@ export type InlineFieldProps = { onFocus?: TextInputProps['onFocus']; onEndEditing?: TextInputProps['onEndEditing']; selectionColor?: string; + shouldFormatText?: boolean; startsWith?: string; validations?: { onChange?: { @@ -48,9 +49,9 @@ export default function InlineField({ onFocus, placeholder, inputProps, - validations, onEndEditing, selectionColor, + shouldFormatText, startsWith, value, testID, @@ -72,25 +73,6 @@ export default function InlineField({ } }, []); - const handleChangeText = useCallback( - text => { - const { onChange: { match = null } = {} } = validations || {}; - if (!match) { - onChangeText(text); - return; - } - if (text === '') { - onChangeText(text); - return; - } - if (match?.test(text)) { - onChangeText(text); - return; - } - }, - [onChangeText, validations] - ); - const valueRef = useRef(value); const style = useMemo( () => ({ @@ -121,6 +103,11 @@ export default function InlineField({ [textStyle, inputHeight, inputProps?.multiline, startsWith, width] ); + let keyboardType = inputProps?.keyboardType; + if (android) { + keyboardType = shouldFormatText ? 'default' : 'visible-password'; + } + return ( @@ -163,9 +150,11 @@ export default function InlineField({ )} diff --git a/src/components/list/MarqueeList.js b/src/components/list/MarqueeList.js index a8ca19d2392..95176260ab5 100644 --- a/src/components/list/MarqueeList.js +++ b/src/components/list/MarqueeList.js @@ -56,7 +56,7 @@ const SingleElement = ({ ); }; -const SwipeableList = ({ components, speed, testID }) => { +const SwipeableList = ({ components, height, speed, testID }) => { const transX = useSharedValue(0); const swiping = useSharedValue(0); const offset = useSharedValue(100000); @@ -175,10 +175,7 @@ const SwipeableList = ({ components, speed, testID }) => { ref={panRef} simultaneousHandlers={[lpRef, tapRef]} > - + { ); }; -const MarqueeList = ({ items = [], renderItem, speed, testID }) => { +const MarqueeList = ({ height, items = [], renderItem, speed, testID }) => { return ( <> { testID: item.testID, }), }))} + height={height} speed={speed} testID={testID} /> diff --git a/src/components/send/SendButton.js b/src/components/send/SendButton.js index dd3a556f621..3ff0027dd66 100644 --- a/src/components/send/SendButton.js +++ b/src/components/send/SendButton.js @@ -5,9 +5,11 @@ import { HoldToAuthorizeButton } from '../buttons'; export default function SendButton({ backgroundColor, disabled, + insufficientEth, isAuthorizing, isNft, onLongPress, + requiresChecks, testID, ...props }) { @@ -34,8 +36,10 @@ export default function SendButton({ hideInnerBorder isAuthorizing={isAuthorizing} label={ - disabled + disabled && requiresChecks ? `􀄨 ${lang.t('wallet.transaction.complete_checks')}` + : insufficientEth + ? lang.t('button.confirm_exchange.insufficient_funds') : lang.t('button.hold_to_send') } onLongPress={onLongPress} diff --git a/src/components/send/SendHeader.js b/src/components/send/SendHeader.js index 8e299589acc..4fd3746f060 100644 --- a/src/components/send/SendHeader.js +++ b/src/components/send/SendHeader.js @@ -19,7 +19,6 @@ import useExperimentalFlag, { } from '@rainbow-me/config/experimentalHooks'; import { resolveNameOrAddress } from '@rainbow-me/handlers/web3'; import { removeFirstEmojiFromString } from '@rainbow-me/helpers/emojiHandler'; -import { isENSAddressFormat } from '@rainbow-me/helpers/validators'; import { useClipboard, useDimensions } from '@rainbow-me/hooks'; import Routes from '@rainbow-me/routes'; import styled from '@rainbow-me/styled-components'; @@ -176,9 +175,7 @@ export default function SendHeader({ destructiveButtonIndex: 0, options: [ lang.t('contacts.options.delete'), // <-- destructiveButtonIndex - profilesEnabled && isENSAddressFormat(recipient) - ? lang.t('contacts.options.view') - : lang.t('contacts.options.edit'), + lang.t('contacts.options.edit'), lang.t('wallet.settings.copy_address_capitalized'), lang.t('contacts.options.cancel'), // <-- cancelButtonIndex ], @@ -194,15 +191,8 @@ export default function SendHeader({ removeContact: removeContact, }); } else if (buttonIndex === 1) { - if (profilesEnabled && isENSAddressFormat(recipient)) { - navigate(Routes.PROFILE_SHEET, { - address: recipient, - fromRoute: 'SendHeader', - }); - } else { - handleNavigateToContact(); - onRefocusInput(); - } + handleNavigateToContact(); + onRefocusInput(); } else if (buttonIndex === 2) { setClipboard(hexAddress); onRefocusInput(); @@ -213,10 +203,7 @@ export default function SendHeader({ contact?.ens, handleNavigateToContact, hexAddress, - navigate, onRefocusInput, - profilesEnabled, - recipient, removeContact, setClipboard, name, diff --git a/src/components/settings-menu/BackupSection/WalletSelectionView.js b/src/components/settings-menu/BackupSection/WalletSelectionView.js index 7a55e938757..e1a04c42222 100644 --- a/src/components/settings-menu/BackupSection/WalletSelectionView.js +++ b/src/components/settings-menu/BackupSection/WalletSelectionView.js @@ -9,6 +9,7 @@ import Divider from '../../Divider'; import { ButtonPressAnimation } from '../../animations'; import { BottomRowText } from '../../coin-row'; import { ContactAvatar } from '../../contacts'; +import ImageAvatar from '../../contacts/ImageAvatar'; import { Icon } from '../../icons'; import { Centered, Column, ColumnWithMargins, Row } from '../../layout'; import { Text, TruncatedAddress } from '../../text'; @@ -132,7 +133,7 @@ const WalletSelectionView = () => { const visibleAccounts = wallet.addresses.filter(a => a.visible); const account = visibleAccounts[0]; const totalAccounts = visibleAccounts.length; - const { color, label, index, address } = account; + const { color, image, label, index, address } = account; if (wallet.backupType === WalletBackupTypes.cloud) { cloudBackedUpWallets += 1; } @@ -153,13 +154,22 @@ const WalletSelectionView = () => { > - + {image ? ( + + ) : ( + + )} {labelOrName ? ( diff --git a/src/components/step-indicator/StepIndicator.tsx b/src/components/step-indicator/StepIndicator.tsx index f73d03f89ad..22b8b60adc6 100644 --- a/src/components/step-indicator/StepIndicator.tsx +++ b/src/components/step-indicator/StepIndicator.tsx @@ -8,9 +8,11 @@ import Animated, { withTiming, } from 'react-native-reanimated'; import { Box, Columns, useForegroundColor } from '@rainbow-me/design-system'; +import { useDimensions } from '@rainbow-me/hooks'; import { magicMemo } from '@rainbow-me/utils'; const PULSE_STEP_DURATION = 1000; +const STEP_SPACING = 9; type StepIndicatorProps = { steps: number; // 1 indexed @@ -18,6 +20,8 @@ type StepIndicatorProps = { }; const StepIndicator = ({ steps, currentStep }: StepIndicatorProps) => { + const { width: screenWidth } = useDimensions(); + const stepWidth = screenWidth / steps - STEP_SPACING; const accentColor = useForegroundColor('accent'); const accentColorTint = accentColor + '25'; @@ -35,9 +39,9 @@ const StepIndicator = ({ steps, currentStep }: StepIndicatorProps) => { const pulseStepTranslate = useDerivedValue(() => withRepeat( withSequence( - withTiming(-100, { duration: PULSE_STEP_DURATION }), - withTiming(-100, { duration: PULSE_STEP_DURATION }), - withTiming(0, { duration: PULSE_STEP_DURATION }) + withTiming(-stepWidth, { duration: PULSE_STEP_DURATION }), + withTiming(-stepWidth, { duration: PULSE_STEP_DURATION }), + withTiming(5, { duration: PULSE_STEP_DURATION }) ), -1 ) @@ -51,8 +55,8 @@ const StepIndicator = ({ steps, currentStep }: StepIndicatorProps) => { ); const animatedPulseStyle = useAnimatedStyle(() => ({ - ...(ios ? { left: `${pulseStepTranslate?.value}%` } : {}), opacity: pulseStepOpacity?.value, + transform: [{ translateX: pulseStepTranslate.value }], })); const animatedFinishedStyle = useAnimatedStyle(() => ({ @@ -61,7 +65,7 @@ const StepIndicator = ({ steps, currentStep }: StepIndicatorProps) => { return ( - + {Array.from({ length: steps }).map((_, index) => { const stepIndex = index + 1; const isCurrentStep = stepIndex === currentStep; diff --git a/src/components/walletconnect-list/WalletConnectListItem.js b/src/components/walletconnect-list/WalletConnectListItem.js index ef6d8cef922..f660512a470 100644 --- a/src/components/walletconnect-list/WalletConnectListItem.js +++ b/src/components/walletconnect-list/WalletConnectListItem.js @@ -22,11 +22,7 @@ import { NETWORK_MENU_ACTION_KEY_FILTER, networksMenuItems, } from '@rainbow-me/helpers/walletConnectNetworks'; -import { - useAccountSettings, - useWalletConnectConnections, - useWallets, -} from '@rainbow-me/hooks'; +import { useWalletConnectConnections, useWallets } from '@rainbow-me/hooks'; import { Navigation, useNavigation } from '@rainbow-me/navigation'; import Routes from '@rainbow-me/routes'; import styled from '@rainbow-me/styled-components'; @@ -89,7 +85,6 @@ export default function WalletConnectListItem({ const { goBack } = useNavigation(); const { colors } = useTheme(); const { wallets, walletNames } = useWallets(); - const { network } = useAccountSettings(); const overrideLogo = useMemo(() => { return dappLogoOverride(dappUrl); @@ -104,13 +99,12 @@ export default function WalletConnectListItem({ const approvalAccountInfo = getAccountProfileInfo( selectedWallet, walletNames, - network, account ); return { ...approvalAccountInfo, }; - }, [wallets, walletNames, network, account]); + }, [wallets, walletNames, account]); const connectionNetworkInfo = useMemo(() => { const network = ethereumUtils.getNetworkFromChainId(Number(chainId)); diff --git a/src/config/experimental.ts b/src/config/experimental.ts index 94155be83d8..7d0627cabcd 100644 --- a/src/config/experimental.ts +++ b/src/config/experimental.ts @@ -20,7 +20,7 @@ export const defaultConfig = { [L2_TXS]: { needsRestart: true, settings: true, value: false }, [LANGUAGE_SETTINGS]: { settings: false, value: false }, [NOTIFICATIONS]: { needsRestart: true, settings: true, value: false }, - [PROFILES]: { settings: true, value: false }, + [PROFILES]: { settings: true, value: true }, [REVIEW_ANDROID]: { settings: false, value: false }, }; diff --git a/src/design-system/color/palettes.ts b/src/design-system/color/palettes.ts index 99a6cd488fa..2edd67dc1b3 100644 --- a/src/design-system/color/palettes.ts +++ b/src/design-system/color/palettes.ts @@ -5,9 +5,10 @@ export const colors = { appleBlueLight: '#1F87FF', black: '#000000', blackTint: '#12131a', - grey: 'rgb(60, 66, 82)', + grey: '#3C4252', grey06: 'rgba(60, 66, 82, 0.06)', grey10: 'rgba(60, 66, 82, 0.1)', + grey15: 'rgba(60, 66, 82, 0.15)', grey20: 'rgba(60, 66, 82, 0.2)', grey25: 'rgba(60, 66, 82, 0.25)', grey30: 'rgba(60, 66, 82, 0.3)', @@ -21,6 +22,7 @@ export const colors = { sky: '#E0E8FF', sky06: 'rgba(224, 232, 255, 0.06)', sky10: 'rgba(224, 232, 255, 0.1)', + sky15: 'rgba(224, 232, 255, 0.15)', sky20: 'rgba(224, 232, 255, 0.2)', sky25: 'rgba(224, 232, 255, 0.25)', sky30: 'rgba(224, 232, 255, 0.3)', @@ -33,6 +35,7 @@ export const colors = { white: '#FFFFFF', white06: 'rgba(255, 255, 255, 0.06)', white10: 'rgba(255, 255, 255, 0.1)', + white15: 'rgba(255, 255, 255, 0.15)', white20: 'rgba(255, 255, 255, 0.2)', white25: 'rgba(255, 255, 255, 0.25)', white30: 'rgba(255, 255, 255, 0.3)', @@ -114,6 +117,7 @@ export type ForegroundColor = | 'secondary' | 'secondary06' | 'secondary10' + | 'secondary15' | 'secondary20' | 'secondary25' | 'secondary30' @@ -179,6 +183,11 @@ export const foregroundColors: Record< darkTinted: colors.white10, light: colors.grey10, }, + secondary15: { + dark: colors.sky15, + darkTinted: colors.white15, + light: colors.grey15, + }, secondary20: { dark: colors.sky20, darkTinted: colors.white20, diff --git a/src/design-system/layout/shadow.ts b/src/design-system/layout/shadow.ts index 176b3d16900..9bcecafcf40 100644 --- a/src/design-system/layout/shadow.ts +++ b/src/design-system/layout/shadow.ts @@ -44,17 +44,25 @@ export const shadowHierarchy = { opacity: 0.8, }, } as ShadowValue, - '12px medium': { + '12px light': { ios: [ { - offset: { x: 0, y: 2 }, - blur: 4, - opacity: 0.12, + offset: { x: 0, y: 4 }, + blur: 12, + opacity: 0.1, }, + ], + android: { + elevation: 12, + opacity: 0.6, + }, + } as ShadowValue, + '12px medium': { + ios: [ { - offset: { x: 0, y: 6 }, + offset: { x: 0, y: 4 }, blur: 12, - opacity: 0.04, + opacity: 0.15, }, ], android: { @@ -93,6 +101,19 @@ export const shadowHierarchy = { opacity: 0.5, }, } as ShadowValue, + '15px medium': { + ios: [ + { + offset: { x: 0, y: 5 }, + blur: 15, + opacity: 0.2, + }, + ], + android: { + elevation: 15, + opacity: 0.5, + }, + } as ShadowValue, '21px light': { ios: [ { diff --git a/src/design-system/typography/typeHierarchy.ts b/src/design-system/typography/typeHierarchy.ts index d992355aef2..49367b2d886 100644 --- a/src/design-system/typography/typeHierarchy.ts +++ b/src/design-system/typography/typeHierarchy.ts @@ -12,7 +12,7 @@ export const typeHierarchy = { '20px': { fontSize: 20, letterSpacing: 0.6, - lineHeight: 24, + lineHeight: 22, marginCorrection: { android: 0, ios: -0.5, @@ -63,6 +63,15 @@ export const typeHierarchy = { ios: 0.5, }, }, + '44px': { + fontSize: 44, + letterSpacing: 0.4, + lineHeight: 53, + marginCorrection: { + android: 0, + ios: 0.5, + }, + }, }, text: { @@ -138,5 +147,50 @@ export const typeHierarchy = { ios: -0.3, }, }, + 'icon 12px': { + fontSize: 12, + letterSpacing: 0, + lineHeight: 14, + marginCorrection: { + android: 0, + ios: 0, + }, + }, + 'icon 14px': { + fontSize: 14, + letterSpacing: 0, + lineHeight: 19, + marginCorrection: { + android: 0, + ios: 0, + }, + }, + 'icon 16px': { + fontSize: 16, + letterSpacing: 0, + lineHeight: 22, + marginCorrection: { + android: 0, + ios: 0, + }, + }, + 'icon 19px': { + fontSize: 19, + letterSpacing: 0, + lineHeight: 24, + marginCorrection: { + android: 0, + ios: 0, + }, + }, + 'icon 20px': { + fontSize: 20, + letterSpacing: 0, + lineHeight: 24, + marginCorrection: { + android: 0, + ios: 0, + }, + }, }, } as const; diff --git a/src/ens-avatar/src/index.ts b/src/ens-avatar/src/index.ts index f230c95cab2..ff7518df9c0 100644 --- a/src/ens-avatar/src/index.ts +++ b/src/ens-avatar/src/index.ts @@ -21,7 +21,7 @@ export const specs: { [key: string]: new () => Spec } = Object.freeze({ export interface AvatarRequestOpts { allowNonOwnerNFTs?: boolean; - type?: 'avatar' | 'cover'; + type?: 'avatar' | 'header'; } interface AvatarResolverOpts { diff --git a/src/ens-avatar/src/specs/erc1155.ts b/src/ens-avatar/src/specs/erc1155.ts index 3fce23d5085..77ec6202d21 100644 --- a/src/ens-avatar/src/specs/erc1155.ts +++ b/src/ens-avatar/src/specs/erc1155.ts @@ -3,7 +3,11 @@ import { Contract } from '@ethersproject/contracts'; import { BaseProvider } from '@ethersproject/providers'; import { AvatarRequestOpts } from '..'; import { resolveURI } from '../utils'; -import { apiGetUniqueTokenImage } from '@rainbow-me/handlers/opensea-api'; +import { UniqueAsset } from '@rainbow-me/entities'; +import { apiGetAccountUniqueToken } from '@rainbow-me/handlers/opensea-api'; +import { getNFTByTokenId } from '@rainbow-me/handlers/simplehash'; +import svgToPngIfNeeded from '@rainbow-me/handlers/svgs'; +import { NetworkTypes } from '@rainbow-me/helpers'; const abi = [ 'function uri(uint256 _id) public view returns (string memory)', @@ -37,10 +41,19 @@ export default class ERC1155 { return JSON.parse(_resolvedUri); } - const { image_url } = await apiGetUniqueTokenImage( - contractAddress, - tokenID - ); - return { image: image_url }; + let image; + try { + const data: UniqueAsset = await apiGetAccountUniqueToken( + NetworkTypes.mainnet, + contractAddress, + tokenID + ); + image = svgToPngIfNeeded(data?.image_url, false) || data?.lowResUrl; + } catch (error) { + const data = await getNFTByTokenId({ contractAddress, tokenId: tokenID }); + image = data?.previews?.image_medium_url; + if (!image) throw new Error('no image found'); + } + return { image }; } } diff --git a/src/ens-avatar/src/specs/erc721.ts b/src/ens-avatar/src/specs/erc721.ts index e167bdfa68b..182caaa52b4 100644 --- a/src/ens-avatar/src/specs/erc721.ts +++ b/src/ens-avatar/src/specs/erc721.ts @@ -3,8 +3,11 @@ import { Contract } from '@ethersproject/contracts'; import { BaseProvider } from '@ethersproject/providers'; import { AvatarRequestOpts } from '..'; import { resolveURI } from '../utils'; -import { apiGetUniqueTokenImage } from '@rainbow-me/handlers/opensea-api'; +import { UniqueAsset } from '@rainbow-me/entities'; +import { apiGetAccountUniqueToken } from '@rainbow-me/handlers/opensea-api'; import { getNFTByTokenId } from '@rainbow-me/handlers/simplehash'; +import svgToPngIfNeeded from '@rainbow-me/handlers/svgs'; +import { NetworkTypes } from '@rainbow-me/helpers'; const abi = [ 'function tokenURI(uint256 tokenId) external view returns (string memory)', @@ -46,11 +49,16 @@ export default class ERC721 { let image; try { - const data = await apiGetUniqueTokenImage(contractAddress, tokenID); - image = data?.image_url; + const data: UniqueAsset = await apiGetAccountUniqueToken( + NetworkTypes.mainnet, + contractAddress, + tokenID + ); + image = svgToPngIfNeeded(data?.image_url, false) || data?.lowResUrl; } catch (error) { const data = await getNFTByTokenId({ contractAddress, tokenId: tokenID }); image = data?.previews?.image_medium_url; + if (!image) throw new Error('no image found'); } return { image }; } diff --git a/src/entities/ens.ts b/src/entities/ens.ts new file mode 100644 index 00000000000..cdd9214392a --- /dev/null +++ b/src/entities/ens.ts @@ -0,0 +1,3 @@ +import { useENSProfile } from '@rainbow-me/hooks'; + +export type ENSProfile = ReturnType; diff --git a/src/entities/transactions/transaction.ts b/src/entities/transactions/transaction.ts index 75ceb6d11d6..8e642fa33ba 100644 --- a/src/entities/transactions/transaction.ts +++ b/src/entities/transactions/transaction.ts @@ -33,6 +33,8 @@ export interface RainbowTransaction { pending: boolean; protocol?: ProtocolType | null; flashbots?: boolean; + ensRegistrationName?: string; + ensRegistration?: boolean; sourceAmount?: string; // for purchases status: TransactionStatus; symbol: string | null; @@ -60,6 +62,8 @@ export interface NewTransaction { nonce: number | null; protocol?: ProtocolType | null; flashbots?: boolean; + ensRegistrationName?: string; + ensRegistration?: boolean; sourceAmount?: string; // for purchases status?: TransactionStatus; timestamp?: number; // for purchases diff --git a/src/handlers/ens.ts b/src/handlers/ens.ts index 49edf6bfe89..efc20a148cd 100644 --- a/src/handlers/ens.ts +++ b/src/handlers/ens.ts @@ -1,8 +1,9 @@ import { formatsByCoinType, formatsByName } from '@ensdomains/address-encoder'; +import { getAddress } from '@ethersproject/address'; import { Resolver } from '@ethersproject/providers'; import { captureException } from '@sentry/react-native'; import { Duration, sub } from 'date-fns'; -import { isZeroAddress } from 'ethereumjs-util'; +import { isValidAddress, isZeroAddress } from 'ethereumjs-util'; import { BigNumber } from 'ethers'; import { debounce, isEmpty, sortBy } from 'lodash'; import { ensClient } from '../apollo/client'; @@ -22,8 +23,17 @@ import { EnsGetRecordsData, EnsGetRegistrationData, } from '../apollo/queries'; -import { ensProfileImagesQueryKey } from '../hooks/useENSProfileImages'; -import { ENSActionParameters } from '../raps/common'; +import { prefetchENSAddress } from '../hooks/useENSAddress'; +import { fetchENSAvatar, prefetchENSAvatar } from '../hooks/useENSAvatar'; +import { prefetchENSCover } from '../hooks/useENSCover'; +import { prefetchENSFirstTransactionTimestamp } from '../hooks/useENSFirstTransactionTimestamp'; +import { prefetchENSRecords } from '../hooks/useENSRecords'; +import { ENSActionParameters, RapActionTypes } from '../raps/common'; +import { + getENSData, + getNameFromLabelhash, + saveENSData, +} from './localstorage/ens'; import { estimateGasWithPadding, getProviderForNetwork } from './web3'; import { ENSRegistrationRecords, @@ -45,19 +55,18 @@ import { getOpenSeaCollectionUrl, handleAndSignImages, } from '@rainbow-me/parsers'; -import { queryClient } from '@rainbow-me/react-query/queryClient'; import { ENS_NFT_CONTRACT_ADDRESS, - ensPublicResolverAddress, + ensIntroMarqueeNames, ethUnits, } from '@rainbow-me/references'; import { labelhash, logger, profileUtils } from '@rainbow-me/utils'; import { AvatarResolver } from 'ens-avatar'; const DUMMY_RECORDS = { - 'cover': - 'https://cloudflare-ipfs.com/ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/I/m/Vincent_van_Gogh_-_Self-Portrait_-_Google_Art_Project_(454045).jpg', 'description': 'description', + 'header': + 'https://cloudflare-ipfs.com/ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/I/m/Vincent_van_Gogh_-_Self-Portrait_-_Google_Art_Project_(454045).jpg', 'me.rainbow.displayName': 'name', }; @@ -132,9 +141,9 @@ const buildEnsToken = ({ } as UniqueAsset; }; -export const isUnknownOpenSeaENS = (asset?: any) => { +export const isUnknownOpenSeaENS = (asset?: UniqueAsset) => { const isENS = - asset?.asset_contract?.address.toLowerCase() === + asset?.asset_contract?.address?.toLowerCase() === ENS_NFT_CONTRACT_ADDRESS.toLowerCase(); return ( isENS && @@ -154,15 +163,21 @@ export const fetchMetadata = async ({ tokenId: string; }) => { try { - const { data } = await ensClient.query({ - query: ENS_GET_NAME_FROM_LABELHASH, - variables: { - labelhash: BigNumber.from(tokenId).toHexString(), - }, - }); - const name = data.domains[0].labelName; + const labelhash = BigNumber.from(tokenId).toHexString(); + + let name = await getNameFromLabelhash(labelhash); + if (!name) { + const { data } = await ensClient.query({ + query: ENS_GET_NAME_FROM_LABELHASH, + variables: { + labelhash, + }, + }); + name = `${data.domains[0].labelName}.eth`; + } + const image_url = `https://metadata.ens.domains/mainnet/${contractAddress}/${tokenId}/image`; - return { image_url, name: `${name}.eth` }; + return { image_url, name }; } catch (error) { logger.sentry('ENS: Error getting ENS metadata', error); captureException(new Error('ENS: Error getting ENS metadata')); @@ -189,7 +204,7 @@ export const fetchEnsTokens = async ({ ).toString(), }, }); - return data.account.registrations.map(registration => { + return data?.account?.registrations?.map(registration => { const tokenId = BigNumber.from(registration.domain.labelhash).toString(); const token = buildEnsToken({ contractAddress, @@ -212,6 +227,42 @@ export const fetchSuggestions = async ( setIsFetching = (_unused: any) => {}, profilesEnabled = false ) => { + if (isValidAddress(recipient)) { + const address = getAddress(recipient); + const ens = await fetchReverseRecord(address); + if (!ens) { + setSuggestions([]); + setIsFetching(false); + return []; + } + let avatar; + try { + avatar = await fetchENSAvatar(ens, { + cacheFirst: true, + }); + prefetchENSAddress(ens, { cacheFirst: true }); + prefetchENSCover(ens, { cacheFirst: true }); + prefetchENSRecords(ens, { cacheFirst: true }); + prefetchENSFirstTransactionTimestamp(ens, { + cacheFirst: true, + }); + // eslint-disable-next-line no-empty + } catch (e) {} + const suggestion = [ + { + address: address, + color: profileUtils.addressHashedColorIndex(recipient), + ens: true, + image: avatar?.imageUrl, + network: 'mainnet', + nickname: ens, + uniqueId: address, + }, + ]; + setSuggestions(suggestion); + setIsFetching(false); + return suggestion; + } if (recipient.length > 2) { let suggestions: { address: any; @@ -239,24 +290,33 @@ export const fetchSuggestions = async ( !isZeroAddress(domain.owner.id) ) .map( - async (domain: { - name: string; - resolver: { texts: string[] }; - owner: { id: string }; - }) => { + async ( + domain: { + name: string; + resolver: { texts: string[] }; + owner: { id: string }; + }, + i: number + ) => { const hasAvatar = domain?.resolver?.texts?.find( text => text === ENS_RECORDS.avatar ); if (!!hasAvatar && profilesEnabled) { try { - const images = await fetchImages(domain.name); - queryClient.setQueryData( - ensProfileImagesQueryKey(domain.name), - images - ); + const avatar = await fetchENSAvatar(domain.name, { + cacheFirst: true, + }); + if (i === 0) { + prefetchENSAddress(domain.name, { cacheFirst: true }); + prefetchENSCover(domain.name, { cacheFirst: true }); + prefetchENSRecords(domain.name, { cacheFirst: true }); + prefetchENSFirstTransactionTimestamp(domain.name, { + cacheFirst: true, + }); + } return { ...domain, - avatar: images.avatarUrl, + avatar: avatar?.imageUrl, }; // eslint-disable-next-line no-empty } catch (e) {} @@ -326,36 +386,33 @@ export const fetchAccountRegistrations = async (address: string) => { return registrations; }; -export const fetchImages = async (ensName: string) => { - let avatarUrl; - let coverUrl; +export const fetchImage = async ( + imageType: 'avatar' | 'header', + ensName: string +) => { + let imageUrl; const provider = await getProviderForNetwork(); try { const avatarResolver = new AvatarResolver(provider); - [avatarUrl, coverUrl] = await Promise.all([ - avatarResolver.getImage(ensName, { - allowNonOwnerNFTs: true, - type: 'avatar', - }), - avatarResolver.getImage(ensName, { - allowNonOwnerNFTs: true, - type: 'cover', - }), - ]); - ImgixImage.preload([ - ...(avatarUrl ? [{ uri: avatarUrl }] : []), - ...(coverUrl ? [{ uri: coverUrl }] : []), - ]); - // eslint-disable-next-line no-empty - } catch (err) {} + imageUrl = await avatarResolver.getImage(ensName, { + allowNonOwnerNFTs: true, + type: imageType, + }); + ImgixImage.preload([...(imageUrl ? [{ uri: imageUrl }] : [])]); + saveENSData(imageType, ensName, { imageUrl }); + } catch (err) { + // Fallback to storage images + const data = await getENSData(imageType, ensName); + imageUrl = data?.imageUrl as string; + } - return { - avatarUrl, - coverUrl, - }; + return { imageUrl }; }; -export const fetchRecords = async (ensName: string) => { +export const fetchRecords = async ( + ensName: string, + { supportedOnly = true }: { supportedOnly?: boolean } = {} +) => { const response = await ensClient.query({ query: ENS_GET_RECORDS, variables: { @@ -369,7 +426,7 @@ export const fetchRecords = async (ensName: string) => { const supportedRecords = Object.values(ENS_RECORDS); const rawRecordKeys: string[] = data.resolver?.texts || []; const recordKeys = (rawRecordKeys as ENS_RECORDS[]).filter(key => - supportedRecords.includes(key) + supportedOnly ? supportedRecords.includes(key) : true ); const recordValues = await Promise.all( recordKeys.map((key: string) => resolver?.getText(key)) @@ -385,7 +442,8 @@ export const fetchRecords = async (ensName: string) => { }; export const fetchCoinAddresses = async ( - ensName: string + ensName: string, + { supportedOnly = true }: { supportedOnly?: boolean } = {} ): Promise<{ [key in ENS_RECORDS]: string }> => { const response = await ensClient.query({ query: ENS_GET_COIN_TYPES, @@ -403,7 +461,7 @@ export const fetchCoinAddresses = async ( ); const coinTypes: number[] = (rawCoinTypesNames as ENS_RECORDS[]) - .filter(name => supportedRecords.includes(name)) + .filter(name => (supportedOnly ? supportedRecords.includes(name) : true)) .map(name => formatsByName[name].coinType) || []; const coinAddressValues = await Promise.all( @@ -489,55 +547,15 @@ export const fetchAccountPrimary = async (accountAddress: string) => { }; }; -export const fetchProfile = async (ensName: string) => { - const [ - resolver, - records, - coinAddresses, - images, - owner, - { registrant, registration }, - primary, - ] = await Promise.all([ - fetchResolver(ensName), - fetchRecords(ensName), - fetchCoinAddresses(ensName), - fetchImages(ensName), - fetchOwner(ensName), - fetchRegistration(ensName), - fetchPrimary(ensName), - ]); - - const resolverData = { - address: resolver?.address, - type: resolver?.address === ensPublicResolverAddress ? 'default' : 'custom', - }; - - return { - coinAddresses, - images, - owner, - primary, - records, - registrant, - registration, - resolver: resolverData, - }; -}; - -export const fetchProfileRecords = async (ensName: string) => { - const [records, coinAddresses, images] = await Promise.all([ - fetchRecords(ensName), - fetchCoinAddresses(ensName), - fetchImages(ensName), - ]); - - return { - coinAddresses, - images, - records, - }; -}; +export function prefetchENSIntroData() { + for (const name of ensIntroMarqueeNames) { + prefetchENSAddress(name, { cacheFirst: true }); + prefetchENSAvatar(name, { cacheFirst: true }); + prefetchENSCover(name, { cacheFirst: true }); + prefetchENSRecords(name, { cacheFirst: true }); + prefetchENSFirstTransactionTimestamp(name, { cacheFirst: true }); + } +} export const estimateENSCommitGasLimit = async ({ name, @@ -622,6 +640,38 @@ export const estimateENSSetNameGasLimit = async ({ type: ENSRegistrationTransactionType.SET_NAME, }); +export const estimateENSReclaimGasLimit = async ({ + name, + ownerAddress, + toAddress, +}: { + name: string; + ownerAddress: string; + toAddress: string; +}) => + estimateENSTransactionGasLimit({ + name, + ownerAddress, + toAddress, + type: ENSRegistrationTransactionType.RECLAIM, + }); + +export const estimateENSSetAddressGasLimit = async ({ + name, + ownerAddress, + records, +}: { + name: string; + ownerAddress?: string; + records: ENSRegistrationRecords; +}) => + estimateENSTransactionGasLimit({ + name, + ownerAddress, + records, + type: ENSRegistrationTransactionType.SET_ADDR, + }); + export const estimateENSSetTextGasLimit = async ({ name, records, @@ -642,6 +692,7 @@ export const estimateENSTransactionGasLimit = async ({ name, type, ownerAddress, + toAddress, rentPrice, duration, records, @@ -650,6 +701,7 @@ export const estimateENSTransactionGasLimit = async ({ name?: string; type: ENSRegistrationTransactionType; ownerAddress?: string; + toAddress?: string; rentPrice?: string; duration?: number; salt?: string; @@ -662,6 +714,7 @@ export const estimateENSTransactionGasLimit = async ({ records, rentPrice, salt, + toAddress, type, }); @@ -764,6 +817,7 @@ export const estimateENSRegisterSetRecordsAndNameGasLimit = async ({ promises.push( estimateENSSetRecordsGasLimit({ name, + ownerAddress, records, }) ); @@ -779,22 +833,62 @@ export const estimateENSSetRecordsGasLimit = async ({ name, records, ownerAddress, + setReverseRecord, }: - | { name: string; records: Records; ownerAddress?: string } + | { + name: string; + records: Records; + ownerAddress?: string; + setReverseRecord?: boolean; + } | ENSActionParameters) => { - let gasLimit: string | null = '0'; + const promises = []; const ensRegistrationRecords = formatRecordsForTransaction(records); const validRecords = recordsForTransactionAreValid(ensRegistrationRecords); if (validRecords) { - const shouldUseMulticall = shouldUseMulticallTransaction( - ensRegistrationRecords + const txType = getTransactionTypeForRecords(ensRegistrationRecords); + switch (txType) { + case ENSRegistrationTransactionType.MULTICALL: + promises.push( + estimateENSMulticallGasLimit({ + name, + ownerAddress, + records: ensRegistrationRecords, + }) + ); + break; + case ENSRegistrationTransactionType.SET_ADDR: + promises.push( + estimateENSSetAddressGasLimit({ + name, + ownerAddress, + records: ensRegistrationRecords, + }) + ); + break; + case ENSRegistrationTransactionType.SET_TEXT: + promises.push( + estimateENSSetTextGasLimit({ + name, + ownerAddress, + records: ensRegistrationRecords, + }) + ); + break; + default: + } + } + if (setReverseRecord && ownerAddress) { + promises.push( + estimateENSSetNameGasLimit({ + name, + ownerAddress, + }) ); - gasLimit = await (shouldUseMulticall - ? estimateENSMulticallGasLimit - : estimateENSSetTextGasLimit)({ - ...{ name, ownerAddress, records: ensRegistrationRecords }, - }); } + const gasLimits = await Promise.all(promises); + const gasLimit = gasLimits.reduce((a, b) => add(a || 0, b || 0)); + if (!gasLimit) return '0'; return gasLimit; }; @@ -808,7 +902,7 @@ export const formatRecordsForTransaction = ( records && Object.entries(records).forEach(([key, value]) => { switch (key) { - case ENS_RECORDS.cover: + case ENS_RECORDS.header: case ENS_RECORDS.twitter: case ENS_RECORDS.displayName: case ENS_RECORDS.email: @@ -869,7 +963,7 @@ export const recordsForTransactionAreValid = ( return true; }; -export const shouldUseMulticallTransaction = ( +export const getTransactionTypeForRecords = ( registrationRecords: ENSRegistrationRecords ) => { const { @@ -878,21 +972,42 @@ export const shouldUseMulticallTransaction = ( ensAssociatedAddress, text, } = registrationRecords; + if ( - !coinAddress?.length && - !contentHash && - !ensAssociatedAddress && - text?.length === 1 + contentHash || + ensAssociatedAddress || + (text?.length || 0) + (coinAddress?.length || 0) > 1 ) { - return false; + return ENSRegistrationTransactionType.MULTICALL; + } else if (text?.length) { + return ENSRegistrationTransactionType.SET_TEXT; + } else if (coinAddress?.length) { + return ENSRegistrationTransactionType.SET_ADDR; + } else { + return null; + } +}; + +export const getRapActionTypeForTxType = ( + txType: ENSRegistrationTransactionType +) => { + switch (txType) { + case ENSRegistrationTransactionType.MULTICALL: + return RapActionTypes.multicallENS; + case ENSRegistrationTransactionType.SET_ADDR: + return RapActionTypes.setAddrENS; + case ENSRegistrationTransactionType.SET_TEXT: + return RapActionTypes.setTextENS; + default: + return null; } - return true; }; export const fetchReverseRecord = async (address: string) => { try { + const checksumAddress = getAddress(address); const provider = await getProviderForNetwork(); - const reverseRecord = await provider.lookupAddress(address); + const reverseRecord = await provider.lookupAddress(checksumAddress); return reverseRecord ?? ''; } catch (e) { return ''; diff --git a/src/handlers/localstorage/ens.ts b/src/handlers/localstorage/ens.ts index 81b7bf3ab9e..9b8dd8dd47b 100644 --- a/src/handlers/localstorage/ens.ts +++ b/src/handlers/localstorage/ens.ts @@ -1,59 +1,63 @@ import { getGlobal, saveGlobal } from './common'; -const ensProfileVersion = '0.1.0'; +export type ENSDataType = + | 'avatar' + | 'header' + | 'registrant' + | 'owner' + | 'address' + | 'records' + | 'resolver' + | 'firstTxTimestamp'; -const ensProfileKey = (key: string) => `ensProfile.${key}`; -const ensProfileImagesKey = (key: string) => `ensProfileImages.${key}`; -const ensProfileRecordsKey = (key: string) => `ensProfileRecords.${key}`; -const ensResolveNameKey = (key: string) => `ensResolveName.${key}`; +const ensProfileVersion = '0.2.0'; + +const ensLabelhashesKey = (key: string) => `ensLabelhashes.${key}`; +const ensDataKey = (dataType: ENSDataType, key: string) => + `ens.${dataType}.${key}`; +const ensProfileKey = (key: string) => `ens.profile.${key}`; const ensDomains = (key: string) => `ensDomains.${key}`; const ensSeenOnchainDataDisclaimerKey = 'ensProfile.seenOnchainDisclaimer'; -export const getProfile = async (key: string) => { - const profile = await getGlobal(ensProfileKey(key), null, ensProfileVersion); - return profile ? JSON.parse(profile) : null; -}; - -export const saveProfile = (key: string, value: Object) => - saveGlobal(ensProfileKey(key), JSON.stringify(value), ensProfileVersion); - -export const getProfileImages = async (key: string) => { - const images = await getGlobal( - ensProfileImagesKey(key), +export const getNameFromLabelhash = async (key: string) => { + const labelhash = await getGlobal( + ensLabelhashesKey(key), null, ensProfileVersion ); - return images ? JSON.parse(images) : null; + return labelhash; }; -export const saveProfileImages = (key: string, value: Object) => - saveGlobal( - ensProfileImagesKey(key), - JSON.stringify(value), - ensProfileVersion - ); +export const saveNameFromLabelhash = (key: string, value: Object) => + saveGlobal(ensLabelhashesKey(key), value, ensProfileVersion); -export const getProfileRecords = async (key: string) => { - const records = await getGlobal( - ensProfileRecordsKey(key), +export const getENSData = async (dataType: ENSDataType, key: string) => { + const profile = await getGlobal( + ensDataKey(dataType, key), null, ensProfileVersion ); - return records ? JSON.parse(records) : null; + return profile ? JSON.parse(profile) : null; }; -export const saveProfileRecords = (key: string, value: Object) => +export const saveENSData = ( + dataType: ENSDataType, + key: string, + value: Object +) => saveGlobal( - ensProfileRecordsKey(key), + ensDataKey(dataType, key), JSON.stringify(value), ensProfileVersion ); -export const getResolveName = (key: string) => - getGlobal(ensResolveNameKey(key), null); +export const getENSProfile = async (key: string) => { + const profile = await getGlobal(ensProfileKey(key), null, ensProfileVersion); + return profile ? JSON.parse(profile) : null; +}; -export const saveResolveName = (key: string, value: string) => - saveGlobal(ensResolveNameKey(key), value); +export const saveENSProfile = (key: string, value: Object) => + saveGlobal(ensProfileKey(key), JSON.stringify(value), ensProfileVersion); export const getSeenOnchainDataDisclaimer = () => getGlobal(ensSeenOnchainDataDisclaimerKey, false); @@ -61,13 +65,14 @@ export const getSeenOnchainDataDisclaimer = () => export const saveSeenOnchainDataDisclaimer = (value: boolean) => saveGlobal(ensSeenOnchainDataDisclaimerKey, value); -export const getENSDomains = (key: string) => getGlobal(ensDomains(key), []); +export const getENSDomains = (key: string) => + getGlobal(ensDomains(key), null, ensProfileVersion); export const setENSDomains = ( key: string, value: { name: string; owner: { id: string }; - images: { avatarUrl?: string | null; coverUrl?: string | null }; + labelhash: string; }[] ) => saveGlobal(ensDomains(key), value); diff --git a/src/handlers/opensea-api.ts b/src/handlers/opensea-api.ts index b1a1e464e19..eb904ec34be 100644 --- a/src/handlers/opensea-api.ts +++ b/src/handlers/opensea-api.ts @@ -1,4 +1,5 @@ import { captureException } from '@sentry/react-native'; +import PQueue from 'p-queue/dist'; import { // @ts-ignore NFT_API_KEY, @@ -17,6 +18,9 @@ import logger from 'logger'; export const UNIQUE_TOKENS_LIMIT_PER_PAGE: number = 50; export const UNIQUE_TOKENS_LIMIT_TOTAL: number = 2000; +// limiting our opensea api requests to 10/sec so we don't max out +const queue = new PQueue({ interval: 1000, intervalCap: 10 }); + export const apiGetAccountUniqueTokens = async ( network: Network, address: string, @@ -28,28 +32,31 @@ export const apiGetAccountUniqueTokens = async ( const offset = page * UNIQUE_TOKENS_LIMIT_PER_PAGE; const url = `https://${networkPrefix}${NFT_API_URL}/api/v1/assets`; const urlV2 = `https://${NFT_API_URL}/api/v2/beta/assets`; - const data = await rainbowFetch(isPolygon ? urlV2 : url, { - headers: { - 'Accept': 'application/json', - 'X-Api-Key': NFT_API_KEY, - }, - method: 'get', - params: { - // @ts-expect-error ts-migrate(2322) FIXME: Type '{ limit: number; offset: number; owner: any;... Remove this comment to see the full error message - limit: UNIQUE_TOKENS_LIMIT_PER_PAGE, - // @ts-expect-error ts-migrate(2322) FIXME: Type '{ limit: number; offset: number; owner: any;... Remove this comment to see the full error message - offset: offset, - ...(isPolygon - ? { - chain_identifier: 'matic', - owner_address: address, - } - : { - owner: address, - }), - }, - timeout: 10000, // 10 secs - }); + const data = await queue.add( + async () => + await rainbowFetch(isPolygon ? urlV2 : url, { + headers: { + 'Accept': 'application/json', + 'X-Api-Key': NFT_API_KEY, + }, + method: 'get', + params: { + // @ts-expect-error ts-migrate(2322) FIXME: Type '{ limit: number; offset: number; owner: any;... Remove this comment to see the full error message + limit: UNIQUE_TOKENS_LIMIT_PER_PAGE, + // @ts-expect-error ts-migrate(2322) FIXME: Type '{ limit: number; offset: number; owner: any;... Remove this comment to see the full error message + offset: offset, + ...(isPolygon + ? { + chain_identifier: 'matic', + owner_address: address, + } + : { + owner: address, + }), + }, + timeout: 10000, // 10 secs + }) + ); return isPolygon ? parseAccountUniqueTokensPolygon(data) : parseAccountUniqueTokens(data); @@ -83,14 +90,17 @@ export const apiGetAccountUniqueToken = async ( const urlV2 = `https://${NFT_API_URL}/api/v2/beta/asset/${contractAddress}/${tokenId}${ forceUpdate ? '?force_update=true' : '' }`; - const { data } = await rainbowFetch(isPolygon ? urlV2 : url, { - headers: { - 'Accept': 'application/json', - 'X-Api-Key': NFT_API_KEY, - }, - method: 'get', - timeout: 10000, // 10 secs - }); + const { data } = await queue.add( + async () => + await rainbowFetch(isPolygon ? urlV2 : url, { + headers: { + 'Accept': 'application/json', + 'X-Api-Key': NFT_API_KEY, + }, + method: 'get', + timeout: 10000, // 10 secs + }) + ); return isPolygon ? parseAccountUniqueTokensPolygon({ data: { results: [data] }, @@ -103,39 +113,6 @@ export const apiGetAccountUniqueToken = async ( } }; -export const apiGetUniqueTokenImage = async ( - contractAddress: string, - tokenId: string -) => { - try { - const url = `https://${NFT_API_URL}/api/v1/asset/${contractAddress}/${tokenId}`; - const data = await rainbowFetch(url, { - headers: { - 'Accept': 'application/json', - 'X-Api-Key': NFT_API_KEY, - }, - method: 'get', - timeout: 10000, // 10 secs - }); - const { - image_url, - image_thumbnail_url, - image_original_url, - image_preview_url, - } = data?.data; - return { - image_original_url, - image_preview_url, - image_thumbnail_url, - image_url, - }; - } catch (error) { - logger.sentry('Error getting unique token image', error); - captureException(new Error('Opensea: Error getting unique token image')); - throw error; - } -}; - export const apiGetUniqueTokenFloorPrice = async ( network: any, urlSuffixForAsset: any @@ -143,26 +120,32 @@ export const apiGetUniqueTokenFloorPrice = async ( try { const networkPrefix = network === NetworkTypes.mainnet ? '' : `${network}-`; const url = `https://${networkPrefix}${NFT_API_URL}/api/v1/asset/${urlSuffixForAsset}`; - const data = await rainbowFetch(url, { - headers: { - 'Accept': 'application/json', - 'X-Api-Key': NFT_API_KEY, // 5 secs - }, - method: 'get', - timeout: 5000, - }); + const data = await queue.add( + async () => + await rainbowFetch(url, { + headers: { + 'Accept': 'application/json', + 'X-Api-Key': NFT_API_KEY, // 5 secs + }, + method: 'get', + timeout: 5000, + }) + ); const slug = data?.data?.collection?.slug; const collectionURL = `https://${networkPrefix}${NFT_API_URL}/api/v1/collection/${slug}`; - const collectionData = await rainbowFetch(collectionURL, { - headers: { - 'Accept': 'application/json', - 'X-Api-Key': NFT_API_KEY, - }, - method: 'get', - timeout: 5000, // 5 secs - }); + const collectionData = await queue.add( + async () => + await rainbowFetch(collectionURL, { + headers: { + 'Accept': 'application/json', + 'X-Api-Key': NFT_API_KEY, + }, + method: 'get', + timeout: 5000, // 5 secs + }) + ); const tempPrice = collectionData?.data?.collection?.stats?.floor_price; diff --git a/src/handlers/web3.ts b/src/handlers/web3.ts index 8aa0b34b366..e464594e235 100644 --- a/src/handlers/web3.ts +++ b/src/handlers/web3.ts @@ -553,6 +553,7 @@ export const getTransferNftTransaction = async ( | 'gasPrice' | 'gasLimit' | 'network' + | 'nonce' | 'maxFeePerGas' | 'maxPriorityFeePerGas' > @@ -563,7 +564,7 @@ export const getTransferNftTransaction = async ( throw new Error(`Invalid recipient "${transaction.to}"`); } - const { from } = transaction; + const { from, nonce } = transaction; const contractAddress = transaction.asset.asset_contract?.address; const data = await getDataForNftTransfer(from, recipient, transaction.asset); const gasParams = getTransactionGasParams(transaction); @@ -572,6 +573,7 @@ export const getTransferNftTransaction = async ( from, gasLimit: transaction.gasLimit?.toString(), network: transaction.network, + nonce, to: contractAddress, ...gasParams, }; diff --git a/src/helpers/accountInfo.ts b/src/helpers/accountInfo.ts index 2d19a4ba337..9a1434c7b58 100644 --- a/src/helpers/accountInfo.ts +++ b/src/helpers/accountInfo.ts @@ -8,7 +8,6 @@ import { addressHashedEmoji, isValidImagePath } from '../utils/profileUtils'; export function getAccountProfileInfo( selectedWallet: any, walletNames: any, - network: any, accountAddress: any ) { if (!selectedWallet) { diff --git a/src/helpers/assets.ts b/src/helpers/assets.ts index deada81b16a..8a61176035e 100644 --- a/src/helpers/assets.ts +++ b/src/helpers/assets.ts @@ -1,13 +1,18 @@ import lang from 'i18n-js'; import { chunk, compact, groupBy, isEmpty, slice, sortBy } from 'lodash'; import { add, convertAmountToNativeDisplay, greaterThan } from './utilities'; +import { AssetListType } from '@/components/asset-list/RecyclerAssetList2'; import store from '@rainbow-me/redux/store'; import { ETH_ADDRESS, ETH_ICON_URL, supportedNativeCurrencies, } from '@rainbow-me/references'; -import { ethereumUtils } from '@rainbow-me/utils'; +import { + ethereumUtils, + getUniqueTokenFormat, + getUniqueTokenType, +} from '@rainbow-me/utils'; const COINS_TO_SHOW = 5; @@ -320,7 +325,8 @@ export const buildBriefUniqueTokenList = ( uniqueTokens: any, selectedShowcaseTokens: any, sellingTokens: any[] = [], - hiddenTokens: string[] = [] + hiddenTokens: string[] = [], + listType: AssetListType = 'wallet' ) => { const hiddenUniqueTokensIds = uniqueTokens .filter(({ fullUniqueId }: any) => hiddenTokens.includes(fullUniqueId)) @@ -331,7 +337,16 @@ export const buildBriefUniqueTokenList = ( const uniqueTokensInShowcaseIds = nonHiddenUniqueTokens .filter(({ uniqueId }: any) => selectedShowcaseTokens?.includes(uniqueId)) .map(({ uniqueId }: any) => uniqueId); - const grouped2 = groupBy(nonHiddenUniqueTokens, token => token.familyName); + + const filteredUniqueTokens = nonHiddenUniqueTokens.filter((token: any) => { + if (listType === 'select-nft') { + const format = getUniqueTokenFormat(token); + const type = getUniqueTokenType(token); + return format === 'image' && type === 'NFT'; + } + return true; + }); + const grouped2 = groupBy(filteredUniqueTokens, token => token.familyName); const families2 = sortBy(Object.keys(grouped2), row => row.replace(regex, '').toLowerCase() ); @@ -340,7 +355,7 @@ export const buildBriefUniqueTokenList = ( { type: 'NFTS_HEADER', uid: 'nfts-header' }, { type: 'NFTS_HEADER_SPACE_AFTER', uid: 'nfts-header-space-after' }, ]; - if (uniqueTokensInShowcaseIds.length > 0) { + if (uniqueTokensInShowcaseIds.length > 0 && listType !== 'select-nft') { result.push({ // @ts-expect-error "name" does not exist in type. name: 'Showcase', @@ -400,7 +415,7 @@ export const buildBriefUniqueTokenList = ( result.push({ type: 'NFT_SPACE_AFTER', uid: `${family}-space-after` }); } - if (hiddenUniqueTokensIds.length > 0) { + if (hiddenUniqueTokensIds.length > 0 && listType === 'wallet') { result.push({ // @ts-expect-error "name" does not exist in type. name: lang.t('button.hidden'), @@ -424,5 +439,13 @@ export const buildBriefUniqueTokenList = ( return result; }; -export const buildUniqueTokenName = ({ collection, id, name }: any) => - name || `${collection?.name} #${id}`; +export const buildUniqueTokenName = ({ + collection, + id, + name, + uniqueId, +}: any) => { + if (name) return name; + if (id) return `${collection?.name} #${id}`; + return uniqueId; +}; diff --git a/src/helpers/buildWalletSections.tsx b/src/helpers/buildWalletSections.tsx index b92a03bc37a..7a0d554ad69 100644 --- a/src/helpers/buildWalletSections.tsx +++ b/src/helpers/buildWalletSections.tsx @@ -47,11 +47,13 @@ const networkSelector = (state: any) => state.network; const nativeCurrencySelector = (state: any) => state.nativeCurrency; const pinnedCoinsSelector = (state: any) => state.pinnedCoins; const savingsSelector = (state: any) => state.savings; +const sellingTokensSelector = (state: any) => state.sellingTokens; const showcaseTokensSelector = (state: any) => state.showcaseTokens; const hiddenTokensSelector = (state: any) => state.hiddenTokens; const uniqueTokensSelector = (state: any) => state.uniqueTokens; const uniswapSelector = (state: any) => state.uniswap; const uniswapTotalSelector = (state: any) => state.uniswapTotal; +const listTypeSelector = (state: any) => state.listType; const enhanceRenderItem = compose( withNavigation, @@ -487,8 +489,9 @@ const briefUniqueTokenDataSelector = createSelector( [ uniqueTokensSelector, showcaseTokensSelector, - () => [], + sellingTokensSelector, hiddenTokensSelector, + listTypeSelector, ], buildBriefUniqueTokenList ); diff --git a/src/helpers/ens.ts b/src/helpers/ens.ts index eb15c1d06d9..753a98b3610 100644 --- a/src/helpers/ens.ts +++ b/src/helpers/ens.ts @@ -1,7 +1,7 @@ import { formatsByName } from '@ensdomains/address-encoder'; import { hash } from '@ensdomains/eth-ens-namehash'; import { Wallet } from '@ethersproject/wallet'; -import { BigNumberish, Contract } from 'ethers'; +import { BigNumber, BigNumberish, Contract } from 'ethers'; import lang from 'i18n-js'; import { atom } from 'recoil'; import { InlineFieldProps } from '../components/inputs/InlineField'; @@ -37,11 +37,18 @@ import { } from '@rainbow-me/utils/contenthash'; export const ENS_SECONDS_WAIT = 60; +export const ENS_SECONDS_PADDING = 5; +export const ENS_SECONDS_WAIT_WITH_PADDING = + ENS_SECONDS_WAIT + ENS_SECONDS_PADDING; +export const ENS_SECONDS_WAIT_PROVIDER_PADDING = + ENS_SECONDS_WAIT + 4 * ENS_SECONDS_PADDING; export enum ENSRegistrationTransactionType { COMMIT = 'commit', REGISTER_WITH_CONFIG = 'registerWithConfig', RENEW = 'renew', + SET_ADDR = 'setAddr', + RECLAIM = 'reclaim', SET_TEXT = 'setText', SET_NAME = 'setName', MULTICALL = 'multicall', @@ -53,7 +60,7 @@ export enum ENS_RECORDS { LTC = 'LTC', DOGE = 'DOGE', displayName = 'me.rainbow.displayName', - cover = 'cover', + header = 'header', content = 'content', url = 'url', email = 'email', @@ -79,6 +86,7 @@ export enum REGISTRATION_STEPS { REGISTER = 'REGISTER', RENEW = 'RENEW', SET_NAME = 'SET_NAME', + TRANSFER = 'TRANSFER', WAIT_COMMIT_CONFIRMATION = 'WAIT_COMMIT_CONFIRMATION', WAIT_ENS_COMMITMENT = 'WAIT_ENS_COMMITMENT', } @@ -87,6 +95,7 @@ export enum REGISTRATION_MODES { CREATE = 'CREATE', EDIT = 'EDIT', RENEW = 'RENEW', + SEARCH = 'SEARCH', SET_NAME = 'SET_NAME', } @@ -96,17 +105,9 @@ export type TextRecordField = { label: InlineFieldProps['label']; placeholder: InlineFieldProps['placeholder']; inputProps?: InlineFieldProps['inputProps']; - validations?: InlineFieldProps['validations'] & { - onSubmit?: { - match?: { - value: RegExp; - message: string; - }; - validate?: { - callback: (value: string) => boolean; - message: string; - }; - }; + validation?: { + validator: (value: string) => boolean; + message: string; }; startsWith?: string; }; @@ -140,13 +141,12 @@ export const textRecordFields = { key: ENS_RECORDS.url, label: lang.t('profiles.create.website'), placeholder: lang.t('profiles.create.website_placeholder'), - validations: { - onSubmit: { - match: { - message: lang.t('profiles.create.website_submit_message'), - value: /[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/, - }, - }, + validation: { + message: lang.t('profiles.create.invalid_website'), + validator: value => + /[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/.test( + value + ), }, }, [ENS_RECORDS.twitter]: { @@ -158,27 +158,25 @@ export const textRecordFields = { label: lang.t('profiles.create.twitter'), placeholder: lang.t('profiles.create.username_placeholder'), startsWith: '@', - validations: { - onChange: { - match: /^\w*$/, - }, + validation: { + message: lang.t('profiles.create.invalid_username', { + app: lang.t('profiles.create.twitter'), + }), + validator: value => /^\w*$/.test(value), }, }, [ENS_RECORDS.email]: { id: 'email', inputProps: { + keyboardType: 'email-address', maxLength: 50, }, key: ENS_RECORDS.email, label: lang.t('profiles.create.email'), placeholder: lang.t('profiles.create.email_placeholder'), - validations: { - onSubmit: { - match: { - message: lang.t('profiles.create.email_submit_message'), - value: /^\S+@\S+\.\S+$/, - }, - }, + validation: { + message: lang.t('profiles.create.invalid_email'), + validator: value => /^\S+@\S+\.\S+$/.test(value), }, }, [ENS_RECORDS.instagram]: { @@ -190,10 +188,11 @@ export const textRecordFields = { label: lang.t('profiles.create.instagram'), placeholder: lang.t('profiles.create.username_placeholder'), startsWith: '@', - validations: { - onChange: { - match: /^([\w.])*$/, - }, + validation: { + message: lang.t('profiles.create.invalid_username', { + app: lang.t('profiles.create.instagram'), + }), + validator: value => /^([\w.])*$/.test(value), }, }, [ENS_RECORDS.discord]: { @@ -205,6 +204,12 @@ export const textRecordFields = { label: lang.t('profiles.create.discord'), placeholder: lang.t('profiles.create.username_placeholder'), startsWith: '@', + validation: { + message: lang.t('profiles.create.invalid_username', { + app: lang.t('profiles.create.discord'), + }), + validator: value => /^([\w#.])*$/.test(value), + }, }, [ENS_RECORDS.github]: { id: 'github', @@ -214,6 +219,13 @@ export const textRecordFields = { key: ENS_RECORDS.github, label: lang.t('profiles.create.github'), placeholder: lang.t('profiles.create.username_placeholder'), + startsWith: '@', + validation: { + message: lang.t('profiles.create.invalid_username', { + app: lang.t('profiles.create.github'), + }), + validator: value => /^([\w.])*$/.test(value), + }, }, [ENS_RECORDS.BTC]: { id: 'btc', @@ -226,15 +238,11 @@ export const textRecordFields = { placeholder: lang.t('profiles.create.wallet_placeholder', { coin: lang.t('profiles.create.btc'), }), - validations: { - onSubmit: { - validate: { - callback: value => validateCoinRecordValue(value, ENS_RECORDS.BTC), - message: lang.t('profiles.create.invalid_asset', { - coin: ENS_RECORDS.BTC, - }), - }, - }, + validation: { + message: lang.t('profiles.create.invalid_asset', { + coin: ENS_RECORDS.BTC, + }), + validator: value => validateCoinRecordValue(value, ENS_RECORDS.BTC), }, }, [ENS_RECORDS.snapchat]: { @@ -246,10 +254,11 @@ export const textRecordFields = { label: lang.t('profiles.create.snapchat'), placeholder: lang.t('profiles.create.username_placeholder'), startsWith: '@', - validations: { - onChange: { - match: /^([\w.])*$/, - }, + validation: { + message: lang.t('profiles.create.invalid_username', { + app: lang.t('profiles.create.snapchat'), + }), + validator: value => /^([\w.])*$/.test(value), }, }, [ENS_RECORDS.telegram]: { @@ -261,6 +270,12 @@ export const textRecordFields = { label: lang.t('profiles.create.telegram'), placeholder: lang.t('profiles.create.username_placeholder'), startsWith: '@', + validation: { + message: lang.t('profiles.create.invalid_username', { + app: lang.t('profiles.create.telegram'), + }), + validator: value => /^([\w#.])*$/.test(value), + }, }, [ENS_RECORDS.reddit]: { id: 'reddit', @@ -271,6 +286,12 @@ export const textRecordFields = { label: lang.t('profiles.create.reddit'), placeholder: lang.t('profiles.create.username_placeholder'), startsWith: '@', + validation: { + message: lang.t('profiles.create.invalid_username', { + app: lang.t('profiles.create.reddit'), + }), + validator: value => /^([\w#.])*$/.test(value), + }, }, [ENS_RECORDS.pronouns]: { id: 'pronouns', @@ -309,15 +330,11 @@ export const textRecordFields = { placeholder: lang.t('profiles.create.wallet_placeholder', { coin: lang.t('profiles.create.ltc'), }), - validations: { - onSubmit: { - validate: { - callback: value => validateCoinRecordValue(value, ENS_RECORDS.LTC), - message: lang.t('profiles.create.invalid_asset', { - coin: ENS_RECORDS.LTC, - }), - }, - }, + validation: { + message: lang.t('profiles.create.invalid_asset', { + coin: ENS_RECORDS.LTC, + }), + validator: value => validateCoinRecordValue(value, ENS_RECORDS.LTC), }, }, [ENS_RECORDS.DOGE]: { @@ -330,15 +347,11 @@ export const textRecordFields = { placeholder: lang.t('profiles.create.wallet_placeholder', { coin: lang.t('profiles.create.doge'), }), - validations: { - onSubmit: { - validate: { - callback: value => validateCoinRecordValue(value, ENS_RECORDS.DOGE), - message: lang.t('profiles.create.invalid_asset', { - coin: ENS_RECORDS.DOGE, - }), - }, - }, + validation: { + message: lang.t('profiles.create.invalid_asset', { + coin: ENS_RECORDS.DOGE, + }), + validator: value => validateCoinRecordValue(value, ENS_RECORDS.DOGE), }, }, [ENS_RECORDS.content]: { @@ -347,13 +360,9 @@ export const textRecordFields = { key: ENS_RECORDS.content, label: lang.t('profiles.create.content'), placeholder: lang.t('profiles.create.content_placeholder'), - validations: { - onSubmit: { - validate: { - callback: value => validateContentHashRecordValue(value), - message: lang.t('profiles.create.invalid_content_hash'), - }, - }, + validation: { + message: lang.t('profiles.create.invalid_content_hash'), + validator: value => validateContentHashRecordValue(value), }, }, } as { @@ -404,14 +413,21 @@ const getENSBaseRegistrarImplementationContract = async (wallet?: Wallet) => { ); }; -const getENSRegistryContract = async () => { - const provider = await getProviderForNetwork(); - return new Contract(ensRegistryAddress, ENSRegistryWithFallbackABI, provider); +const getENSRegistryContract = async (wallet?: Wallet) => { + const signerOrProvider = wallet ?? (await getProviderForNetwork()); + return new Contract( + ensRegistryAddress, + ENSRegistryWithFallbackABI, + signerOrProvider + ); }; -const getAvailable = async (name: string): Promise => { - const contract = await getENSRegistrarControllerContract(); - return contract.available(name); +const getAvailable = async ( + name: string, + contract?: Contract +): Promise => { + const ensContract = contract ?? (await getENSRegistrarControllerContract()); + return ensContract.available(name); }; const getNameExpires = async (name: string): Promise => { @@ -424,9 +440,13 @@ const getNameOwner = async (name: string): Promise => { return contract.owner(hash(name)); }; -const getRentPrice = async (name: string, duration: number): Promise => { - const contract = await getENSRegistrarControllerContract(); - return contract.rentPrice(name, duration); +const getRentPrice = async ( + name: string, + duration: number, + contract?: Contract +): Promise => { + const ensContract = contract ?? (await getENSRegistrarControllerContract()); + return ensContract.rentPrice(name, duration); }; const setupMulticallRecords = ( @@ -492,7 +512,9 @@ const setupMulticallRecords = ( if (textAssociatedRecord) { data.push( textAssociatedRecord - .filter(textRecord => Boolean(textRecord.value)) + .filter( + textRecord => Boolean(textRecord.value) || textRecord.value === '' + ) .map(textRecord => { return resolver.encodeFunctionData('setText', [ namehash, @@ -522,6 +544,7 @@ const getENSExecutionDetails = async ({ type, ownerAddress, salt, + toAddress, rentPrice, duration, records, @@ -534,6 +557,7 @@ const getENSExecutionDetails = async ({ rentPrice?: string; duration?: number; records?: ENSRegistrationRecords; + toAddress?: string; wallet?: Wallet; salt?: string; resolverAddress?: EthereumAddress; @@ -598,6 +622,25 @@ const getENSExecutionDetails = async ({ args = [name]; contract = await getENSReverseRegistrarContract(wallet); break; + case ENSRegistrationTransactionType.SET_ADDR: { + if (!name || !records || !records?.coinAddress?.[0]) + throw new Error('Bad arguments for setAddr'); + const record = records?.coinAddress[0]; + const namehash = hash(name); + const coinType = formatsByName[record.key].coinType; + args = [namehash, coinType, record.address]; + contract = await getENSPublicResolverContract(wallet, resolverAddress); + break; + } + case ENSRegistrationTransactionType.RECLAIM: { + if (!name || !toAddress) throw new Error('Bad arguments for reclaim'); + const id = BigNumber.from( + labelhash(name.replace(ENS_DOMAIN, '')) + ).toString(); + args = [id, toAddress]; + contract = await getENSBaseRegistrarImplementationContract(wallet); + break; + } case ENSRegistrationTransactionType.SET_TEXT: { if (!name || !records || !records?.text?.[0]) throw new Error('Bad arguments for setText'); diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 14511a9452c..ab48e6991c0 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -38,9 +38,26 @@ export { default as useDeleteWallet } from './useDeleteWallet'; export { default as useDPI } from './useDPI'; export { default as useEffectDebugger } from './useEffectDebugger'; export { default as useEmailRainbow } from './useEmailRainbow'; +export { default as useENSLocalTransactions } from './useENSLocalTransactions'; export { default as useENSPendingRegistrations } from './useENSPendingRegistrations'; -export { default as useENSProfile } from './useENSProfile'; -export { default as useENSProfileImages } from './useENSProfileImages'; +export { default as useENSAddress, prefetchENSAddress } from './useENSAddress'; +export { default as useENSAvatar, prefetchENSAvatar } from './useENSAvatar'; +export { default as useENSCover, prefetchENSCover } from './useENSCover'; +export { default as useENSProfile, prefetchENSProfile } from './useENSProfile'; +export { default as useENSOwner, prefetchENSOwner } from './useENSOwner'; +export { + default as useENSResolver, + prefetchENSResolver, +} from './useENSResolver'; +export { + default as useENSRegistrant, + prefetchENSRegistrant, +} from './useENSRegistrant'; +export { + default as useENSRecords, + prefetchENSRecords, + ensRecordsQueryKey, +} from './useENSRecords'; export { default as useFadeImage } from './useFadeImage'; export { default as useTrackENSProfile } from './useTrackENSProfile'; export { default as useENSRecordDisplayProperties } from './useENSRecordDisplayProperties'; @@ -50,13 +67,13 @@ export { default as useENSRegistrationActionHandler } from './useENSRegistration export { default as useENSRegistrationStepHandler } from './useENSRegistrationStepHandler'; export { default as useENSRegistrationCosts } from './useENSRegistrationCosts'; export { default as useENSRegistrationForm } from './useENSRegistrationForm'; -export { default as useENSResolveName } from './useENSResolveName'; -export { default as useENSProfileRecords } from './useENSProfileRecords'; export { default as useENSSearch } from './useENSSearch'; +export { default as useENSUniqueToken } from './useENSUniqueToken'; export { default as useExpandedStateNavigation } from './useExpandedStateNavigation'; export { default as useExternalWalletSectionsData } from './useExternalWalletSectionsData'; +export { default as useFetchHiddenTokens } from './useFetchHiddenTokens'; export { default as useFetchUniqueTokens } from './useFetchUniqueTokens'; -export { default as useFirstTransactionTimestamp } from './useFirstTransactionTimestamp'; +export { default as useENSFirstTransactionTimestamp } from './useENSFirstTransactionTimestamp'; export { default as useGas } from './useGas'; export { default as useGenericAsset } from './useGenericAsset'; export { default as useHeight } from './useHeight'; @@ -81,6 +98,7 @@ export { default as useMagicAutofocus } from './useMagicAutofocus'; export { default as useManageCloudBackups } from './useManageCloudBackups'; export { default as useMaxInputBalance } from './useMaxInputBalance'; export { default as useUniqueToken } from './useUniqueToken'; +export { default as useOpenENSNFTHandler } from './useOpenENSNFTHandler'; export { default as useOpenInvestmentCards } from './useOpenInvestmentCards'; export { default as useOpenSavings } from './useOpenSavings'; export { default as useOpenSmallBalances } from './useOpenSmallBalances'; diff --git a/src/hooks/useAccountENSDomains.ts b/src/hooks/useAccountENSDomains.ts index cdd8af44661..95eb5c9997a 100644 --- a/src/hooks/useAccountENSDomains.ts +++ b/src/hooks/useAccountENSDomains.ts @@ -1,10 +1,9 @@ +import { useMemo } from 'react'; import { useQuery } from 'react-query'; -import useAccountSettings from './useAccountSettings'; +import useAccountProfile from './useAccountProfile'; +import { prefetchENSAvatar } from './useENSAvatar'; import { EnsAccountRegistratonsData } from '@rainbow-me/apollo/queries'; -import { - fetchAccountRegistrations, - fetchImages, -} from '@rainbow-me/handlers/ens'; +import { fetchAccountRegistrations } from '@rainbow-me/handlers/ens'; import { getENSDomains, setENSDomains, @@ -16,8 +15,6 @@ const queryKey = ({ accountAddress }: { accountAddress: string }) => [ accountAddress, ]; -const imagesQueryKey = ({ name }: { name: string }) => ['domainImages', name]; - const STALE_TIME = 10000; async function fetchAccountENSDomains({ @@ -25,20 +22,12 @@ async function fetchAccountENSDomains({ }: { accountAddress: string; }) { - if (!accountAddress) return []; const result = await fetchAccountRegistrations(accountAddress); const registrations = result.data?.account?.registrations || []; - const domains = await Promise.all( - registrations.map(async ({ domain }) => { - const images = await fetchAccountENSImages(domain.name); - return { - ...domain, - images, - }; - }) - ); - - return domains; + return registrations.map(({ domain }) => { + prefetchENSAvatar(domain.name, { cacheFirst: true }); + return domain; + }); } async function fetchENSDomainsWithCache({ @@ -61,27 +50,52 @@ export async function prefetchAccountENSDomains({ }) { queryClient.prefetchQuery( queryKey({ accountAddress }), - async () => fetchENSDomainsWithCache({ accountAddress }), + async () => await fetchENSDomainsWithCache({ accountAddress }), { staleTime: STALE_TIME } ); } -async function fetchAccountENSImages(name: string) { - return queryClient.fetchQuery( - imagesQueryKey({ name }), - async () => await fetchImages(name), +export default function useAccountENSDomains() { + const { accountAddress, accountENS } = useAccountProfile(); + + const { data: domains, isLoading, isFetched, isSuccess } = useQuery< + EnsAccountRegistratonsData['account']['registrations'][number]['domain'][] + >( + queryKey({ accountAddress }), + async () => fetchENSDomainsWithCache({ accountAddress }), { - staleTime: 120000, + enabled: Boolean(accountAddress), } ); -} -export default function useAccountENSDomains() { - const { accountAddress } = useAccountSettings(); + const { ownedDomains, primaryDomain, nonPrimaryDomains } = useMemo(() => { + const ownedDomains = domains?.filter( + ({ owner }) => owner?.id?.toLowerCase() === accountAddress?.toLowerCase() + ); + return { + nonPrimaryDomains: + ownedDomains?.filter(({ name }) => accountENS !== name) || [], + ownedDomains, + primaryDomain: ownedDomains?.find(({ name }) => accountENS === name), + }; + }, [accountAddress, accountENS, domains]); - return useQuery< - EnsAccountRegistratonsData['account']['registrations'][number]['domain'][] - >(queryKey({ accountAddress }), async () => - fetchENSDomainsWithCache({ accountAddress }) - ); + const uniqueDomain = useMemo(() => { + return primaryDomain + ? primaryDomain + : nonPrimaryDomains?.length === 1 + ? nonPrimaryDomains?.[0] + : null; + }, [nonPrimaryDomains, primaryDomain]); + + return { + domains, + isFetched, + isLoading, + isSuccess, + nonPrimaryDomains, + ownedDomains, + primaryDomain, + uniqueDomain, + }; } diff --git a/src/hooks/useAccountProfile.ts b/src/hooks/useAccountProfile.ts index 5395e576860..60adc9ec06f 100644 --- a/src/hooks/useAccountProfile.ts +++ b/src/hooks/useAccountProfile.ts @@ -1,15 +1,46 @@ -import { useMemo } from 'react'; -import useAccountSettings from './useAccountSettings'; -import useWallets from './useWallets'; +import { useSelector } from 'react-redux'; +import { createSelector } from 'reselect'; +import { EthereumAddress } from '@/entities'; import { getAccountProfileInfo } from '@rainbow-me/helpers/accountInfo'; +const walletSelector = createSelector( + ({ wallets: { selected = {}, walletNames } }) => ({ + selectedWallet: selected as any, + walletNames, + }), + ({ selectedWallet, walletNames }) => ({ + selectedWallet, + walletNames, + }) +); + +const settingsSelector = createSelector( + ({ settings: { accountAddress } }) => ({ + accountAddress, + }), + ({ accountAddress }) => ({ + accountAddress, + }) +); + +const buildAccountProfile = ( + wallet: { + selectedWallet: any; + walletNames: { [a: EthereumAddress]: string }; + }, + account: { accountAddress: string } +) => + getAccountProfileInfo( + wallet.selectedWallet, + wallet.walletNames, + account.accountAddress + ); + export default function useAccountProfile() { - const wallets = useWallets(); - const { selectedWallet, walletNames } = wallets; - const { - accountAddress: accountsettingsAddress, - network, - } = useAccountSettings(); + const accountProfileSelector = createSelector( + [walletSelector, settingsSelector], + buildAccountProfile + ); const { accountAddress, @@ -18,16 +49,8 @@ export default function useAccountProfile() { accountImage, accountName, accountSymbol, - } = useMemo( - () => - getAccountProfileInfo( - selectedWallet, - walletNames, - network, - accountsettingsAddress - ), - [accountsettingsAddress, network, selectedWallet, walletNames] - ); + // @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'OutputParametricSelector<{ walle... Remove this comment to see the full error message + } = useSelector(accountProfileSelector); return { accountAddress, diff --git a/src/hooks/useAndroidScrollViewGestureHandler.ts b/src/hooks/useAndroidScrollViewGestureHandler.ts index b329dc4bea6..d191831750e 100644 --- a/src/hooks/useAndroidScrollViewGestureHandler.ts +++ b/src/hooks/useAndroidScrollViewGestureHandler.ts @@ -10,7 +10,7 @@ export default function useAndroidScrollViewGestureHandler({ navigation: navigationOverride, }: { navigation?: NavigationProp; -}) { +} = {}) { const inferredNavigation = useNavigation(); const navigation = navigationOverride || inferredNavigation; diff --git a/src/hooks/useCollectible.ts b/src/hooks/useCollectible.ts index 4889491aade..2f82b02a0ee 100644 --- a/src/hooks/useCollectible.ts +++ b/src/hooks/useCollectible.ts @@ -1,7 +1,6 @@ import { useEffect, useMemo } from 'react'; -import { useQueryClient } from 'react-query'; +import { useQuery } from 'react-query'; import { useDispatch, useSelector } from 'react-redux'; -import useAccountSettings from './useAccountSettings'; import { uniqueTokensQueryKey } from './useFetchUniqueTokens'; import { revalidateUniqueToken } from '@rainbow-me/redux/uniqueTokens'; @@ -15,17 +14,14 @@ export default function useCollectible( // @ts-expect-error ts-migrate(2339) FIXME: Property 'uniqueTokens' does not exist on type 'De... Remove this comment to see the full error message ({ uniqueTokens: { uniqueTokens } }) => uniqueTokens ); - const { accountAddress } = useAccountSettings(); - const queryClient = useQueryClient(); - const externalUniqueTokens = useMemo(() => { - return ( - queryClient.getQueryData( - uniqueTokensQueryKey({ address: externalAddress }) - ) || [] - ); - }, [queryClient, externalAddress]); - const isExternal = - Boolean(externalAddress) && externalAddress !== accountAddress; + const { data: externalUniqueTokens } = useQuery( + uniqueTokensQueryKey({ address: externalAddress }), + // We just want to watch for changes in the query key, + // so just supplying a noop function & staleTime of Infinity. + async () => [], + { staleTime: Infinity } + ); + const isExternal = Boolean(externalAddress); // Use the appropriate tokens based on if the user is viewing the // current accounts tokens, or external tokens (e.g. ProfileSheet) const uniqueTokens = useMemo( diff --git a/src/hooks/useENSAddress.ts b/src/hooks/useENSAddress.ts new file mode 100644 index 00000000000..aeea871c589 --- /dev/null +++ b/src/hooks/useENSAddress.ts @@ -0,0 +1,50 @@ +import { useQuery } from 'react-query'; +import { fetchPrimary } from '@rainbow-me/handlers/ens'; +import { getENSData, saveENSData } from '@rainbow-me/handlers/localstorage/ens'; +import { queryClient } from '@rainbow-me/react-query/queryClient'; +import { QueryConfig, UseQueryData } from '@rainbow-me/react-query/types'; + +export const ensAddressQueryKey = (name: string) => ['ens-address', name]; + +const STALE_TIME = 10000; + +export async function fetchENSAddress( + name: string, + { cacheFirst }: { cacheFirst?: boolean } = {} +) { + const cachedData = await getENSData('address', name); + if (cachedData) { + queryClient.setQueryData(ensAddressQueryKey(name), cachedData?.address); + if (cacheFirst) return cachedData?.address as string | null; + } + + const data = await fetchPrimary(name); + saveENSData('address', name, data); + return data?.address; +} + +export async function prefetchENSAddress( + name: string, + { cacheFirst }: { cacheFirst?: boolean } = {} +) { + queryClient.prefetchQuery( + ensAddressQueryKey(name), + async () => fetchENSAddress(name, { cacheFirst }), + { staleTime: STALE_TIME } + ); +} + +export default function useENSAddress( + name: string, + config?: QueryConfig +) { + return useQuery>( + ensAddressQueryKey(name), + async () => fetchENSAddress(name), + { + ...config, + // Data will be stale for 10s to avoid dupe queries + staleTime: STALE_TIME, + } + ); +} diff --git a/src/hooks/useENSAvatar.ts b/src/hooks/useENSAvatar.ts new file mode 100644 index 00000000000..32b0d6ab70a --- /dev/null +++ b/src/hooks/useENSAvatar.ts @@ -0,0 +1,57 @@ +import { useQuery } from 'react-query'; +import { fetchImage } from '@rainbow-me/handlers/ens'; +import { getENSData, saveENSData } from '@rainbow-me/handlers/localstorage/ens'; +import { queryClient } from '@rainbow-me/react-query/queryClient'; +import { QueryConfig, UseQueryData } from '@rainbow-me/react-query/types'; + +export const ensAvatarQueryKey = (name: string) => ['ens-avatar', name]; + +const STALE_TIME = 10000; + +export async function fetchENSAvatar( + name: string, + { + cacheFirst, + swallowError, + }: { cacheFirst?: boolean; swallowError?: boolean } = {} +) { + try { + const cachedAvatar = await getENSData('avatar', name); + if (cachedAvatar) { + queryClient.setQueryData(ensAvatarQueryKey(name), cachedAvatar); + if (cacheFirst) return cachedAvatar as { imageUrl: string }; + } + const avatar = await fetchImage('avatar', name); + saveENSData('avatar', name, avatar); + return avatar; + } catch (err) { + if (swallowError) return undefined; + throw err; + } +} + +export async function prefetchENSAvatar( + name: string, + { cacheFirst }: { cacheFirst?: boolean } = {} +) { + queryClient.prefetchQuery( + ensAvatarQueryKey(name), + async () => fetchENSAvatar(name, { cacheFirst }), + { staleTime: STALE_TIME } + ); +} + +export default function useENSAvatar( + name: string, + config?: QueryConfig +) { + return useQuery>( + ensAvatarQueryKey(name), + async () => fetchENSAvatar(name), + { + ...config, + // Data will be stale for 10s to avoid dupe queries + staleTime: STALE_TIME, + } + ); +} diff --git a/src/hooks/useENSCover.ts b/src/hooks/useENSCover.ts new file mode 100644 index 00000000000..fe2c69682ac --- /dev/null +++ b/src/hooks/useENSCover.ts @@ -0,0 +1,49 @@ +import { useQuery } from 'react-query'; +import { fetchImage } from '@rainbow-me/handlers/ens'; +import { getENSData, saveENSData } from '@rainbow-me/handlers/localstorage/ens'; +import { queryClient } from '@rainbow-me/react-query/queryClient'; +import { QueryConfig, UseQueryData } from '@rainbow-me/react-query/types'; + +export const ensCoverQueryKey = (name: string) => ['ens-header', name]; + +const STALE_TIME = 10000; + +export async function fetchENSCover( + name: string, + { cacheFirst }: { cacheFirst?: boolean } = {} +) { + const cachedCover = await getENSData('header', name); + if (cachedCover) { + queryClient.setQueryData(ensCoverQueryKey(name), cachedCover); + if (cacheFirst) return cachedCover as { imageUrl?: string | null }; + } + const cover = await fetchImage('header', name); + saveENSData('header', name, cover); + return cover; +} + +export async function prefetchENSCover( + name: string, + { cacheFirst }: { cacheFirst?: boolean } = {} +) { + queryClient.prefetchQuery( + ensCoverQueryKey(name), + async () => fetchENSCover(name, { cacheFirst }), + { staleTime: STALE_TIME } + ); +} + +export default function useENSCover( + name: string, + config?: QueryConfig +) { + return useQuery>( + ensCoverQueryKey(name), + async () => fetchENSCover(name), + { + ...config, + // Data will be stale for 10s to avoid dupe queries + staleTime: STALE_TIME, + } + ); +} diff --git a/src/hooks/useENSFirstTransactionTimestamp.ts b/src/hooks/useENSFirstTransactionTimestamp.ts new file mode 100644 index 00000000000..2d7a708ffbe --- /dev/null +++ b/src/hooks/useENSFirstTransactionTimestamp.ts @@ -0,0 +1,68 @@ +import PQueue from 'p-queue/dist'; +import { useQuery } from 'react-query'; +import { fetchENSAddress } from './useENSAddress'; +import { getENSData, saveENSData } from '@rainbow-me/handlers/localstorage/ens'; +import { queryClient } from '@rainbow-me/react-query/queryClient'; +import { QueryConfig, UseQueryData } from '@rainbow-me/react-query/types'; +import { getFirstTransactionTimestamp } from '@rainbow-me/utils/ethereumUtils'; + +const ensFirstTxTimestampQueryKey = (name: string) => [ + 'first-transaction-timestamp', + name, +]; + +const queue = new PQueue({ interval: 1000, intervalCap: 5 }); + +export async function fetchENSFirstTransactionTimestamp( + name: string, + { cacheFirst }: { cacheFirst?: boolean } = {} +) { + const cachedData = await getENSData('firstTxTimestamp', name); + if (cachedData) { + queryClient.setQueryData( + ensFirstTxTimestampQueryKey(name), + cachedData?.firstTxTimestamp + ); + if (cacheFirst) return cachedData?.firstTxTimestamp; + } + + const address = await fetchENSAddress(name); + const timestamp = address + ? await queue.add(async () => getFirstTransactionTimestamp(address)) + : undefined; + + timestamp + ? saveENSData('firstTxTimestamp', name, { firstTxTimestamp: timestamp }) + : queryClient.invalidateQueries(ensFirstTxTimestampQueryKey(name)); + + return timestamp; +} + +export async function prefetchENSFirstTransactionTimestamp( + name: string, + { cacheFirst }: { cacheFirst?: boolean } = {} +) { + queryClient.prefetchQuery( + ensFirstTxTimestampQueryKey(name), + async () => fetchENSFirstTransactionTimestamp(name, { cacheFirst }), + { cacheTime: Infinity, staleTime: Infinity } + ); +} + +export default function useENSFirstTransactionTimestamp( + name: string, + config?: QueryConfig +) { + return useQuery>( + ensFirstTxTimestampQueryKey(name), + async () => fetchENSFirstTransactionTimestamp(name), + { + ...config, + cacheTime: Infinity, + enabled: Boolean(name), + // First transaction timestamp will obviously never be stale. + // So we won't fetch / refetch it again. + staleTime: Infinity, + } + ); +} diff --git a/src/hooks/useENSLocalTransactions.ts b/src/hooks/useENSLocalTransactions.ts new file mode 100644 index 00000000000..6b5753d48a0 --- /dev/null +++ b/src/hooks/useENSLocalTransactions.ts @@ -0,0 +1,42 @@ +import { useSelector } from 'react-redux'; +import { + useAccountSettings, + useAccountTransactions, + usePendingTransactions, +} from '.'; +import { ENSRegistrationState } from '@rainbow-me/entities'; +import { AppState } from '@rainbow-me/redux/store'; +import { ethereumUtils } from '@rainbow-me/utils'; + +/** + * @description Returns the local ENS transactions for a given name. + * */ +export default function useENSLocalTransactions({ name }: { name: string }) { + const { accountAddress } = useAccountSettings(); + const { getPendingTransactionByHash } = usePendingTransactions(); + const { transactions } = useAccountTransactions(true, true); + + const registration = useSelector(({ ensRegistration }: AppState) => { + const { registrations } = ensRegistration as ENSRegistrationState; + const accountRegistrations = + registrations?.[accountAddress.toLowerCase()] || {}; + const registration = accountRegistrations[name]; + return registration; + }); + + const commitTransactionHash = registration?.commitTransactionHash?.toString(); + const pendingRegistrationTransaction = getPendingTransactionByHash( + registration?.registerTransactionHash?.toString() || '' + ); + const confirmedRegistrationTransaction = transactions.find( + (txn: any) => + ethereumUtils.getHash(txn) === registration?.registerTransactionHash && + !txn.pending + ); + + return { + commitTransactionHash, + confirmedRegistrationTransaction, + pendingRegistrationTransaction, + }; +} diff --git a/src/hooks/useENSModifiedRegistration.ts b/src/hooks/useENSModifiedRegistration.ts index b2decda32f3..2b128828e92 100644 --- a/src/hooks/useENSModifiedRegistration.ts +++ b/src/hooks/useENSModifiedRegistration.ts @@ -1,10 +1,15 @@ import { differenceWith, isEqual } from 'lodash'; import { useEffect, useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import useENSProfileRecords from './useENSProfileRecords'; -import useENSRegistration from './useENSRegistration'; -import { usePrevious } from '.'; +import { + useENSAvatar, + useENSCover, + useENSRecords, + useENSRegistration, + usePrevious, +} from '.'; import { Records, UniqueAsset } from '@rainbow-me/entities'; +import svgToPngIfNeeded from '@rainbow-me/handlers/svgs'; import { REGISTRATION_MODES } from '@rainbow-me/helpers/ens'; import * as ensRedux from '@rainbow-me/redux/ensRegistration'; import { AppState } from '@rainbow-me/redux/store'; @@ -15,7 +20,7 @@ import { } from '@rainbow-me/utils'; const getImageUrl = ( - key: 'avatar' | 'cover', + key: 'avatar' | 'header', records: Records, changedRecords: Records, uniqueTokens: UniqueAsset[], @@ -41,7 +46,9 @@ const getImageUrl = ( token.id === tokenId ); if (uniqueToken?.image_url) { - imageUrl = uniqueToken?.image_url; + imageUrl = svgToPngIfNeeded(uniqueToken?.image_url, false); + } else if (uniqueToken?.lowResUrl) { + imageUrl = uniqueToken?.lowResUrl; } else if (uniqueToken?.image_thumbnail_url) { imageUrl = uniqueToken?.image_thumbnail_url; } @@ -71,31 +78,44 @@ export default function useENSModifiedRegistration({ const uniqueTokens = useSelector( ({ uniqueTokens }: AppState) => uniqueTokens.uniqueTokens ); - const profileQuery = useENSProfileRecords(name, { - enabled: - mode === REGISTRATION_MODES.EDIT || - mode === REGISTRATION_MODES.RENEW || - mode === REGISTRATION_MODES.SET_NAME, + + const fetchEnabled = + mode === REGISTRATION_MODES.EDIT || + mode === REGISTRATION_MODES.RENEW || + mode === REGISTRATION_MODES.SET_NAME; + const { data: avatar, isSuccess: isAvatarSuccess } = useENSAvatar(name, { + enabled: fetchEnabled, + }); + const { data: cover, isSuccess: isCoverSuccess } = useENSCover(name, { + enabled: fetchEnabled, + }); + const { + data: { coinAddresses: fetchedCoinAddresses, records: fetchedRecords } = {}, + isSuccess: isRecordsSuccess, + } = useENSRecords(name, { + enabled: fetchEnabled, }); + const isSuccess = isAvatarSuccess && isCoverSuccess && isRecordsSuccess; + useEffect(() => { if ( setInitialRecordsWhenInEditMode && mode === REGISTRATION_MODES.EDIT && - profileQuery.isSuccess + isSuccess ) { const initialRecords = { - ...profileQuery.data?.records, - ...profileQuery.data?.coinAddresses, + ...fetchedRecords, + ...fetchedCoinAddresses, } as Records; dispatch(ensRedux.setInitialRecords(initialRecords)); } }, [ dispatch, mode, - profileQuery.data?.coinAddresses, - profileQuery.data?.records, - profileQuery.isSuccess, + fetchedCoinAddresses, + fetchedRecords, + isSuccess, setInitialRecordsWhenInEditMode, ]); @@ -163,15 +183,15 @@ export default function useENSModifiedRegistration({ records, changedRecords, uniqueTokens, - profileQuery.data?.images.avatarUrl, + avatar?.imageUrl, mode ); const coverUrl = getImageUrl( - 'cover', + 'header', records, changedRecords, uniqueTokens, - profileQuery.data?.images.coverUrl, + cover?.imageUrl, mode ); @@ -180,17 +200,17 @@ export default function useENSModifiedRegistration({ coverUrl, }; }, [ - profileQuery.data?.images.avatarUrl, - profileQuery.data?.images.coverUrl, records, - uniqueTokens, changedRecords, + uniqueTokens, + avatar?.imageUrl, mode, + cover?.imageUrl, ]); return { changedRecords, images, - profileQuery, + isSuccess, }; } diff --git a/src/hooks/useENSOwner.ts b/src/hooks/useENSOwner.ts new file mode 100644 index 00000000000..4b5b79e17b5 --- /dev/null +++ b/src/hooks/useENSOwner.ts @@ -0,0 +1,54 @@ +import { useQuery } from 'react-query'; +import useAccountSettings from './useAccountSettings'; +import { fetchOwner } from '@rainbow-me/handlers/ens'; +import { getENSData, saveENSData } from '@rainbow-me/handlers/localstorage/ens'; +import { queryClient } from '@rainbow-me/react-query/queryClient'; +import { QueryConfig, UseQueryData } from '@rainbow-me/react-query/types'; + +export const ensOwnerQueryKey = (name: string) => ['ens-owner', name]; + +const STALE_TIME = 10000; + +export async function fetchENSOwner(name: string) { + const cachedOwner = await getENSData('owner', name); + if (cachedOwner) { + queryClient.setQueryData(ensOwnerQueryKey(name), cachedOwner); + } + const owner = await fetchOwner(name); + saveENSData('owner', name, owner); + return owner; +} + +export async function prefetchENSOwner(name: string) { + queryClient.prefetchQuery( + ensOwnerQueryKey(name), + async () => fetchENSOwner(name), + { staleTime: STALE_TIME } + ); +} + +export default function useENSOwner( + name: string, + config?: QueryConfig +) { + const { accountAddress } = useAccountSettings(); + + const { data, ...query } = useQuery>( + ensOwnerQueryKey(name), + async () => fetchENSOwner(name), + { + ...config, + // Data will be stale for 10s to avoid dupe queries + staleTime: STALE_TIME, + } + ); + + const isOwner = + data?.address?.toLowerCase() === accountAddress?.toLowerCase(); + + return { + data, + isOwner, + ...query, + }; +} diff --git a/src/hooks/useENSPendingRegistrations.tsx b/src/hooks/useENSPendingRegistrations.tsx index 8b6f4e7cca9..991e3cf722d 100644 --- a/src/hooks/useENSPendingRegistrations.tsx +++ b/src/hooks/useENSPendingRegistrations.tsx @@ -2,13 +2,17 @@ import { useCallback, useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useAccountSettings, useENSRegistration } from '.'; import { ENSRegistrationState } from '@rainbow-me/entities'; +import { REGISTRATION_MODES } from '@rainbow-me/helpers/ens'; +import { useNavigation } from '@rainbow-me/navigation'; import { removeExpiredRegistrations } from '@rainbow-me/redux/ensRegistration'; import { AppState } from '@rainbow-me/redux/store'; +import Routes from '@rainbow-me/routes'; import { getENSNFTAvatarUrl } from '@rainbow-me/utils'; export default function useENSPendingRegistrations() { const { accountAddress } = useAccountSettings(); - const { removeRegistrationByName } = useENSRegistration(); + const { removeRegistrationByName, startRegistration } = useENSRegistration(); + const { navigate } = useNavigation(); const dispatch = useDispatch(); const { pendingRegistrations, accountRegistrations } = useSelector( @@ -63,8 +67,19 @@ export default function useENSPendingRegistrations() { dispatch(removeExpiredRegistrations()); }, [dispatch]); + const finishRegistration = useCallback( + name => { + startRegistration(name, REGISTRATION_MODES.CREATE); + setTimeout(() => { + navigate(Routes.ENS_CONFIRM_REGISTER_SHEET, {}); + }, 100); + }, + [navigate, startRegistration] + ); + return { accountRegistrations, + finishRegistration, pendingRegistrations, registrationImages, removeExpiredRegistrations: refreshRegistrations, diff --git a/src/hooks/useENSProfile.ts b/src/hooks/useENSProfile.ts index 324cdd92059..a4f0f620f59 100644 --- a/src/hooks/useENSProfile.ts +++ b/src/hooks/useENSProfile.ts @@ -1,49 +1,122 @@ import { useQuery } from 'react-query'; import useAccountSettings from './useAccountSettings'; +import { ensAddressQueryKey, fetchENSAddress } from './useENSAddress'; +import { ensAvatarQueryKey, fetchENSAvatar } from './useENSAvatar'; +import { ensCoverQueryKey, fetchENSCover } from './useENSCover'; +import { ensOwnerQueryKey, fetchENSOwner } from './useENSOwner'; +import { ensRecordsQueryKey, fetchENSRecords } from './useENSRecords'; +import { ensRegistrantQueryKey, fetchENSRegistrant } from './useENSRegistrant'; +import { ensResolverQueryKey, fetchENSResolver } from './useENSResolver'; import useWallets from './useWallets'; -import { fetchProfile } from '@rainbow-me/handlers/ens'; -import { getProfile, saveProfile } from '@rainbow-me/handlers/localstorage/ens'; +import { + getENSProfile, + saveENSProfile, +} from '@rainbow-me/handlers/localstorage/ens'; import { queryClient } from '@rainbow-me/react-query/queryClient'; import { QueryConfig, UseQueryData } from '@rainbow-me/react-query/types'; -const queryKey = (name: string) => ['ens-profile', name]; +const queryKey = ( + name: string, + { supportedRecordsOnly }: { supportedRecordsOnly?: boolean } = {} +) => ['ens-profile', name, { supportedRecordsOnly }]; const STALE_TIME = 10000; -async function fetchENSProfile({ name }: { name: string }) { - const cachedProfile = await getProfile(name); +async function fetchENSProfile( + name: string, + { supportedRecordsOnly = true }: { supportedRecordsOnly?: boolean } = {} +) { + const cachedProfile = await getENSProfile(name); if (cachedProfile) { - queryClient.setQueryData(queryKey(name), cachedProfile); + queryClient.setQueryData( + queryKey(name, { supportedRecordsOnly }), + cachedProfile + ); } - const profile = await fetchProfile(name); - saveProfile(name, profile); + + const [ + address, + avatar, + header, + owner, + { coinAddresses, records }, + { registration, registrant }, + resolver, + ] = await Promise.all([ + queryClient.fetchQuery(ensAddressQueryKey(name), () => + fetchENSAddress(name) + ), + queryClient.fetchQuery(ensAvatarQueryKey(name), () => fetchENSAvatar(name)), + queryClient.fetchQuery(ensCoverQueryKey(name), () => fetchENSCover(name)), + queryClient.fetchQuery(ensOwnerQueryKey(name), () => fetchENSOwner(name)), + queryClient.fetchQuery(ensRecordsQueryKey({ name }), () => + fetchENSRecords(name, { supportedOnly: supportedRecordsOnly }) + ), + queryClient.fetchQuery(ensRegistrantQueryKey(name), () => + fetchENSRegistrant(name) + ), + queryClient.fetchQuery(ensResolverQueryKey(name), () => + fetchENSResolver(name) + ), + ]); + + const profile = { + address, + coinAddresses, + images: { + avatar, + header, + }, + owner, + records, + registrant, + registration, + resolver, + }; + saveENSProfile(name, profile); return profile; } -export async function prefetchENSProfile({ name }: { name: string }) { - queryClient.prefetchQuery( - queryKey(name), - async () => fetchENSProfile({ name }), - { staleTime: STALE_TIME } - ); +export async function prefetchENSProfile(name: string) { + queryClient.prefetchQuery(queryKey(name), async () => fetchENSProfile(name), { + staleTime: STALE_TIME, + }); } +/** + * @description Hook to fetch a whole ENS profile. + * + * WARNING: This will invoke several requests to the RPC. You may + * be better off using the individual hooks (e.g. `useENSAvatar`, `useENSRecords`, etc) + * if you do not need everything. + */ export default function useENSProfile( name: string, - config?: QueryConfig + { + supportedRecordsOnly = true, + ...config + }: QueryConfig & { + supportedRecordsOnly?: boolean; + } = {} ) { const { accountAddress } = useAccountSettings(); const { walletNames } = useWallets(); const { data, isLoading, isSuccess } = useQuery< - UseQueryData - >(queryKey(name), async () => fetchENSProfile({ name }), { - ...config, - // Data will be stale for 10s to avoid dupe queries - staleTime: STALE_TIME, - }); + UseQueryData + >( + queryKey(name, { supportedRecordsOnly }), + async () => fetchENSProfile(name, { supportedRecordsOnly }), + { + ...config, + // Data will be stale for 10s to avoid dupe queries + staleTime: STALE_TIME, + } + ); const isOwner = data?.owner?.address?.toLowerCase() === accountAddress?.toLowerCase(); + const isRegistrant = + data?.registrant?.address?.toLowerCase() === accountAddress?.toLowerCase(); // if a ENS NFT is sent, the ETH coinAddress record doesn't change // if the user tries to use it to set primary name the tx will go through @@ -59,6 +132,7 @@ export default function useENSProfile( isLoading, isOwner, isPrimaryName, + isRegistrant, isSetNameEnabled, isSuccess, }; diff --git a/src/hooks/useENSProfileImages.ts b/src/hooks/useENSProfileImages.ts deleted file mode 100644 index 14f5232c62d..00000000000 --- a/src/hooks/useENSProfileImages.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { useQuery } from 'react-query'; -import { fetchImages } from '@rainbow-me/handlers/ens'; -import { - getProfileImages, - saveProfileImages, -} from '@rainbow-me/handlers/localstorage/ens'; -import { queryClient } from '@rainbow-me/react-query/queryClient'; -import { QueryConfig, UseQueryData } from '@rainbow-me/react-query/types'; - -export const ensProfileImagesQueryKey = (name: string) => [ - 'ens-profile-images', - name, -]; - -const STALE_TIME = 10000; - -async function fetchENSProfileImages({ name }: { name: string }) { - const cachedImages = await getProfileImages(name); - if (cachedImages) { - queryClient.setQueryData(ensProfileImagesQueryKey(name), cachedImages); - } - const images = await fetchImages(name); - saveProfileImages(name, images); - return images; -} - -export async function prefetchENSProfileImages({ name }: { name: string }) { - queryClient.prefetchQuery( - ensProfileImagesQueryKey(name), - async () => fetchENSProfileImages({ name }), - { staleTime: STALE_TIME } - ); -} - -export default function useENSProfileImages( - name: string, - config?: QueryConfig -) { - const { data, isFetched } = useQuery>( - ensProfileImagesQueryKey(name), - async () => fetchENSProfileImages({ name }), - { - ...config, - // Data will be stale for 10s to avoid dupe queries - staleTime: STALE_TIME, - } - ); - - return { data, isFetched }; -} diff --git a/src/hooks/useENSProfileRecords.ts b/src/hooks/useENSProfileRecords.ts deleted file mode 100644 index c570fb2df99..00000000000 --- a/src/hooks/useENSProfileRecords.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { useQuery, useQueryClient } from 'react-query'; -import { fetchProfileRecords } from '@rainbow-me/handlers/ens'; -import { - getProfileRecords, - saveProfileRecords, -} from '@rainbow-me/handlers/localstorage/ens'; -import { QueryConfig, UseQueryData } from '@rainbow-me/react-query/types'; - -const queryKey = (name: string) => ['ens-profile-records', name]; - -const STALE_TIME = 10000; - -export default function useENSProfileRecords( - name: string, - config?: QueryConfig -) { - const queryClient = useQueryClient(); - const { data, isLoading, isSuccess } = useQuery< - UseQueryData - >( - queryKey(name), - async () => { - const cachedProfile = await getProfileRecords(name); - if (cachedProfile) { - queryClient.setQueryData(queryKey(name), cachedProfile); - } - const profileRecords = await fetchProfileRecords(name); - saveProfileRecords(name, profileRecords); - return profileRecords; - }, - { - ...config, - // Data will be stale for 10s to avoid dupe queries - staleTime: STALE_TIME, - } - ); - - return { data, isLoading, isSuccess }; -} diff --git a/src/hooks/useENSRecordDisplayProperties.tsx b/src/hooks/useENSRecordDisplayProperties.tsx index 274e6d0623d..cbe25dc8a61 100644 --- a/src/hooks/useENSRecordDisplayProperties.tsx +++ b/src/hooks/useENSRecordDisplayProperties.tsx @@ -2,10 +2,10 @@ import lang from 'i18n-js'; import { upperFirst } from 'lodash'; import React, { useCallback, useMemo } from 'react'; import { Linking } from 'react-native'; -import { ContextMenuButton } from 'react-native-ios-context-menu'; import URL from 'url-parse'; import useClipboard from './useClipboard'; import useENSRegistration from './useENSRegistration'; +import ContextMenuButton from '@/components/native-context-menu/contextMenu'; import { ENS_RECORDS, REGISTRATION_MODES, @@ -13,12 +13,17 @@ import { } from '@rainbow-me/helpers/ens'; import { useNavigation } from '@rainbow-me/navigation'; import Routes from '@rainbow-me/routes'; -import { showActionSheetWithOptions } from '@rainbow-me/utils'; import { formatAddressForDisplay } from '@rainbow-me/utils/abbreviations'; +type ImageSource = { imageUrl?: string | null }; +type ENSImages = { + avatar?: ImageSource; + cover?: ImageSource; +}; + const imageKeyMap = { [ENS_RECORDS.avatar]: 'avatarUrl', - [ENS_RECORDS.cover]: 'coverUrl', + [ENS_RECORDS.header]: 'coverUrl', } as { [key: string]: 'avatarUrl' | 'coverUrl'; }; @@ -47,12 +52,14 @@ const links = { export default function useENSRecordDisplayProperties({ allowEdit, ensName, + images, key: recordKey, value: recordValue, type, }: { allowEdit?: boolean; ensName?: string; + images?: ENSImages; key: string; value: string; type: 'address' | 'record'; @@ -70,6 +77,9 @@ export default function useENSRecordDisplayProperties({ const isUrlValue = useMemo(() => recordValue.match(/^http/), [recordValue]); const url = useMemo(() => { + if (isImageValue) { + return images?.[recordKey as 'avatar' | 'cover']?.imageUrl || undefined; + } if (isUrlValue || isUrlRecord) { return recordValue.match(/^http/) ? recordValue @@ -78,7 +88,7 @@ export default function useENSRecordDisplayProperties({ if (links[recordKey]) { return `${links[recordKey]}${recordValue.replace('@', '')}`; } - }, [isUrlRecord, isUrlValue, recordKey, recordValue]); + }, [images, isImageValue, isUrlRecord, isUrlValue, recordKey, recordValue]); const { displayUrl, displayUrlUsername } = useMemo(() => { const urlObj = url ? new URL(url) : { hostname: '', pathname: '' }; @@ -102,7 +112,7 @@ export default function useENSRecordDisplayProperties({ const value = useMemo(() => { if (isUrlRecord && displayUrl) { - return `􀤆 ${displayUrl}`; + return android ? ` 􀤆 ${displayUrl} ` : `􀤆 ${displayUrl}`; } if (isUrlValue && displayUrlUsername) { return displayUrlUsername; @@ -113,6 +123,18 @@ export default function useENSRecordDisplayProperties({ if (type === 'address') { return formatAddressForDisplay(recordValue, 4, 4) || ''; } + if ( + recordValue.includes('@') && + (recordKey === ENS_RECORDS.discord || + recordKey === ENS_RECORDS.github || + recordKey === ENS_RECORDS.reddit || + recordKey === ENS_RECORDS.instagram || + recordKey === ENS_RECORDS.snapchat || + recordKey === ENS_RECORDS.telegram || + recordKey === ENS_RECORDS.twitter) + ) { + return recordValue.replace('@', ''); + } return recordValue; }, [ displayUrl, @@ -206,28 +228,12 @@ export default function useENSRecordDisplayProperties({ ] ); - const handleAndroidPress = useCallback(() => { - const actionSheetOptions = menuItems - .map(item => item?.actionTitle) - .filter(Boolean) as any; - - showActionSheetWithOptions( - { - options: actionSheetOptions, - }, - async (buttonIndex: number) => { - const actionKey = menuItems[buttonIndex]?.actionKey; - handlePressMenuItem({ nativeEvent: { actionKey } }); - } - ); - }, [handlePressMenuItem, menuItems]); - const Button = useCallback( ({ children, ...props }) => ( ), - [handleAndroidPress, handlePressMenuItem, isImageValue, menuItems] + [handlePressMenuItem, isImageValue, menuItems] ); return { diff --git a/src/hooks/useENSRecords.ts b/src/hooks/useENSRecords.ts new file mode 100644 index 00000000000..63d45f7e7fd --- /dev/null +++ b/src/hooks/useENSRecords.ts @@ -0,0 +1,75 @@ +import { useQuery } from 'react-query'; +import { Records } from '@rainbow-me/entities'; +import { fetchCoinAddresses, fetchRecords } from '@rainbow-me/handlers/ens'; +import { getENSData, saveENSData } from '@rainbow-me/handlers/localstorage/ens'; +import { ENS_RECORDS } from '@rainbow-me/helpers/ens'; +import { queryClient } from '@rainbow-me/react-query/queryClient'; +import { QueryConfig, UseQueryData } from '@rainbow-me/react-query/types'; + +export const ensRecordsQueryKey = ({ + name, + supportedOnly, +}: { + name: string; + supportedOnly?: boolean; +}) => ['ens-records', name, supportedOnly]; + +const STALE_TIME = 10000; + +export async function fetchENSRecords( + name: string, + { + cacheFirst, + supportedOnly = true, + }: { cacheFirst?: boolean; supportedOnly?: boolean } = {} +) { + const cachedRecords: { + coinAddresses: { [key in ENS_RECORDS]: string }; + records: Partial; + } | null = await getENSData('records', name); + + if (cachedRecords) { + queryClient.setQueryData( + ensRecordsQueryKey({ name, supportedOnly }), + cachedRecords + ); + if (cacheFirst) return cachedRecords; + } + const records = await fetchRecords(name, { supportedOnly }); + const coinAddresses = await fetchCoinAddresses(name, { supportedOnly }); + const data = { coinAddresses, records }; + saveENSData('records', name, data); + return data; +} + +export async function prefetchENSRecords( + name: string, + { + cacheFirst, + supportedOnly = true, + }: { cacheFirst?: boolean; supportedOnly?: boolean } = {} +) { + queryClient.prefetchQuery( + ensRecordsQueryKey({ name, supportedOnly }), + async () => fetchENSRecords(name, { cacheFirst, supportedOnly }), + { staleTime: STALE_TIME } + ); +} + +export default function useENSRecords( + name: string, + { + supportedOnly = true, + ...config + }: QueryConfig & { supportedOnly?: boolean } = {} +) { + return useQuery>( + ensRecordsQueryKey({ name, supportedOnly }), + async () => fetchENSRecords(name, { supportedOnly }), + { + ...config, + // Data will be stale for 10s to avoid dupe queries + staleTime: STALE_TIME, + } + ); +} diff --git a/src/hooks/useENSRegistrant.ts b/src/hooks/useENSRegistrant.ts new file mode 100644 index 00000000000..c30eff85033 --- /dev/null +++ b/src/hooks/useENSRegistrant.ts @@ -0,0 +1,42 @@ +import { useQuery } from 'react-query'; +import { fetchRegistration } from '@rainbow-me/handlers/ens'; +import { getENSData, saveENSData } from '@rainbow-me/handlers/localstorage/ens'; +import { queryClient } from '@rainbow-me/react-query/queryClient'; +import { QueryConfig, UseQueryData } from '@rainbow-me/react-query/types'; + +export const ensRegistrantQueryKey = (name: string) => ['ens-registrant', name]; + +const STALE_TIME = 10000; + +export async function fetchENSRegistrant(name: string) { + const cachedRegistrant = await getENSData('registrant', name); + if (cachedRegistrant) { + queryClient.setQueryData(ensRegistrantQueryKey(name), cachedRegistrant); + } + const registrant = await fetchRegistration(name); + saveENSData('registrant', name, registrant); + return registrant; +} + +export async function prefetchENSRegistrant(name: string) { + queryClient.prefetchQuery( + ensRegistrantQueryKey(name), + async () => fetchENSRegistrant(name), + { staleTime: STALE_TIME } + ); +} + +export default function useENSRegistrant( + name: string, + config?: QueryConfig +) { + return useQuery>( + ensRegistrantQueryKey(name), + async () => fetchENSRegistrant(name), + { + ...config, + // Data will be stale for 10s to avoid dupe queries + staleTime: STALE_TIME, + } + ); +} diff --git a/src/hooks/useENSRegistrationActionHandler.ts b/src/hooks/useENSRegistrationActionHandler.ts index a769c6e2eaa..4bb44a0b5b4 100644 --- a/src/hooks/useENSRegistrationActionHandler.ts +++ b/src/hooks/useENSRegistrationActionHandler.ts @@ -1,16 +1,23 @@ +import { StaticJsonRpcProvider } from '@ethersproject/providers'; import { useNavigation } from '@react-navigation/core'; -import { useCallback, useMemo } from 'react'; +import { useCallback, useEffect, useMemo, useRef } from 'react'; import { Image } from 'react-native-image-crop-picker'; -import { useDispatch } from 'react-redux'; import { useRecoilValue } from 'recoil'; import { avatarMetadataAtom } from '../components/ens-registration/RegistrationAvatar/RegistrationAvatar'; import { coverMetadataAtom } from '../components/ens-registration/RegistrationCover/RegistrationCover'; import { ENSActionParameters, RapActionTypes } from '../raps/common'; import usePendingTransactions from './usePendingTransactions'; -import { useAccountSettings, useCurrentNonce, useENSRegistration } from '.'; +import { + useAccountSettings, + useCurrentNonce, + useENSRegistration, + useWalletENSAvatar, +} from '.'; import { Records, RegistrationParameters } from '@rainbow-me/entities'; import { fetchResolver } from '@rainbow-me/handlers/ens'; +import { saveNameFromLabelhash } from '@rainbow-me/handlers/localstorage/ens'; import { uploadImage } from '@rainbow-me/handlers/pinata'; +import { getProviderForNetwork } from '@rainbow-me/handlers/web3'; import { ENS_DOMAIN, generateSalt, @@ -19,16 +26,16 @@ import { } from '@rainbow-me/helpers/ens'; import { loadWallet } from '@rainbow-me/model/wallet'; import { executeRap } from '@rainbow-me/raps'; -import { saveCommitRegistrationParameters } from '@rainbow-me/redux/ensRegistration'; import { timeUnits } from '@rainbow-me/references'; import Routes from '@rainbow-me/routes'; -import { logger } from '@rainbow-me/utils'; +import { labelhash, logger } from '@rainbow-me/utils'; const formatENSActionParams = ( registrationParameters: RegistrationParameters ): ENSActionParameters => { return { duration: registrationParameters?.duration, + mode: registrationParameters?.mode, name: registrationParameters?.name, ownerAddress: registrationParameters?.ownerAddress, records: registrationParameters?.records, @@ -40,30 +47,51 @@ const formatENSActionParams = ( export default function useENSRegistrationActionHandler( { - sendReverseRecord, - yearsDuration, + sendReverseRecord = false, + yearsDuration = 1, step: registrationStep, }: { - yearsDuration: number; - sendReverseRecord: boolean; + yearsDuration?: number; + sendReverseRecord?: boolean; step: keyof typeof REGISTRATION_STEPS; } = {} as any ) { - const dispatch = useDispatch(); const { accountAddress, network } = useAccountSettings(); const getNextNonce = useCurrentNonce(accountAddress, network); const { registrationParameters } = useENSRegistration(); const { navigate } = useNavigation(); const { getPendingTransactionByHash } = usePendingTransactions(); + const { updateWalletENSAvatars } = useWalletENSAvatar(); const avatarMetadata = useRecoilValue(avatarMetadataAtom); const coverMetadata = useRecoilValue(coverMetadataAtom); const duration = yearsDuration * timeUnits.secs.year; + const updateAvatarsOnNextBlock = useRef(false); + useEffect(() => { + let provider: StaticJsonRpcProvider; + + const updateAvatars = () => { + if (updateAvatarsOnNextBlock.current) { + updateWalletENSAvatars(); + updateAvatarsOnNextBlock.current = false; + } + }; + + (async () => { + provider = await getProviderForNetwork(); + provider.on('block', updateAvatars); + })(); + return () => { + provider?.off('block', updateAvatars); + }; + }, [updateWalletENSAvatars]); + // actions const commitAction = useCallback( async (callback: () => void) => { + updateAvatarsOnNextBlock.current = true; const wallet = await loadWallet(); if (!wallet) { return; @@ -78,6 +106,11 @@ export default function useENSRegistrationActionHandler( ), ]); + const hash = labelhash( + registrationParameters.name.replace(ENS_DOMAIN, '') + ); + await saveNameFromLabelhash(hash, registrationParameters.name); + const commitEnsRegistrationParameters: ENSActionParameters = { ...formatENSActionParams(registrationParameters), duration, @@ -103,25 +136,17 @@ export default function useENSRegistrationActionHandler( // we want to speed up the last commit tx sent const commitTransactionHash = registrationParameters?.commitTransactionHash; - const saveCommitTransactionHash = (hash: string) => { - dispatch( - saveCommitRegistrationParameters({ - commitTransactionHash: hash, - }) - ); - }; + const tx = getPendingTransactionByHash(commitTransactionHash || ''); commitTransactionHash && tx && navigate(Routes.SPEED_UP_AND_CANCEL_SHEET, { accentColor, - onSendTransactionCallback: saveCommitTransactionHash, tx, type: 'speed_up', }); }, [ - dispatch, getPendingTransactionByHash, navigate, registrationParameters?.commitTransactionHash, @@ -145,7 +170,7 @@ export default function useENSRegistrationActionHandler( getRentPrice(name.replace(ENS_DOMAIN, ''), duration), uploadRecordImages(registrationParameters.changedRecords, { avatar: avatarMetadata, - cover: coverMetadata, + header: coverMetadata, }), ]); @@ -165,6 +190,8 @@ export default function useENSRegistrationActionHandler( registerEnsRegistrationParameters, callback ); + + updateAvatarsOnNextBlock.current = true; }, [ accountAddress, @@ -247,7 +274,7 @@ export default function useENSRegistrationActionHandler( getNextNonce(), uploadRecordImages(registrationParameters.changedRecords, { avatar: avatarMetadata, - cover: coverMetadata, + header: coverMetadata, }), fetchResolver(registrationParameters.name), ]); @@ -258,6 +285,7 @@ export default function useENSRegistrationActionHandler( ownerAddress: accountAddress, records: changedRecords, resolverAddress: resolver?.address, + setReverseRecord: sendReverseRecord, }; await executeRap( @@ -266,6 +294,8 @@ export default function useENSRegistrationActionHandler( setRecordsEnsRegistrationParameters, callback ); + + updateAvatarsOnNextBlock.current = true; }, [ accountAddress, @@ -273,9 +303,56 @@ export default function useENSRegistrationActionHandler( coverMetadata, getNextNonce, registrationParameters, + sendReverseRecord, ] ); + const transferAction = useCallback( + async ( + callback: () => void, + { + clearRecords, + records, + name, + setAddress, + toAddress, + transferControl, + wallet: walletOverride, + } + ) => { + const wallet = walletOverride || (await loadWallet()); + if (!wallet) { + return; + } + + const nonce = await getNextNonce(); + + const transferEnsParameters: ENSActionParameters = { + ...formatENSActionParams({ + ...registrationParameters, + records: records ?? registrationParameters.records, + }), + clearRecords, + name, + nonce, + ownerAddress: accountAddress, + setAddress, + toAddress, + transferControl, + }; + + const { nonce: newNonce } = await executeRap( + wallet, + RapActionTypes.transferENS, + transferEnsParameters, + callback + ); + + return { nonce: newNonce }; + }, + [accountAddress, getNextNonce, registrationParameters] + ); + const actions = useMemo( () => ({ [REGISTRATION_STEPS.COMMIT]: commitAction, @@ -283,6 +360,7 @@ export default function useENSRegistrationActionHandler( [REGISTRATION_STEPS.REGISTER]: registerAction, [REGISTRATION_STEPS.RENEW]: renewAction, [REGISTRATION_STEPS.SET_NAME]: setNameAction, + [REGISTRATION_STEPS.TRANSFER]: transferAction, [REGISTRATION_STEPS.WAIT_COMMIT_CONFIRMATION]: speedUpCommitAction, [REGISTRATION_STEPS.WAIT_ENS_COMMITMENT]: () => null, }), @@ -293,6 +371,7 @@ export default function useENSRegistrationActionHandler( setNameAction, setRecordsAction, speedUpCommitAction, + transferAction, ] ); @@ -303,9 +382,9 @@ export default function useENSRegistrationActionHandler( async function uploadRecordImages( records: Partial | undefined, - imageMetadata: { avatar?: Image; cover?: Image } + imageMetadata: { avatar?: Image; header?: Image } ) { - const uploadRecordImage = async (key: 'avatar' | 'cover') => { + const uploadRecordImage = async (key: 'avatar' | 'header') => { if ( (records?.[key]?.startsWith('~') || records?.[key]?.startsWith('file')) && imageMetadata[key] @@ -325,14 +404,14 @@ async function uploadRecordImages( return records?.[key]; }; - const [avatar, cover] = await Promise.all([ + const [avatar, header] = await Promise.all([ uploadRecordImage('avatar'), - uploadRecordImage('cover'), + uploadRecordImage('header'), ]); return { ...records, avatar, - cover, + header, }; } diff --git a/src/hooks/useENSRegistrationCosts.ts b/src/hooks/useENSRegistrationCosts.ts index d22e74ed491..8f74ed4ef8e 100644 --- a/src/hooks/useENSRegistrationCosts.ts +++ b/src/hooks/useENSRegistrationCosts.ts @@ -75,6 +75,7 @@ export default function useENSRegistrationCosts({ isValidGas: useGasIsValidGas, gasLimit: useGasGasLimit, selectedGasFeeOption, + isGasReady, } = useGas(); const [gasFeeParams, setGasFeeParams] = useState({ @@ -151,9 +152,10 @@ export default function useENSRegistrationCosts({ ownerAddress: mode === REGISTRATION_MODES.EDIT ? accountAddress : undefined, records: changedRecords, + setReverseRecord: sendReverseRecord, }); return newSetRecordsGasLimit || ''; - }, [changedRecords, name, accountAddress, mode]); + }, [name, mode, accountAddress, changedRecords, sendReverseRecord]); const getSetNameGasLimit = useCallback(async () => { const newSetNameGasLimit = await estimateENSSetNameGasLimit({ @@ -199,16 +201,21 @@ export default function useENSRegistrationCosts({ enabled: step === REGISTRATION_STEPS.COMMIT || step === REGISTRATION_STEPS.EDIT, queryFn: getSetRecordsGasLimit, - queryKey: [QUERY_KEYS.GET_SET_RECORDS_GAS_LIMIT, name, changedRecords], + queryKey: [ + QUERY_KEYS.GET_SET_RECORDS_GAS_LIMIT, + name, + changedRecords, + sendReverseRecord, + ], staleTime: QUERY_STALE_TIME, }, { enabled: (step === REGISTRATION_STEPS.COMMIT || step === REGISTRATION_STEPS.REGISTER) && - nameUpdated, + Boolean(accountAddress), queryFn: getReverseRecord, - queryKey: [QUERY_KEYS.GET_REVERSE_RECORD, name], + queryKey: [QUERY_KEYS.GET_REVERSE_RECORD, accountAddress], staleTime: QUERY_STALE_TIME, }, { @@ -229,6 +236,7 @@ export default function useENSRegistrationCosts({ sendReverseRecord, nameUpdated, changedRecords, + name, ], staleTime: QUERY_STALE_TIME, }, @@ -278,6 +286,7 @@ export default function useENSRegistrationCosts({ [REGISTRATION_STEPS.EDIT]: setRecordsGasLimit, [REGISTRATION_STEPS.REGISTER]: registerRapGasLimit, [REGISTRATION_STEPS.SET_NAME]: setNameGasLimit, + [REGISTRATION_STEPS.TRANSFER]: null, [REGISTRATION_STEPS.WAIT_COMMIT_CONFIRMATION]: null, [REGISTRATION_STEPS.WAIT_ENS_COMMITMENT]: null, }), @@ -357,15 +366,16 @@ export default function useENSRegistrationCosts({ }, [prevIsSufficientGas, prevIsValidGas, setIsValidGas, useGasIsValidGas]); useEffect(() => { - if (!currentStepGasLimit) startPollingGasFees(); - }, [currentStepGasLimit, startPollingGasFees, step]); + startPollingGasFees(); + }, [startPollingGasFees, step]); useEffect(() => { if ( !isEmpty(useGasGasFeeParamsBySpeed) && - gasFeeParams.gasFeeParamsBySpeed !== useGasGasFeeParamsBySpeed && - gasFeeParams.currentBaseFee !== useGasCurrentBlockParams.baseFeePerGas && - useGasCurrentBlockParams.baseFeePerGas + gasFeeParams?.gasFeeParamsBySpeed !== useGasGasFeeParamsBySpeed && + gasFeeParams?.currentBaseFee !== + useGasCurrentBlockParams?.baseFeePerGas && + useGasCurrentBlockParams?.baseFeePerGas ) { setGasFeeParams({ currentBaseFee: useGasCurrentBlockParams.baseFeePerGas, @@ -477,27 +487,21 @@ export default function useENSRegistrationCosts({ yearsDuration, ]); - const gasFeeReady = useMemo( - () => - !isEmpty(useGasGasFeeParamsBySpeed) && !isEmpty(useGasCurrentBlockParams), - [useGasCurrentBlockParams, useGasGasFeeParamsBySpeed] - ); - const { isSuccess, isLoading, isIdle } = useMemo(() => { const statusQueries = queries.slice(0, 2); const isSuccess = !statusQueries.some(a => a.status !== 'success') && !!data?.estimatedRentPrice && - gasFeeReady; + isGasReady; const isLoading = statusQueries .map(({ isLoading }) => isLoading) - .reduce((a, b) => a || b) && !gasFeeReady; + .reduce((a, b) => a || b) && !isGasReady; const isIdle = statusQueries .map(({ isIdle }) => ({ isIdle })) .reduce((a, b) => a && b); return { isIdle, isLoading, isSuccess }; - }, [data?.estimatedRentPrice, gasFeeReady, queries]); + }, [data?.estimatedRentPrice, isGasReady, queries]); return { data, diff --git a/src/hooks/useENSRegistrationForm.ts b/src/hooks/useENSRegistrationForm.ts index 01835cafad4..70d51fff448 100644 --- a/src/hooks/useENSRegistrationForm.ts +++ b/src/hooks/useENSRegistrationForm.ts @@ -20,6 +20,11 @@ const errorsAtom = atom<{ [name: string]: string }>({ key: 'ensProfileForm.errors', }); +const isValidatingAtom = atom({ + default: false, + key: 'ensProfileForm.isValidating', +}); + const selectedFieldsAtom = atom({ default: [], key: 'ensProfileForm.selectedFields', @@ -45,7 +50,7 @@ const defaultInitialRecords = { const cleanFormRecords = (initialRecords: Records) => { // delete these to show an empty form if the user only have one of these set // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { ETH, avatar, cover, ...cleanFormRecords } = initialRecords; + const { ETH, avatar, header, ...cleanFormRecords } = initialRecords; // if ENS has some records, only show those if (Object.keys(cleanFormRecords).length) return initialRecords; return { ...defaultInitialRecords, ...initialRecords }; @@ -68,7 +73,7 @@ export default function useENSRegistrationForm({ updateRecordByKey, updateRecords, } = useENSRegistration(); - const { changedRecords, profileQuery } = useENSModifiedRegistration(); + const { changedRecords, isSuccess } = useENSModifiedRegistration(); // The initial records will be the existing records belonging to the profile in "edit mode", // but will be all of the records in "create mode". @@ -81,6 +86,7 @@ export default function useENSRegistrationForm({ const [errors, setErrors] = useRecoilState(errorsAtom); const [submitting, setSubmitting] = useRecoilState(submittingAtom); + const [isValidating, setIsValidating] = useRecoilState(isValidatingAtom); const [disabled, setDisabled] = useRecoilState(disabledAtom); useEffect(() => { @@ -88,10 +94,8 @@ export default function useENSRegistrationForm({ // when there are no changed records. // Note: We don't want to do this in create mode as we have the "Skip" // button. - setDisabled( - mode === REGISTRATION_MODES.EDIT ? isEmpty(changedRecords) : false - ); - }, [changedRecords, disabled, mode, setDisabled]); + setDisabled(mode === REGISTRATION_MODES.EDIT && isEmpty(changedRecords)); + }, [changedRecords, mode, setDisabled]); const [selectedFields, setSelectedFields] = useRecoilState( selectedFieldsAtom @@ -140,12 +144,17 @@ export default function useENSRegistrationForm({ }; }, {}); updateRecords(records); - } else if (mode === REGISTRATION_MODES.EDIT && !isEmpty(defaultRecords)) { - updateRecords(defaultRecords); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [isEmpty(defaultRecords), updateRecords]); + // Reset errors if changedRecords is reset + useEffect(() => { + if (isEmpty(changedRecords)) { + setErrors({}); + } + }, [changedRecords, initializeForm, setErrors]); + const onAddField = useCallback( (fieldToAdd, selectedFields) => { setSelectedFields(selectedFields); @@ -202,42 +211,50 @@ export default function useENSRegistrationForm({ const onChangeField = useCallback( ({ key, value }) => { - if (!isEmpty(errors)) { - setErrors(errors => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { [key]: _, ...newErrors } = errors; - - return newErrors; - }); + setIsValidating(true); + const validation = textRecordFields[key as ENS_RECORDS]?.validation; + if (validation) { + const { message, validator } = validation; + const isValid = !value || validator(value); + isValid + ? key in errors && + setErrors(errors => { + // omit key from errors + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { [key]: _, ...newErrors } = errors; + return newErrors; + }) + : setErrors({ ...errors, [key]: message }); } - + setIsValidating(false); setValuesMap(values => ({ ...values, [name]: { ...values?.[name], [key]: value }, })); updateRecordByKey(key, value); }, - [errors, name, setErrors, setValuesMap, updateRecordByKey] + [errors, name, setErrors, setIsValidating, setValuesMap, updateRecordByKey] ); const blurFields = useCallback(() => { updateRecords(values); }, [updateRecords, values]); + const isEmptyValues = Object.values(values).filter(x => x).length === 0; + const [isLoading, setIsLoading] = useState( - mode === REGISTRATION_MODES.EDIT && - (!profileQuery.isSuccess || isEmpty(values)) + mode === REGISTRATION_MODES.EDIT && (!isSuccess || isEmptyValues) ); useEffect(() => { if (mode === REGISTRATION_MODES.EDIT) { - if (profileQuery.isSuccess || !isEmpty(values)) { + if (isSuccess || !isEmptyValues) { setTimeout(() => setIsLoading(false), 200); } else { setIsLoading(true); } } - }, [mode, profileQuery.isSuccess, values]); + }, [mode, isSuccess, isEmptyValues]); const clearValues = useCallback(() => { setValuesMap({}); @@ -247,46 +264,17 @@ export default function useENSRegistrationForm({ const submit = useCallback( async submitFn => { - const errors = Object.entries(textRecordFields).reduce( - (currentErrors, [key, { validations }]) => { - const value = values[key as ENS_RECORDS]; - if (validations?.onSubmit?.match) { - const { value: regex, message } = - validations?.onSubmit?.match || {}; - if (regex && value && !value.match(regex)) { - return { - ...currentErrors, - [key]: message, - }; - } - } - if (validations?.onSubmit?.validate) { - const { callback, message } = validations?.onSubmit?.validate || {}; - if (value && !callback(value)) { - return { - ...currentErrors, - [key]: message, - }; - } - } - return currentErrors; - }, - {} - ); - setErrors(errors); - setSubmitting(true); - if (isEmpty(errors)) { - try { - await submitFn(); - // eslint-disable-next-line no-empty - } catch (err) {} - } + try { + await submitFn(); + // eslint-disable-next-line no-empty + } catch (err) {} + setTimeout(() => { setSubmitting(false); }, 100); }, - [setErrors, setSubmitting, values] + [setSubmitting] ); return { @@ -296,11 +284,12 @@ export default function useENSRegistrationForm({ errors, isEmpty: empty, isLoading, + isSuccess, + isValidating, onAddField, onBlurField, onChangeField, onRemoveField, - profileQuery, selectedFields, setDisabled, submit, diff --git a/src/hooks/useENSRegistrationStepHandler.tsx b/src/hooks/useENSRegistrationStepHandler.tsx index 10f6f747517..75174b8dfe4 100644 --- a/src/hooks/useENSRegistrationStepHandler.tsx +++ b/src/hooks/useENSRegistrationStepHandler.tsx @@ -1,22 +1,56 @@ import { differenceInSeconds } from 'date-fns'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -// @ts-expect-error ts-migrate(2305) FIXME: Module '"react-native-dotenv"' has no exported mem... Remove this comment to see the full error message -import { IS_TESTING } from 'react-native-dotenv'; import { useDispatch } from 'react-redux'; import usePrevious from './usePrevious'; -import { useENSRegistration } from '.'; +import { useENSRegistration, useInterval } from '.'; +import { RegistrationParameters } from '@/entities'; import { getProviderForNetwork, isHardHat, web3Provider, } from '@rainbow-me/handlers/web3'; import { + ENS_SECONDS_PADDING, ENS_SECONDS_WAIT, + ENS_SECONDS_WAIT_PROVIDER_PADDING, + ENS_SECONDS_WAIT_WITH_PADDING, REGISTRATION_MODES, REGISTRATION_STEPS, } from '@rainbow-me/helpers/ens'; import { updateTransactionRegistrationParameters } from '@rainbow-me/redux/ensRegistration'; +const checkRegisterBlockTimestamp = async ({ + registrationParameters, + secondsSinceCommitConfirmed, + isTestingHardhat, +}: { + registrationParameters: RegistrationParameters; + secondsSinceCommitConfirmed: number; + isTestingHardhat: boolean; +}) => { + try { + const provider = await getProviderForNetwork(); + const block = await provider.getBlock('latest'); + const msBlockTimestamp = getBlockMsTimestamp(block); + const secs = differenceInSeconds( + msBlockTimestamp, + registrationParameters?.commitTransactionConfirmedAt || msBlockTimestamp + ); + if ( + (secs > ENS_SECONDS_WAIT_WITH_PADDING && + secondsSinceCommitConfirmed > ENS_SECONDS_WAIT_WITH_PADDING) || + // sometimes the provider.getBlock('latest) takes a long time to update to newest block + secondsSinceCommitConfirmed > ENS_SECONDS_WAIT_PROVIDER_PADDING || + isTestingHardhat + ) { + return true; + } + return false; + } catch (e) { + return false; + } +}; + const getBlockMsTimestamp = (block: { timestamp: number }) => block.timestamp * 1000; @@ -25,6 +59,7 @@ export default function useENSRegistrationStepHandler(observer = true) { const { registrationParameters, mode } = useENSRegistration(); const commitTransactionHash = registrationParameters?.commitTransactionHash; const prevCommitTrasactionHash = usePrevious(commitTransactionHash); + const [startInterval, stopInterval, timeoutRef] = useInterval(); const timeout = useRef(); @@ -41,12 +76,12 @@ export default function useENSRegistrationStepHandler(observer = true) { ); const isTestingHardhat = useMemo( - () => IS_TESTING === 'true' && isHardHat(web3Provider.connection.url), + () => isHardHat(web3Provider.connection.url), [] ); const [readyToRegister, setReadyToRegister] = useState( - isTestingHardhat || secondsSinceCommitConfirmed > 60 + secondsSinceCommitConfirmed > ENS_SECONDS_WAIT ); // flag to wait 10 secs before we get the tx block, to be able to simulate not confirmed tx when testing @@ -61,11 +96,17 @@ export default function useENSRegistrationStepHandler(observer = true) { if (!registrationParameters.commitTransactionHash) return REGISTRATION_STEPS.COMMIT; // COMMIT tx sent, but not confirmed yet - if (!registrationParameters.commitTransactionConfirmedAt) + if ( + !registrationParameters.commitTransactionConfirmedAt || + secondsSinceCommitConfirmed <= ENS_SECONDS_PADDING + ) return REGISTRATION_STEPS.WAIT_COMMIT_CONFIRMATION; // COMMIT tx was confirmed but 60 secs haven't passed yet // or current block is not 60 secs ahead of COMMIT tx block - if (secondsSinceCommitConfirmed < ENS_SECONDS_WAIT || !readyToRegister) + if ( + secondsSinceCommitConfirmed < ENS_SECONDS_WAIT_WITH_PADDING || + !readyToRegister + ) return REGISTRATION_STEPS.WAIT_ENS_COMMITMENT; return REGISTRATION_STEPS.REGISTER; }, [ @@ -90,7 +131,9 @@ export default function useENSRegistrationStepHandler(observer = true) { const timeDifference = isTestingHardhat ? now - msBlockTimestamp : 0; const commitTransactionConfirmedAt = msBlockTimestamp + timeDifference; const secs = differenceInSeconds(now, commitTransactionConfirmedAt); - setSecondsSinceCommitConfirmed(secs); + setSecondsSinceCommitConfirmed( + secondsSinceCommitConfirmed < 0 ? 0 : secs + ); dispatch( updateTransactionRegistrationParameters({ commitTransactionConfirmedAt, @@ -101,7 +144,13 @@ export default function useENSRegistrationStepHandler(observer = true) { shouldLoopForConfirmation.current = false; } return confirmed; - }, [observer, dispatch, isTestingHardhat, commitTransactionHash]); + }, [ + observer, + commitTransactionHash, + isTestingHardhat, + secondsSinceCommitConfirmed, + dispatch, + ]); const startPollingWatchCommitTransaction = useCallback(async () => { if (observer) return; @@ -135,57 +184,69 @@ export default function useENSRegistrationStepHandler(observer = true) { ]); useEffect(() => { - if (observer) return; - if (registrationStep === REGISTRATION_STEPS.WAIT_COMMIT_CONFIRMATION) { + if ( + !observer && + registrationStep === REGISTRATION_STEPS.WAIT_COMMIT_CONFIRMATION + ) { startPollingWatchCommitTransaction(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [observer, registrationStep]); useEffect(() => { - if (observer) return; - let interval: NodeJS.Timer; - if (registrationStep === REGISTRATION_STEPS.WAIT_ENS_COMMITMENT) { - interval = setInterval(() => { - setSecondsSinceCommitConfirmed((seconds: any) => seconds + 1); - }, 1000); + if ( + !observer && + !timeoutRef.current && + ((registrationParameters.commitTransactionConfirmedAt && + registrationStep === REGISTRATION_STEPS.WAIT_COMMIT_CONFIRMATION) || + registrationStep === REGISTRATION_STEPS.WAIT_ENS_COMMITMENT) + ) { + startInterval( + () => setSecondsSinceCommitConfirmed((seconds: any) => seconds + 1), + 1000 + ); } - return () => clearInterval(interval); - }, [observer, registrationStep, secondsSinceCommitConfirmed]); + }, [ + observer, + readyToRegister, + registrationParameters.commitTransactionConfirmedAt, + registrationParameters.commitTransactionHash, + registrationStep, + startInterval, + timeoutRef, + ]); useEffect(() => { - if (observer) return; // we need to check from blocks if the time has passed or not - const checkRegisterBlockTimestamp = async () => { - try { - const provider = await getProviderForNetwork(); - const block = await provider.getBlock('latest'); - const msBlockTimestamp = getBlockMsTimestamp(block); - const secs = differenceInSeconds( - msBlockTimestamp, - registrationParameters?.commitTransactionConfirmedAt || - msBlockTimestamp - ); - if (secs > ENS_SECONDS_WAIT) setReadyToRegister(true); - // eslint-disable-next-line no-empty - } catch (e) {} - }; - if (secondsSinceCommitConfirmed >= ENS_SECONDS_WAIT) { - checkRegisterBlockTimestamp(); + if ( + !observer && + secondsSinceCommitConfirmed % 2 === 0 && + secondsSinceCommitConfirmed >= ENS_SECONDS_WAIT && + !readyToRegister + ) { + const checkIfReadyToRegister = async () => { + const readyToRegister = await checkRegisterBlockTimestamp({ + isTestingHardhat, + registrationParameters, + secondsSinceCommitConfirmed, + }); + setReadyToRegister(readyToRegister); + }; + checkIfReadyToRegister(); } }, [ isTestingHardhat, observer, - registrationParameters?.commitTransactionConfirmedAt, - registrationStep, + readyToRegister, + registrationParameters, secondsSinceCommitConfirmed, ]); useEffect( () => () => { - !observer && timeout.current && clearTimeout(timeout.current); + !observer && stopInterval(); }, - [observer] + [observer, stopInterval] ); return { secondsSinceCommitConfirmed, diff --git a/src/hooks/useENSResolveName.ts b/src/hooks/useENSResolveName.ts deleted file mode 100644 index cc39d92e524..00000000000 --- a/src/hooks/useENSResolveName.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { useQuery } from 'react-query'; -import { - getResolveName, - saveResolveName, -} from '@rainbow-me/handlers/localstorage/ens'; -import { web3Provider } from '@rainbow-me/handlers/web3'; - -export default function useENSResolveName(ensName: string) { - return useQuery(['resolve-name', ensName], async () => { - const cachedAddress = await getResolveName(ensName); - if (cachedAddress) return cachedAddress; - - const address = await web3Provider.resolveName(ensName); - address && saveResolveName(ensName, address); - return address; - }); -} diff --git a/src/hooks/useENSResolver.ts b/src/hooks/useENSResolver.ts new file mode 100644 index 00000000000..5392dbec67a --- /dev/null +++ b/src/hooks/useENSResolver.ts @@ -0,0 +1,47 @@ +import { useQuery } from 'react-query'; +import { fetchResolver } from '@rainbow-me/handlers/ens'; +import { getENSData, saveENSData } from '@rainbow-me/handlers/localstorage/ens'; +import { queryClient } from '@rainbow-me/react-query/queryClient'; +import { QueryConfig, UseQueryData } from '@rainbow-me/react-query/types'; +import { ensPublicResolverAddress } from '@rainbow-me/references'; + +export const ensResolverQueryKey = (name: string) => ['ens-resolver', name]; + +const STALE_TIME = 10000; + +export async function fetchENSResolver(name: string) { + const cachedResolver = await getENSData('resolver', name); + if (cachedResolver) { + queryClient.setQueryData(ensResolverQueryKey(name), cachedResolver); + } + const resolver = await fetchResolver(name); + const data = { + address: resolver?.address, + type: resolver?.address === ensPublicResolverAddress ? 'default' : 'custom', + }; + saveENSData('resolver', name, data); + return data; +} + +export async function prefetchENSResolver(name: string) { + queryClient.prefetchQuery( + ensResolverQueryKey(name), + async () => fetchENSResolver(name), + { staleTime: STALE_TIME } + ); +} + +export default function useENSResolver( + name: string, + config?: QueryConfig +) { + return useQuery>( + ensResolverQueryKey(name), + async () => fetchENSResolver(name), + { + ...config, + // Data will be stale for 10s to avoid dupe queries + staleTime: STALE_TIME, + } + ); +} diff --git a/src/hooks/useENSSearch.ts b/src/hooks/useENSSearch.ts index f0ce5984450..6bf504ac9ad 100644 --- a/src/hooks/useENSSearch.ts +++ b/src/hooks/useENSSearch.ts @@ -1,12 +1,13 @@ import { format } from 'date-fns'; -import { useCallback, useMemo } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useQuery } from 'react-query'; -import { useAccountSettings } from '.'; +import { useAccountSettings, useENSLocalTransactions } from '.'; import { fetchRegistrationDate } from '@rainbow-me/handlers/ens'; import { ENS_DOMAIN, formatRentPrice, getAvailable, + getENSRegistrarControllerContract, getNameExpires, getRentPrice, } from '@rainbow-me/helpers/ens'; @@ -26,10 +27,33 @@ export default function useENSSearch({ yearsDuration?: number; name: string; }) { + const [contract, setContract]: any = useState(null); + + useEffect(() => { + const getContract = async () => { + const theContract = await getENSRegistrarControllerContract(); + setContract(theContract); + }; + if (!contract) { + getContract(); + } + }, [contract, setContract]); + const name = inputName.replace(ENS_DOMAIN, ''); const { nativeCurrency } = useAccountSettings(); + + const { + commitTransactionHash, + confirmedRegistrationTransaction, + pendingRegistrationTransaction, + } = useENSLocalTransactions({ + name: `${name}${ENS_DOMAIN}`, + }); + const isValidLength = useMemo(() => name.length > 2, [name.length]); + const duration = yearsDuration * timeUnits.secs.year; + const getRegistrationValues = useCallback(async () => { const ensValidation = validateENS(`${name}${ENS_DOMAIN}`, { includeSubdomains: false, @@ -43,8 +67,10 @@ export default function useENSSearch({ }; } - const isAvailable = await getAvailable(name); - const rentPrice = await getRentPrice(name, duration); + const [isAvailable, rentPrice] = await Promise.all([ + getAvailable(name, contract), + getRentPrice(name, duration, contract), + ]); const nativeAssetPrice = ethereumUtils.getPriceOfNativeAssetForNetwork( Network.mainnet ); @@ -54,37 +80,90 @@ export default function useENSSearch({ nativeCurrency, nativeAssetPrice ); + if (isAvailable) { + if (confirmedRegistrationTransaction) { + return { + available: false, + pending: false, + valid: true, + }; + } + + if (pendingRegistrationTransaction) { + return { + available: false, + pending: true, + rentPrice: formattedRentPrice, + valid: true, + }; + } + + if (commitTransactionHash) { + return { + pending: true, + rentPrice: formattedRentPrice, + valid: true, + }; + } + return { - available: isAvailable, + available: true, rentPrice: formattedRentPrice, valid: true, }; } else { - // we need the expiration and registration date when is not available - const registrationDate = await fetchRegistrationDate(name + ENS_DOMAIN); - const nameExpires = await getNameExpires(name); + const [registrationDate, nameExpires] = await Promise.all([ + fetchRegistrationDate(name + ENS_DOMAIN), + getNameExpires(name), + ]); + const formattedRegistrarionDate = formatTime(registrationDate, false); const formattedExpirationDate = formatTime(nameExpires); return { - available: isAvailable, + available: false, expirationDate: formattedExpirationDate, registrationDate: formattedRegistrarionDate, rentPrice: formattedRentPrice, valid: true, }; } - }, [duration, name, nativeCurrency, yearsDuration]); + }, [ + name, + pendingRegistrationTransaction, + commitTransactionHash, + confirmedRegistrationTransaction, + contract, + duration, + yearsDuration, + nativeCurrency, + ]); const { data, status, isIdle, isLoading } = useQuery( - ['getRegistrationValues', [duration, name, nativeCurrency]], + [ + 'getRegistrationValues', + [ + duration, + name, + nativeCurrency, + yearsDuration, + commitTransactionHash, + pendingRegistrationTransaction, + confirmedRegistrationTransaction, + ], + ], getRegistrationValues, - { enabled: isValidLength, retry: 0, staleTime: Infinity } + { + enabled: isValidLength && Boolean(contract), + retry: 0, + staleTime: Infinity, + } ); const isAvailable = status === 'success' && data?.available === true; const isRegistered = status === 'success' && data?.available === false; + const isPending = status === 'success' && data?.pending === true; const isInvalid = status === 'success' && !data?.valid; return { @@ -93,6 +172,7 @@ export default function useENSSearch({ isIdle, isInvalid, isLoading, + isPending, isRegistered, }; } diff --git a/src/hooks/useENSUniqueToken.ts b/src/hooks/useENSUniqueToken.ts new file mode 100644 index 00000000000..8fd4cac2184 --- /dev/null +++ b/src/hooks/useENSUniqueToken.ts @@ -0,0 +1,22 @@ +import { UniqueAsset } from '@/entities'; +import { isENSNFTRecord, parseENSNFTRecord } from '@/utils'; + +/** @description Retrieves the unique token corresponding to an ENS NFT record. */ +export default function useENSUniqueToken({ + uniqueTokens, + value, +}: { + uniqueTokens?: UniqueAsset[]; + value?: string; +}) { + if (!value || !isENSNFTRecord(value)) return undefined; + const { contractAddress, tokenId } = parseENSNFTRecord(value); + const uniqueToken = uniqueTokens?.find(token => { + return ( + token.asset_contract.address?.toLowerCase() === + contractAddress?.toLowerCase() && + token.id?.toLowerCase() === tokenId?.toLowerCase() + ); + }); + return uniqueToken; +} diff --git a/src/hooks/useExternalWalletSectionsData.ts b/src/hooks/useExternalWalletSectionsData.ts index 51da9acae92..99860d242d1 100644 --- a/src/hooks/useExternalWalletSectionsData.ts +++ b/src/hooks/useExternalWalletSectionsData.ts @@ -1,18 +1,25 @@ import { useMemo } from 'react'; +import { AssetListType } from '../components/asset-list/RecyclerAssetList2'; +import useFetchHiddenTokens from './useFetchHiddenTokens'; import useFetchShowcaseTokens from './useFetchShowcaseTokens'; import useFetchUniqueTokens from './useFetchUniqueTokens'; import { buildBriefUniqueTokenList } from '@rainbow-me/helpers/assets'; export default function useExternalWalletSectionsData({ address, + infinite = false, + type, }: { address?: string; + infinite?: boolean; + type?: AssetListType; }) { const { data: uniqueTokens, isLoading: isUniqueTokensLoading, isSuccess: isUniqueTokensSuccess, - } = useFetchUniqueTokens({ address }); + } = useFetchUniqueTokens({ address, infinite }); + const { data: hiddenTokens } = useFetchHiddenTokens({ address }); const { data: showcaseTokens } = useFetchShowcaseTokens({ address }); const sellingTokens = useMemo( @@ -23,9 +30,15 @@ export default function useExternalWalletSectionsData({ const briefSectionsData = useMemo( () => uniqueTokens - ? buildBriefUniqueTokenList(uniqueTokens, showcaseTokens, sellingTokens) + ? buildBriefUniqueTokenList( + uniqueTokens, + showcaseTokens, + sellingTokens, + hiddenTokens, + type + ) : [], - [uniqueTokens, showcaseTokens, sellingTokens] + [uniqueTokens, showcaseTokens, sellingTokens, hiddenTokens, type] ); return { diff --git a/src/hooks/useFetchHiddenTokens.ts b/src/hooks/useFetchHiddenTokens.ts new file mode 100644 index 00000000000..1e0ca816d0d --- /dev/null +++ b/src/hooks/useFetchHiddenTokens.ts @@ -0,0 +1,39 @@ +import { useQuery } from 'react-query'; +import useAccountSettings from './useAccountSettings'; +import { getHiddenTokens } from '@rainbow-me/handlers/localstorage/accountLocal'; +import { getPreference } from '@rainbow-me/model/preferences'; + +export const hiddenTokensQueryKey = ({ address }: { address?: string }) => [ + 'hidden-tokens', + address, +]; + +export default function useFetchHiddenTokens({ + address, +}: { + address?: string; +}) { + const { network } = useAccountSettings(); + + return useQuery( + hiddenTokensQueryKey({ address }), + async () => { + if (!address) return; + let hiddenTokens = await getHiddenTokens(address, network); + const hiddenTokensFromCloud = (await getPreference('hidden', address)) as + | any + | undefined; + if ( + hiddenTokensFromCloud?.hidden?.ids && + hiddenTokensFromCloud?.hidden?.ids.length > 0 + ) { + hiddenTokens = hiddenTokensFromCloud.hidden.ids; + } + + return hiddenTokens as string[]; + }, + { + enabled: Boolean(address), + } + ); +} diff --git a/src/hooks/useFetchUniqueTokens.ts b/src/hooks/useFetchUniqueTokens.ts index 22e9991c08f..ec820754b69 100644 --- a/src/hooks/useFetchUniqueTokens.ts +++ b/src/hooks/useFetchUniqueTokens.ts @@ -2,6 +2,8 @@ import { uniqBy } from 'lodash'; import { useEffect, useState } from 'react'; import { useQuery, useQueryClient } from 'react-query'; import useAccountSettings from './useAccountSettings'; +import useIsMounted from './useIsMounted'; +import { applyENSMetadataFallbackToTokens } from '@/parsers/uniqueTokens'; import { UniqueAsset } from '@rainbow-me/entities'; import { fetchEnsTokens } from '@rainbow-me/handlers/ens'; import { @@ -13,6 +15,7 @@ import { UNIQUE_TOKENS_LIMIT_PER_PAGE, UNIQUE_TOKENS_LIMIT_TOTAL, } from '@rainbow-me/handlers/opensea-api'; +import { fetchPoaps } from '@rainbow-me/handlers/poap'; import { Network } from '@rainbow-me/helpers/networkTypes'; export const uniqueTokensQueryKey = ({ address }: { address?: string }) => [ @@ -20,16 +23,19 @@ export const uniqueTokensQueryKey = ({ address }: { address?: string }) => [ address, ]; -const STALE_TIME = 10000; - export default function useFetchUniqueTokens({ address, + infinite = false, + staleTime = 10000, }: { address?: string; + infinite?: boolean; + staleTime?: number; }) { const { network } = useAccountSettings(); + const mounted = useIsMounted(); - const [shouldFetchMore, setShouldFetchMore] = useState(false); + const [shouldFetchMore, setShouldFetchMore] = useState(); // Get unique tokens from device storage const [hasStoredTokens, setHasStoredTokens] = useState(false); @@ -62,13 +68,17 @@ export default function useFetchUniqueTokens({ uniqueTokens = await apiGetAccountUniqueTokens(network, address, 0); } - setShouldFetchMore(true); + // If there are any "unknown" ENS names, fallback to the ENS + // metadata service. + uniqueTokens = await applyENSMetadataFallbackToTokens(uniqueTokens); return uniqueTokens; }, { enabled: Boolean(address), - staleTime: STALE_TIME, + onSuccess: () => + shouldFetchMore === undefined ? setShouldFetchMore(true) : null, + staleTime, } ); const uniqueTokens = uniqueTokensQuery.data; @@ -87,19 +97,29 @@ export default function useFetchUniqueTokens({ page?: number; }): Promise { if ( + mounted.current && uniqueTokens?.length >= page * UNIQUE_TOKENS_LIMIT_PER_PAGE && uniqueTokens?.length < UNIQUE_TOKENS_LIMIT_TOTAL ) { - const moreUniqueTokens = await apiGetAccountUniqueTokens( + let moreUniqueTokens = await apiGetAccountUniqueTokens( network, address as string, page ); + + // If there are any "unknown" ENS names, fallback to the ENS + // metadata service. + moreUniqueTokens = await applyENSMetadataFallbackToTokens( + moreUniqueTokens + ); + if (!hasStoredTokens) { queryClient.setQueryData( uniqueTokensQueryKey({ address }), tokens => - tokens ? [...tokens, ...moreUniqueTokens] : moreUniqueTokens + tokens + ? uniqBy([...tokens, ...moreUniqueTokens], 'uniqueId') + : moreUniqueTokens ); } return fetchMore({ @@ -112,17 +132,28 @@ export default function useFetchUniqueTokens({ } // We have already fetched the first page of results – so let's fetch more! - if (shouldFetchMore && uniqueTokens && uniqueTokens.length > 0) { + if ( + infinite && + shouldFetchMore && + uniqueTokens && + uniqueTokens.length > 0 + ) { setShouldFetchMore(false); (async () => { // Fetch more Ethereum tokens until all have fetched - let tokens = await fetchMore({ - network, - // If there are stored tokens in storage, then we want - // to do a background refresh. - page: hasStoredTokens ? 0 : 1, - uniqueTokens: hasStoredTokens ? [] : uniqueTokens, - }); + let tokens = ( + await fetchMore({ + network, + // If there are stored tokens in storage, then we want + // to do a background refresh. + page: hasStoredTokens ? 0 : 1, + uniqueTokens: hasStoredTokens ? [] : uniqueTokens, + }) + ).filter((token: any) => token.familyName !== 'POAP'); + + // Fetch poaps + const poaps = (await fetchPoaps(address)) || []; + tokens = [...tokens, ...poaps]; // Fetch Polygon tokens until all have fetched const polygonTokens = await fetchMore({ network: Network.polygon }); @@ -135,7 +166,7 @@ export default function useFetchUniqueTokens({ timeAgo: { hours: 48 }, }); if (ensTokens.length > 0) { - tokens = uniqBy([...tokens, ...ensTokens], 'id'); + tokens = uniqBy([...tokens, ...ensTokens], 'uniqueId'); } if (hasStoredTokens) { @@ -155,6 +186,8 @@ export default function useFetchUniqueTokens({ uniqueTokens, queryClient, hasStoredTokens, + infinite, + mounted, ]); return uniqueTokensQuery; diff --git a/src/hooks/useFirstTransactionTimestamp.ts b/src/hooks/useFirstTransactionTimestamp.ts deleted file mode 100644 index d7106b37227..00000000000 --- a/src/hooks/useFirstTransactionTimestamp.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useQuery } from 'react-query'; -import { web3Provider } from '@rainbow-me/handlers/web3'; -import { getFirstTransactionTimestamp } from '@rainbow-me/utils/ethereumUtils'; - -export default function useFirstTransactionTimestamp({ - ensName, -}: { - ensName: string; -}) { - return useQuery( - ['first-transaction-timestamp', ensName], - async () => { - const address = await web3Provider.resolveName(ensName); - return address ? getFirstTransactionTimestamp(address) : undefined; - }, - { - cacheTime: Infinity, - enabled: Boolean(ensName), - // First transaction timestamp will obviously never be stale. - // So we won't fetch / refetch it again. - staleTime: Infinity, - } - ); -} diff --git a/src/hooks/useImportingWallet.ts b/src/hooks/useImportingWallet.ts index 16c3c045225..1a2bd0ed545 100644 --- a/src/hooks/useImportingWallet.ts +++ b/src/hooks/useImportingWallet.ts @@ -7,6 +7,7 @@ import { InteractionManager, Keyboard } from 'react-native'; import { IS_TESTING } from 'react-native-dotenv'; import { useDispatch } from 'react-redux'; import useAccountSettings from './useAccountSettings'; +import { fetchENSAvatar } from './useENSAvatar'; import useInitializeWallet from './useInitializeWallet'; import useIsWalletEthZero from './useIsWalletEthZero'; import useMagicAutofocus from './useMagicAutofocus'; @@ -17,7 +18,7 @@ import useWallets from './useWallets'; import { WrappedAlert as Alert } from '@/helpers/alert'; import { analytics } from '@rainbow-me/analytics'; import { PROFILES, useExperimentalFlag } from '@rainbow-me/config'; -import { fetchImages, fetchReverseRecord } from '@rainbow-me/handlers/ens'; +import { fetchReverseRecord } from '@rainbow-me/handlers/ens'; import { resolveUnstoppableDomain, web3Provider, @@ -116,7 +117,7 @@ export default function useImportingWallet({ showImportModal = true } = {}) { withoutStatusBar: true, }); } else { - importWallet(name, forceColor, avatarUrl); + importWallet(forceColor, name, avatarUrl); } }, [handleSetImporting, navigate, showImportModal] @@ -137,9 +138,11 @@ export default function useImportingWallet({ showImportModal = true } = {}) { // Validate ENS if (isENSAddressFormat(input)) { try { - const [address, images] = await Promise.all([ + const [address, avatar] = await Promise.all([ web3Provider.resolveName(input), - !avatarUrl && profilesEnabled && fetchImages(input), + !avatarUrl && + profilesEnabled && + fetchENSAvatar(input, { swallowError: true }), ]); if (!address) { Alert.alert(lang.t('wallet.invalid_ens_name')); @@ -148,7 +151,7 @@ export default function useImportingWallet({ showImportModal = true } = {}) { // @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message setResolvedAddress(address); name = forceEmoji ? `${forceEmoji} ${input}` : input; - avatarUrl = avatarUrl || images?.avatarUrl; + avatarUrl = avatarUrl || avatar?.imageUrl; startImportProfile(name, guardedForceColor, address, avatarUrl); analytics.track('Show wallet profile modal for ENS address', { address, @@ -181,12 +184,12 @@ export default function useImportingWallet({ showImportModal = true } = {}) { } } else if (isValidAddress(input)) { try { - const ens = await web3Provider.lookupAddress(input); + const ens = await fetchReverseRecord(input); if (ens && ens !== input) { name = forceEmoji ? `${forceEmoji} ${ens}` : ens; if (!avatarUrl && profilesEnabled) { - const images = await fetchImages(name); - avatarUrl = images?.avatarUrl; + const avatar = await fetchENSAvatar(name, { swallowError: true }); + avatarUrl = avatar?.imageUrl; } } analytics.track('Show wallet profile modal for read only wallet', { @@ -211,8 +214,10 @@ export default function useImportingWallet({ showImportModal = true } = {}) { if (ens && ens !== input) { name = forceEmoji ? `${forceEmoji} ${ens}` : ens; if (!avatarUrl && profilesEnabled) { - const images = await fetchImages(name); - avatarUrl = images?.avatarUrl; + const avatar = await fetchENSAvatar(name, { + swallowError: true, + }); + avatarUrl = avatar?.imageUrl; } } setBusy(false); diff --git a/src/hooks/useLoadAccountLateData.ts b/src/hooks/useLoadAccountLateData.ts index d36382d40cc..469efc84a31 100644 --- a/src/hooks/useLoadAccountLateData.ts +++ b/src/hooks/useLoadAccountLateData.ts @@ -2,17 +2,21 @@ import { useCallback } from 'react'; import { promiseUtils } from '../utils'; import { prefetchAccountENSDomains } from './useAccountENSDomains'; import useAccountSettings from './useAccountSettings'; +import useWallets from './useWallets'; import logger from 'logger'; export default function useLoadAccountLateData() { const { accountAddress } = useAccountSettings(); + const { isReadOnlyWallet } = useWallets(); const load = useCallback(async () => { logger.sentry('Load wallet account late data'); return promiseUtils.PromiseAllWithFails([ - prefetchAccountENSDomains({ accountAddress }), + ...(!isReadOnlyWallet + ? [prefetchAccountENSDomains({ accountAddress })] + : []), ]); - }, [accountAddress]); + }, [accountAddress, isReadOnlyWallet]); return load; } diff --git a/src/hooks/useOnAvatarPress.ts b/src/hooks/useOnAvatarPress.ts index df81cbcc4a2..7f3665cbe1a 100644 --- a/src/hooks/useOnAvatarPress.ts +++ b/src/hooks/useOnAvatarPress.ts @@ -1,12 +1,14 @@ import lang from 'i18n-js'; -import { useCallback, useMemo } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { Linking } from 'react-native'; import { useDispatch } from 'react-redux'; import { RainbowAccount } from '../model/wallet'; import { useNavigation } from '../navigation/Navigation'; import useAccountProfile from './useAccountProfile'; -import useENSProfile from './useENSProfile'; -import { prefetchENSProfileImages } from './useENSProfileImages'; +import useENSAvatar, { prefetchENSAvatar } from './useENSAvatar'; +import { prefetchENSCover } from './useENSCover'; +import useENSOwner from './useENSOwner'; +import { prefetchENSRecords } from './useENSRecords'; import useENSRegistration from './useENSRegistration'; import useImagePicker from './useImagePicker'; import useUpdateEmoji from './useUpdateEmoji'; @@ -35,10 +37,28 @@ export default () => { } = useAccountProfile(); const profilesEnabled = useExperimentalFlag(PROFILES); const profileEnabled = Boolean(accountENS); - const ensProfile = useENSProfile(accountENS, { enabled: profileEnabled }); + + const { isOwner } = useENSOwner(accountENS, { + enabled: profileEnabled && profilesEnabled, + }); + + const { data: avatar } = useENSAvatar(accountENS, { + enabled: profileEnabled && profilesEnabled, + }); + const hasENSAvatar = Boolean(avatar?.imageUrl); + const { openPicker } = useImagePicker(); + const { startRegistration } = useENSRegistration(); const { setNextEmoji } = useUpdateEmoji(); + useEffect(() => { + if (accountENS) { + prefetchENSAvatar(accountENS); + prefetchENSCover(accountENS); + prefetchENSRecords(accountENS); + } + }, [accountENS]); + const onAvatarRemovePhoto = useCallback(async () => { const newWallets = { ...wallets, @@ -111,118 +131,166 @@ export default () => { } }, [accountAddress, accountENS]); - const { startRegistration } = useENSRegistration(); + const onAvatarViewProfile = useCallback(() => { + analytics.track('Viewed ENS profile', { + category: 'profiles', + ens: accountENS, + from: 'Transaction list', + }); + navigate(Routes.PROFILE_SHEET, { + address: accountENS, + fromRoute: 'ProfileAvatar', + }); + }, [accountENS, navigate]); + + const onAvatarEditProfile = useCallback(() => { + startRegistration(accountENS, REGISTRATION_MODES.EDIT); + navigate(Routes.REGISTER_ENS_NAVIGATOR, { + ensName: accountENS, + mode: REGISTRATION_MODES.EDIT, + }); + }, [accountENS, navigate, startRegistration]); - const isENSProfile = profilesEnabled && profileEnabled && ensProfile?.isOwner; + const isReadOnly = isReadOnlyWallet && !enableActionsOnReadOnlyWallet; + + const isENSProfile = profilesEnabled && profileEnabled && isOwner; const callback = useCallback( async (buttonIndex: Number) => { - if (isENSProfile) { - if (buttonIndex === 0) { - analytics.track('Viewed ENS profile', { - category: 'profiles', - ens: accountENS, - from: 'Transaction list', - }); - navigate(Routes.PROFILE_SHEET, { - address: accountENS, - fromRoute: 'ProfileAvatar', - }); - } else if (buttonIndex === 1 && !isReadOnlyWallet) { - startRegistration(accountENS, REGISTRATION_MODES.EDIT); - navigate(Routes.REGISTER_ENS_NAVIGATOR, { - ensName: accountENS, - mode: REGISTRATION_MODES.EDIT, - }); + if (buttonIndex === 0) { + if (isENSProfile) { + if (!isReadOnly) { + onAvatarEditProfile(); + } else { + onAvatarViewProfile(); + } + } else { + if (!isReadOnly) { + onAvatarCreateProfile(); + } else { + onAvatarChooseImage(); + } } - } else { - if (buttonIndex === 0) { - onAvatarChooseImage(); - } else if (buttonIndex === 1) { - if (accountImage) { - onAvatarRemovePhoto(); + } else if (buttonIndex === 1) { + if (isENSProfile) { + if (!isReadOnly) { + onAvatarViewProfile(); + } else { + if (!hasENSAvatar) { + onAvatarChooseImage(); + } + } + } else { + if (!isReadOnly) { + onAvatarChooseImage(); + } else { + if (!accountImage) { + if (ios) { + onAvatarPickEmoji(); + } else { + setNextEmoji(); + } + } else { + onAvatarRemovePhoto(); + } + } + } + } else if (buttonIndex === 2) { + if (!hasENSAvatar) { + if (isENSProfile) { + if (!isReadOnly) { + onAvatarChooseImage(); + } else { + if (!accountImage) { + if (ios) { + onAvatarPickEmoji(); + } else { + setNextEmoji(); + } + } else { + onAvatarRemovePhoto(); + } + } } else { + if (!isReadOnly) { + if (!accountImage) { + if (ios) { + onAvatarPickEmoji(); + } else { + setNextEmoji(); + } + } else { + onAvatarRemovePhoto(); + } + } + } + } + } else if (buttonIndex === 3) { + if (!hasENSAvatar && !isReadOnly) { + if (!accountImage) { if (ios) { onAvatarPickEmoji(); } else { setNextEmoji(); } + } else { + onAvatarRemovePhoto(); } - } else if (buttonIndex === 2 && profilesEnabled) { - onAvatarCreateProfile(); } } }, [ - accountENS, accountImage, + hasENSAvatar, isENSProfile, - isReadOnlyWallet, - navigate, + isReadOnly, onAvatarChooseImage, onAvatarCreateProfile, + onAvatarEditProfile, onAvatarPickEmoji, onAvatarRemovePhoto, - profilesEnabled, - startRegistration, + onAvatarViewProfile, setNextEmoji, ] ); - const avatarActionSheetOptions = useMemo( - () => - (isENSProfile - ? [ - lang.t('profiles.profile_avatar.view_profile'), - (!isReadOnlyWallet || enableActionsOnReadOnlyWallet) && - lang.t('profiles.profile_avatar.edit_profile'), - ] - : [ - lang.t('profiles.profile_avatar.choose_from_library'), - !accountImage - ? android - ? lang.t('profiles.profile_avatar.shuffle_emoji') - : lang.t('profiles.profile_avatar.pick_emoji') - : lang.t('profiles.profile_avatar.remove_photo'), - profilesEnabled && - (!isReadOnlyWallet || enableActionsOnReadOnlyWallet) && - lang.t('profiles.profile_avatar.create_profile'), - ] - ) - .filter(option => Boolean(option)) - .concat(ios ? ['Cancel'] : []), - [accountImage, isENSProfile, isReadOnlyWallet, profilesEnabled] - ); + const avatarActionSheetOptions = (hasENSAvatar + ? [ + !isReadOnly && lang.t('profiles.profile_avatar.edit_profile'), + lang.t('profiles.profile_avatar.view_profile'), + ] + : [ + isENSProfile && + !isReadOnly && + lang.t('profiles.profile_avatar.edit_profile'), + isENSProfile && lang.t('profiles.profile_avatar.view_profile'), + !isENSProfile && + !isReadOnly && + lang.t('profiles.profile_avatar.create_profile'), + lang.t('profiles.profile_avatar.choose_from_library'), + !accountImage + ? ios + ? lang.t('profiles.profile_avatar.pick_emoji') + : lang.t('profiles.profile_avatar.shuffle_emoji') + : lang.t('profiles.profile_avatar.remove_photo'), + ] + ) + .filter(option => Boolean(option)) + .concat(ios ? ['Cancel'] : []); const onAvatarPress = useCallback(() => { - if (profileEnabled && !ensProfile?.isSuccess) return; - - if (isENSProfile) { - // Prefetch profile images - prefetchENSProfileImages({ name: accountENS }); - } - showActionSheetWithOptions( { cancelButtonIndex: avatarActionSheetOptions.length - 1, destructiveButtonIndex: - !isENSProfile && accountImage - ? avatarActionSheetOptions.length - (profilesEnabled ? 3 : 2) + !hasENSAvatar && accountImage + ? avatarActionSheetOptions.length - 2 : undefined, options: avatarActionSheetOptions, }, (buttonIndex: Number) => callback(buttonIndex) ); - }, [ - profileEnabled, - ensProfile?.isSuccess, - isENSProfile, - avatarActionSheetOptions, - accountImage, - profilesEnabled, - accountENS, - callback, - ]); + }, [avatarActionSheetOptions, hasENSAvatar, accountImage, callback]); const avatarOptions = useMemo( () => [ diff --git a/src/hooks/useOpenENSNFTHandler.ts b/src/hooks/useOpenENSNFTHandler.ts new file mode 100644 index 00000000000..4bb2ead23bb --- /dev/null +++ b/src/hooks/useOpenENSNFTHandler.ts @@ -0,0 +1,39 @@ +import { useRoute } from '@react-navigation/core'; +import useENSUniqueToken from './useENSUniqueToken'; +import { UniqueAsset } from '@/entities'; +import { useNavigation } from '@/navigation'; +import Routes from '@rainbow-me/routes'; + +/** @description Returns a press handler to open an ENS NFT in an expanded state sheet. */ +export default function useOpenENSNFTHandler({ + uniqueTokens, + value, +}: { + uniqueTokens?: UniqueAsset[]; + value?: string; +}) { + const { name } = useRoute(); + const { goBack, navigate } = useNavigation(); + const uniqueToken = useENSUniqueToken({ + uniqueTokens, + value, + }); + const onPress = uniqueToken + ? () => { + if (name === Routes.EXPANDED_ASSET_SHEET) goBack(); + navigate(Routes.EXPANDED_ASSET_SHEET, { + asset: uniqueToken, + backgroundOpacity: 1, + cornerRadius: 'device', + external: true, + springDamping: 1, + topOffset: 0, + transitionDuration: 0.25, + type: 'unique_token', + }); + } + : undefined; + return { + onPress, + }; +} diff --git a/src/hooks/useSelectImageMenu.tsx b/src/hooks/useSelectImageMenu.tsx index 77e34829545..64cf00f5293 100644 --- a/src/hooks/useSelectImageMenu.tsx +++ b/src/hooks/useSelectImageMenu.tsx @@ -231,5 +231,5 @@ export default function useSelectImageMenu({ [handleAndroidPress, handlePressMenuItem, menuItems, testID] ); - return { ContextMenu, isUploading }; + return { ContextMenu, handleSelectImage, handleSelectNFT, isUploading }; } diff --git a/src/hooks/useTrackENSProfile.ts b/src/hooks/useTrackENSProfile.ts index 5261dec3626..5109cfc508b 100644 --- a/src/hooks/useTrackENSProfile.ts +++ b/src/hooks/useTrackENSProfile.ts @@ -1,12 +1,10 @@ import { useCallback, useMemo } from 'react'; import { useQuery } from 'react-query'; +import { fetchENSRecords } from './useENSRecords'; import useWallets from './useWallets'; import { analytics } from '@rainbow-me/analytics'; import { EthereumAddress } from '@rainbow-me/entities'; -import { - fetchAccountRegistrations, - fetchProfile, -} from '@rainbow-me/handlers/ens'; +import { fetchAccountRegistrations } from '@rainbow-me/handlers/ens'; import { ENS_RECORDS } from '@rainbow-me/helpers/ens'; import walletTypes from '@rainbow-me/helpers/walletTypes'; @@ -39,16 +37,16 @@ export default function useTrackENSProfile() { for (const i in addresses) { const ens = walletNames[addresses[i]]; if (ens) { - const profile = await fetchProfile(ens); + const { records } = await fetchENSRecords(ens); const registrations = await fetchAccountRegistrations(addresses[i]); data.numberOfENSOwned += registrations?.data?.account?.registrations?.length || 0; data.numberOfENSWithAvatarOrCoverSet += - profile?.records?.avatar || profile?.records?.cover ? 1 : 0; + records?.avatar || records?.header ? 1 : 0; - data.numberOfENSWithOtherMetadataSet = Object.keys( - profile?.records || {} - ).some(key => key !== ENS_RECORDS.cover && key !== ENS_RECORDS.avatar) + data.numberOfENSWithOtherMetadataSet = Object.keys(records ?? {}).some( + key => key !== ENS_RECORDS.header && key !== ENS_RECORDS.avatar + ) ? 1 : 0; data.numberOfENSWithPrimaryNameSet += 1; diff --git a/src/hooks/useWalletSectionsData.ts b/src/hooks/useWalletSectionsData.ts index 0057f6382dc..fba89d2d50c 100644 --- a/src/hooks/useWalletSectionsData.ts +++ b/src/hooks/useWalletSectionsData.ts @@ -9,18 +9,26 @@ import useSavingsAccount from './useSavingsAccount'; import useSendableUniqueTokens from './useSendableUniqueTokens'; import useShowcaseTokens from './useShowcaseTokens'; import useSortedAccountAssets from './useSortedAccountAssets'; +import { AppState } from '@/redux/store'; import { buildBriefWalletSectionsSelector, buildWalletSectionsSelector, } from '@rainbow-me/helpers/buildWalletSections'; import { readableUniswapSelector } from '@rainbow-me/helpers/uniswapLiquidityTokenInfoSelector'; -export default function useWalletSectionsData() { +export default function useWalletSectionsData({ + type, +}: { + type?: string; +} = {}) { const sortedAccountData = useSortedAccountAssets(); const isWalletEthZero = useIsWalletEthZero(); const { language, network, nativeCurrency } = useAccountSettings(); - const uniqueTokens = useSendableUniqueTokens(); + const sendableUniqueTokens = useSendableUniqueTokens(); + const allUniqueTokens = useSelector( + (state: AppState) => state.uniqueTokens.uniqueTokens + ); const uniswap = useSelector(readableUniswapSelector); const { showcaseTokens } = useShowcaseTokens(); const { hiddenTokens } = useHiddenTokens(); @@ -46,18 +54,21 @@ export default function useWalletSectionsData() { pinnedCoins, savings, ...sortedAccountData, - ...uniqueTokens, + ...sendableUniqueTokens, ...uniswap, // @ts-expect-error ts-migrate(2698) FIXME: Spread types may only be created from object types... Remove this comment to see the full error message ...isWalletEthZero, hiddenTokens, + listType: type, showcaseTokens, }; const sectionsData = buildWalletSectionsSelector(accountInfo); const briefSectionsData = buildBriefWalletSectionsSelector(accountInfo); + const hasNFTs = allUniqueTokens.length > 0; return { + hasNFTs, isWalletEthZero, refetchSavings, shouldRefetchSavings, @@ -65,7 +76,9 @@ export default function useWalletSectionsData() { briefSectionsData, }; }, [ + allUniqueTokens, hiddenCoins, + hiddenTokens, isCoinListEdited, isWalletEthZero, language, @@ -76,9 +89,9 @@ export default function useWalletSectionsData() { savings, shouldRefetchSavings, showcaseTokens, - hiddenTokens, sortedAccountData, - uniqueTokens, + type, + sendableUniqueTokens, uniswap, ]); return walletSections; diff --git a/src/languages/_english.json b/src/languages/_english.json index 7319e72f9a5..bdbb4886f73 100644 --- a/src/languages/_english.json +++ b/src/languages/_english.json @@ -304,6 +304,30 @@ "sync_codepush": "Sync codepush, current: %{codePushVersion}" }, "discover": { + "dpi": { + "title": "DeFi Pulse Index", + "body": "All the top decentralized finance tokens in one. Track the industry.", + "view": "View" + }, + "ens_create_profile": { + "title": "Create your ENS profile", + "body": "Replace your wallet address with a profile owned entirely by you." + }, + "ens_search": { + "mini_title": "ENS", + "title_part_1": "Register a ", + "title_part_2": ".eth name" + }, + "gas": { + "average": "Average", + "gwei": "Gwei", + "high": "High", + "loading": "Loading…", + "low": "Low", + "network_fees": "Network fees", + "surging": "Surging", + "very_low": "Very low" + }, "lists": { "lists_title": "Lists", "this_list_is_empty": "This list is empty!" @@ -499,7 +523,6 @@ }, "unique_expanded": { "about": "About %{assetFamilyName}", - "advanced": "Advanced", "attributes": "Attributes", "collection_website": "Collection Website", "configuration": "Configuration", @@ -889,9 +912,10 @@ "watching": "Watching" }, "banner": { - "and_create_ens_profile": "Search available .eth names", - "register_name": "Create Your ENS Profile" + "register_name": "Create Your ENS Profile", + "and_create_ens_profile": "Search available .eth names" }, + "select_ens_name": "My ENS Names", "confirm": { "commit_button": "Hold to Commit", "confirm_purchase": "Confirm purchase", @@ -938,9 +962,9 @@ "discord": "Discord", "doge": "Dogecoin", "email": "Email", - "email_message": "Please enter a valid email", + "invalid_email": "Invalid email", "email_placeholder": "Add your email", - "email_submit_message": "Please enter a valid email", + "invalid_username": "Invalid %{app} username", "eth": "Ethereum", "github": "GitHub", "instagram": "Instagram", @@ -967,14 +991,15 @@ "username_placeholder": "username", "wallet_placeholder": "Add a %{coin} address", "website": "Website", - "website_message": "Please enter a valid website URL", - "website_placeholder": "Add your website", - "website_submit_message": "Please enter a valid email" + "invalid_website": "Invalid URL", + "website_placeholder": "Add your website" }, "details": { "add_to_contacts": "Add to Contacts", "copy_address": "Copy Address", + "open_wallet": "Open Wallet", "remove_from_contacts": "Remove from Contacts", + "share": "Share", "view_on_etherscan": "View on Etherscan" }, "edit": { @@ -996,6 +1021,7 @@ "description": "Your profile is stored directly on Ethereum and owned by you.", "title": "Stored on Ethereum" }, + "edit_name": "Edit %{name}", "use_existing_name": "Use an existing ENS name", "use_name": "Use %{name}", "wallet_address_info": { @@ -1021,21 +1047,27 @@ "view_profile": "View Profile" }, "search": { - "3_char_min": "Minimum 3 characters", - "available": "🥳 Available", - "clear": "􀅉 Clear", - "continue": "Continue 􀆊", + "header": "Find your name", "description": "Search available ENS names", + "available": "🥳 Available", + "taken": "😭 Taken", + "registered_on": "This name was last registered on %{content}", + "price": "%{content} / Year", + "expiration": "Til %{content}", + "3_char_min": "Minimum 3 characters", + "already_registering_name": "You are already registering this name", "estimated_total_cost_1": "Estimated total cost of", "estimated_total_cost_2": "with current network fees", - "expiration": "Til %{content}", - "header": "Find your name", "loading_fees": "Loading network fees…", - "price": "%{content} / Year", - "registered_on": "This name was last registered on %{content}", - "taken": "😭 Taken" + "clear": "􀅉 Clear", + "continue": "Continue 􀆊", + "finish": "Finish 􀆊", + "and_create_ens_profile": "Search available .eth names", + "register_name": "Create Your ENS Profile" }, - "select_ens_name": "My ENS Names" + "records": { + "since": "Since" + } }, "review": { "alert": { @@ -1414,10 +1446,14 @@ "transaction_status": "Failed to send transaction status" }, "checkboxes": { + "clear_profile_information": "Clear profile information", + "point_name_to_recipient": "Point this name to the recipient’s wallet address", + "transfer_control": "Transfer control to the recipient", "has_a_wallet_that_supports": "The person I’m sending to has a wallet that supports %{networkName}", "im_not_sending_to_an_exchange": "I’m not sending to an exchange" }, "complete_checks": "Complete checks", + "ens_configuration_options": "ENS configuration options", "confirm": "Confirm transaction", "max": "Max", "placeholder_title": "Placeholder", diff --git a/src/languages/_french.json b/src/languages/_french.json index 13cf23b5ab1..df50791a277 100644 --- a/src/languages/_french.json +++ b/src/languages/_french.json @@ -305,6 +305,30 @@ "sync_codepush": "Sync codepush, current: %{codePushVersion} :)" }, "discover": { + "dpi": { + "title": "DeFi Pulse Index :)", + "body": "All the top decentralized finance tokens in one. Track the industry. :)", + "view": "View :)" + }, + "ens_create_profile": { + "title": "Create your ENS profile :)", + "body": "Replace your wallet address with a profile owned entirely by you. :)" + }, + "ens_search": { + "mini_title": "ENS :)", + "title_part_1": "Register a :)", + "title_part_2": ".eth name :)" + }, + "gas": { + "average": "Average :)", + "gwei": "Gwei :)", + "high": "High :)", + "loading": "Loading… :)", + "low": "Low :)", + "network_fees": "Network fees :)", + "surging": "Surging :)", + "very_low": "Very low :)" + }, "lists": { "lists_title": "Lists :)", "this_list_is_empty": "This list is empty! :)" @@ -499,7 +523,6 @@ }, "unique_expanded": { "about": "About %{assetFamilyName} :)", - "advanced": "Advanced :)", "attributes": "Attributes :)", "collection_website": "Collection Website :)", "configuration": "Configuration :)", @@ -837,6 +860,23 @@ "and_create_ens_profile": "Search available .eth names :)", "register_name": "Create Your ENS Profile :)" }, + "select_ens_name": "My ENS Names :)", + "search": { + "header": "Find your name :)", + "description": "Search available ENS names :)", + "available": "🥳 Available :)", + "taken": "😭 Taken :)", + "registered_on": "This name was last registered on %{content} :)", + "price": "%{content} / Year :)", + "expiration": "Til %{content} :)", + "3_char_min": "Minimum 3 characters :)", + "already_registering_name": "You are already registering this name :)", + "estimated_total_cost_1": "Estimated total cost of :)", + "estimated_total_cost_2": "with current network fees :)", + "loading_fees": "Loading network fees… :)", + "clear": "􀅉 Clear :)", + "continue": "Continue 􀆊 :)" + }, "confirm": { "commit_button": "Hold to Commit :)", "confirm_purchase": "Confirm purchase :)", @@ -883,9 +923,9 @@ "discord": "Discord :)", "doge": "Dogecoin :)", "email": "Email :)", - "email_message": "Please enter a valid email :)", + "invalid_email": "Invalid email :)", "email_placeholder": "Add your email :)", - "email_submit_message": "Please enter a valid email :)", + "invalid_username": "Invalid %{app} username :)", "eth": "Ethereum :)", "github": "GitHub :)", "instagram": "Instagram :)", @@ -912,9 +952,8 @@ "username_placeholder": "username :)", "wallet_placeholder": "Add a %{coin} address :)", "website": "Website :)", - "website_message": "Please enter a valid website URL :)", - "website_placeholder": "Add your website :)", - "website_submit_message": "Please enter a valid email :)" + "invalid_website": "Invalid URL :)", + "website_placeholder": "Add your website :)" }, "details": { "add_to_contacts": "Add to Contacts :)", @@ -965,22 +1004,9 @@ "remove_photo": "Remove Photo :)", "view_profile": "View Profile :)" }, - "search": { - "3_char_min": "Minimum 3 characters :)", - "available": "🥳 Available :)", - "clear": "􀅉 Clear :)", - "continue": "Continue 􀆊 :)", - "description": "Search available ENS names :)", - "estimated_total_cost_1": "Estimated total cost of :)", - "estimated_total_cost_2": "with current network fees :)", - "expiration": "Til %{content} :)", - "header": "Find your name :)", - "loading_fees": "Loading network fees… :)", - "price": "%{content} / Year :)", - "registered_on": "This name was last registered on %{content} :)", - "taken": "😭 Taken :)" - }, - "select_ens_name": "My ENS Names :)" + "records": { + "since": "Since :)" + } }, "review": { "alert": { diff --git a/src/navigation/RegisterENSNavigator.tsx b/src/navigation/RegisterENSNavigator.tsx index 88f32354f98..92b5a80c410 100644 --- a/src/navigation/RegisterENSNavigator.tsx +++ b/src/navigation/RegisterENSNavigator.tsx @@ -11,6 +11,7 @@ import ENSIntroSheet from '../screens/ENSIntroSheet'; import ENSSearchSheet from '../screens/ENSSearchSheet'; import ScrollPagerWrapper from './ScrollPagerWrapper'; import { sharedCoolModalTopOffset } from './config'; +import { avatarMetadataAtom } from '@/components/ens-registration/RegistrationAvatar/RegistrationAvatar'; import { Box } from '@rainbow-me/design-system'; import { accentColorAtom, REGISTRATION_MODES } from '@rainbow-me/helpers/ens'; import { @@ -29,7 +30,6 @@ const renderTabBar = () => null; const renderPager = (props: any) => ( () => { removeRecordByKey('avatar'); + setAvatarMetadata(undefined); setAccentColor(colors.purple); clearValues(); clearCurrentRegistrationName(); @@ -120,6 +126,7 @@ export default function RegisterENSNavigator() { colors.purple, removeRecordByKey, setAccentColor, + setAvatarMetadata, ] ); @@ -157,6 +164,7 @@ export default function RegisterENSNavigator() { ( { - let height = params.shouldShowChecks - ? SendConfirmationSheetHeight - : SendConfirmationSheetHeight - 104; - - if (!params.isL2) { - height -= 59; - } + const height = getSendConfirmationSheetHeight(params); return { ...buildCoolModalConfig({ ...params, diff --git a/src/parsers/newTransaction.ts b/src/parsers/newTransaction.ts index e32cd011a5a..6b5597911a2 100644 --- a/src/parsers/newTransaction.ts +++ b/src/parsers/newTransaction.ts @@ -31,6 +31,8 @@ export const parseNewTransaction = async ( data, from, flashbots, + ensRegistrationName, + ensRegistration, gasLimit, gasPrice, maxFeePerGas, @@ -89,6 +91,8 @@ export const parseNewTransaction = async ( dappName, data, description, + ensRegistration, + ensRegistrationName, flashbots, from, gasLimit, diff --git a/src/rainbow-fetch/index.ts b/src/rainbow-fetch/index.ts index a2fc200f9ef..a67d8046e0e 100644 --- a/src/rainbow-fetch/index.ts +++ b/src/rainbow-fetch/index.ts @@ -39,6 +39,7 @@ export async function rainbowFetch( }, signal: controller.signal, }); + clearTimeout(id); const responseBody = await getBody(response); diff --git a/src/raps/actions/ens.ts b/src/raps/actions/ens.ts index 7e6878968d3..fef12d91b2d 100644 --- a/src/raps/actions/ens.ts +++ b/src/raps/actions/ens.ts @@ -16,6 +16,7 @@ import { NetworkTypes } from '@rainbow-me/helpers'; import { ENSRegistrationTransactionType, getENSExecutionDetails, + REGISTRATION_MODES, } from '@rainbow-me/helpers/ens'; import { dataAddNewTransaction } from '@rainbow-me/redux/data'; import { @@ -189,6 +190,36 @@ const executeSetName = async ( ); }; +const executeSetAddr = async ( + name?: string, + records?: ENSRegistrationRecords, + gasLimit?: string | null, + maxFeePerGas?: string, + maxPriorityFeePerGas?: string, + wallet?: Wallet, + nonce: number | null = null +) => { + const { contract, methodArguments, value } = await getENSExecutionDetails({ + name, + records, + type: ENSRegistrationTransactionType.SET_ADDR, + wallet, + }); + + return ( + methodArguments && + contract?.setAddr(...methodArguments, { + gasLimit: gasLimit ? toHex(gasLimit) : undefined, + maxFeePerGas: maxFeePerGas ? toHex(maxFeePerGas) : undefined, + maxPriorityFeePerGas: maxPriorityFeePerGas + ? toHex(maxPriorityFeePerGas) + : undefined, + nonce: nonce ? toHex(nonce) : undefined, + ...(value ? { value } : {}), + }) + ); +}; + const executeSetText = async ( name?: string, records?: ENSRegistrationRecords, @@ -219,6 +250,38 @@ const executeSetText = async ( ); }; +const executeReclaim = async ( + name?: string, + toAddress?: string, + records?: ENSRegistrationRecords, + gasLimit?: string | null, + maxFeePerGas?: string, + maxPriorityFeePerGas?: string, + wallet?: Wallet, + nonce: number | null = null +) => { + const { contract, methodArguments, value } = await getENSExecutionDetails({ + name, + records, + toAddress, + type: ENSRegistrationTransactionType.RECLAIM, + wallet, + }); + + return ( + methodArguments && + contract?.reclaim(...methodArguments, { + gasLimit: gasLimit ? toHex(gasLimit) : undefined, + maxFeePerGas: maxFeePerGas ? toHex(maxFeePerGas) : undefined, + maxPriorityFeePerGas: maxPriorityFeePerGas + ? toHex(maxPriorityFeePerGas) + : undefined, + nonce: nonce ? toHex(nonce) : undefined, + ...(value ? { value } : {}), + }) + ); +}; + const ensAction = async ( wallet: Wallet, actionName: string, @@ -231,7 +294,15 @@ const ensAction = async ( const { dispatch } = store; const { accountAddress: ownerAddress } = store.getState().settings; const { selectedGasFee } = store.getState().gas; - const { name, duration, rentPrice, records, salt } = parameters; + const { + name, + duration, + rentPrice, + records, + salt, + toAddress, + mode, + } = parameters; logger.log(`[${actionName}] rap for`, name); @@ -246,18 +317,23 @@ const ensAction = async ( type ); - const setRecordsType = + // when registering the ENS if we try to estimate gas for setting records + // (MULTICALL || SET_TEXT) it's going to fail if we put the account address + // since the account doesn't have the ENS yet + const notUseOwnerAddress = IS_TESTING !== 'true' && + mode === REGISTRATION_MODES.CREATE && (type === ENSRegistrationTransactionType.MULTICALL || type === ENSRegistrationTransactionType.SET_TEXT); gasLimit = await estimateENSTransactionGasLimit({ duration, name, - ownerAddress: setRecordsType ? undefined : ownerAddress, + ownerAddress: notUseOwnerAddress ? undefined : ownerAddress, records: ensRegistrationRecords, rentPrice, salt, + toAddress, type, }); } catch (e) { @@ -375,6 +451,35 @@ const ensAction = async ( category: 'profiles', }); break; + case ENSRegistrationTransactionType.SET_ADDR: + tx = await executeSetAddr( + name, + ensRegistrationRecords, + gasLimit, + maxFeePerGas, + maxPriorityFeePerGas, + wallet, + nonce + ); + analytics.track('Edited ENS records', { + category: 'profiles', + }); + break; + case ENSRegistrationTransactionType.RECLAIM: + tx = await executeReclaim( + name, + toAddress, + ensRegistrationRecords, + gasLimit, + maxFeePerGas, + maxPriorityFeePerGas, + wallet, + nonce + ); + analytics.track('Transferred ENS control', { + category: 'profiles', + }); + break; case ENSRegistrationTransactionType.SET_NAME: tx = await executeSetName( name, @@ -404,6 +509,8 @@ const ensAction = async ( amount: 0, asset: nativeAsset, data: tx.data, + ensRegistration: true, + ensRegistrationName: name, from: ownerAddress, gasLimit, hash: tx?.hash, @@ -506,6 +613,40 @@ const setNameENS = async ( ); }; +const setAddrENS = async ( + wallet: Wallet, + currentRap: Rap, + index: number, + parameters: RapENSActionParameters, + baseNonce?: number +): Promise => { + return ensAction( + wallet, + RapActionTypes.setAddrENS, + index, + parameters, + ENSRegistrationTransactionType.SET_ADDR, + baseNonce + ); +}; + +const reclaimENS = async ( + wallet: Wallet, + currentRap: Rap, + index: number, + parameters: RapENSActionParameters, + baseNonce?: number +): Promise => { + return ensAction( + wallet, + RapActionTypes.reclaimENS, + index, + parameters, + ENSRegistrationTransactionType.RECLAIM, + baseNonce + ); +}; + const setTextENS = async ( wallet: Wallet, currentRap: Rap, @@ -526,8 +667,10 @@ const setTextENS = async ( export default { commitENS, multicallENS, + reclaimENS, registerWithConfig, renewENS, + setAddrENS, setNameENS, setTextENS, }; diff --git a/src/raps/common.ts b/src/raps/common.ts index 73a6f6d7c54..cd506317ffb 100644 --- a/src/raps/common.ts +++ b/src/raps/common.ts @@ -16,6 +16,7 @@ import { createRenewENSRap, createSetNameENSRap, createSetRecordsENSRap, + createTransferENSRap, } from './registerENS'; import { createSwapAndDepositCompoundRap, @@ -36,12 +37,15 @@ import { estimateENSSetRecordsGasLimit, } from '@rainbow-me/handlers/ens'; import { ExchangeModalTypes } from '@rainbow-me/helpers'; +import { REGISTRATION_MODES } from '@rainbow-me/helpers/ens'; import logger from 'logger'; const { commitENS, registerWithConfig, multicallENS, + setAddrENS, + reclaimENS, setTextENS, setNameENS, renewENS, @@ -56,6 +60,8 @@ export enum RapActionType { registerENS = 'registerENS', multicallENS = 'multicallENS', renewENS = 'renewENS', + setAddrENS = 'setAddrENS', + reclaimENS = 'reclaimENS', setTextENS = 'setTextENS', setNameENS = 'setNameENS', } @@ -80,6 +86,8 @@ export interface RapENSActionParameters { rentPrice: string; records?: Records; salt: string; + toAddress?: string; + mode?: keyof typeof REGISTRATION_MODES; } export interface UnlockActionParameters { @@ -107,10 +115,15 @@ export interface ENSActionParameters { name: string; rentPrice: string; ownerAddress: string; + toAddress?: string; salt: string; records?: Records; setReverseRecord?: boolean; resolverAddress?: EthereumAddress; + clearRecords?: boolean; + setAddress?: boolean; + transferControl?: boolean; + mode?: keyof typeof REGISTRATION_MODES; } export interface RapActionTransaction { @@ -159,13 +172,16 @@ export const RapActionTypes = { commitENS: 'commitENS' as RapActionType, depositCompound: 'depositCompound' as RapActionType, multicallENS: 'multicallENS' as RapActionType, + reclaimENS: 'reclaimENS' as RapActionType, registerENS: 'registerENS' as RapActionType, registerWithConfigENS: 'registerWithConfigENS' as RapActionType, renewENS: 'renewENS' as RapActionType, + setAddrENS: 'setAddrENS' as RapActionType, setNameENS: 'setNameENS' as RapActionType, setRecordsENS: 'setRecordsENS' as RapActionType, setTextENS: 'setTextENS' as RapActionType, swap: 'swap' as RapActionType, + transferENS: 'transferENS' as RapActionType, unlock: 'unlock' as RapActionType, withdrawCompound: 'withdrawCompound' as RapActionType, }; @@ -209,6 +225,8 @@ const createENSRapByType = ( return createSetNameENSRap(ensRegistrationParameters); case RapActionTypes.setRecordsENS: return createSetRecordsENSRap(ensRegistrationParameters); + case RapActionTypes.transferENS: + return createTransferENSRap(ensRegistrationParameters); case RapActionTypes.commitENS: default: return createCommitENSRap(ensRegistrationParameters); @@ -276,10 +294,14 @@ const findENSActionByType = (type: RapActionType) => { return registerWithConfig; case RapActionTypes.multicallENS: return multicallENS; + case RapActionTypes.setAddrENS: + return setAddrENS; case RapActionTypes.setTextENS: return setTextENS; case RapActionTypes.setNameENS: return setNameENS; + case RapActionTypes.reclaimENS: + return reclaimENS; case RapActionTypes.renewENS: return renewENS; default: @@ -371,8 +393,11 @@ const getRapTypeFromActionType = (actionType: RapActionType) => { case RapActionTypes.multicallENS: case RapActionTypes.renewENS: case RapActionTypes.setNameENS: + case RapActionTypes.setAddrENS: + case RapActionTypes.reclaimENS: case RapActionTypes.setTextENS: case RapActionTypes.setRecordsENS: + case RapActionTypes.transferENS: return RAP_TYPE.ENS; } return ''; @@ -401,10 +426,11 @@ export const executeRap = async ( label: rapName, }); + let nonce = parameters?.nonce; + logger.log('[common - executing rap]: actions', actions); if (actions.length) { const firstAction = actions[0]; - const nonce = parameters?.nonce; const { baseNonce, errorMessage } = await executeAction( firstAction, wallet, @@ -418,6 +444,7 @@ export const executeRap = async ( const action = actions[index]; await executeAction(action, wallet, rap, index, rapName, baseNonce); } + nonce = baseNonce + actions.length - 1; callback(true); } else { // Callback with failure state @@ -430,6 +457,8 @@ export const executeRap = async ( label: rapName, }); logger.log('[common - executing rap] finished execute rap function'); + + return { nonce }; }; export const createNewRap = (actions: RapAction[]) => { diff --git a/src/raps/registerENS.ts b/src/raps/registerENS.ts index ad92c4a1660..99ae7240249 100644 --- a/src/raps/registerENS.ts +++ b/src/raps/registerENS.ts @@ -1,3 +1,5 @@ +import { AddressZero } from '@ethersproject/constants'; +import { isEmpty } from 'lodash'; import { createNewENSAction, createNewRap, @@ -5,11 +7,14 @@ import { RapActionTypes, RapENSAction, } from './common'; +import { Records } from '@rainbow-me/entities'; import { formatRecordsForTransaction, + getRapActionTypeForTxType, + getTransactionTypeForRecords, recordsForTransactionAreValid, - shouldUseMulticallTransaction, } from '@rainbow-me/handlers/ens'; +import { ENS_RECORDS } from '@rainbow-me/helpers/ens'; export const createSetRecordsENSRap = async ( ensActionParameters: ENSActionParameters @@ -21,16 +26,25 @@ export const createSetRecordsENSRap = async ( ); const validRecords = recordsForTransactionAreValid(ensRegistrationRecords); if (validRecords) { - const shouldUseMulticall = shouldUseMulticallTransaction( - ensRegistrationRecords - ); - const recordsAction = createNewENSAction( - shouldUseMulticall - ? RapActionTypes.multicallENS - : RapActionTypes.setTextENS, + const txType = getTransactionTypeForRecords(ensRegistrationRecords); + if (txType) { + const rapActionType = getRapActionTypeForTxType(txType); + if (rapActionType) { + const recordsAction = createNewENSAction( + rapActionType, + ensActionParameters + ); + actions = actions.concat(recordsAction); + } + } + } + + if (ensActionParameters.setReverseRecord) { + const setName = createNewENSAction( + RapActionTypes.setNameENS, ensActionParameters ); - actions = actions.concat(recordsAction); + actions = actions.concat(setName); } // create the overall rap @@ -54,16 +68,17 @@ export const createRegisterENSRap = async ( ); const validRecords = recordsForTransactionAreValid(ensRegistrationRecords); if (validRecords) { - const shouldUseMulticall = shouldUseMulticallTransaction( - ensRegistrationRecords - ); - const recordsAction = createNewENSAction( - shouldUseMulticall - ? RapActionTypes.multicallENS - : RapActionTypes.setTextENS, - ensActionParameters - ); - actions = actions.concat(recordsAction); + const txType = getTransactionTypeForRecords(ensRegistrationRecords); + if (txType) { + const rapActionType = getRapActionTypeForTxType(txType); + if (rapActionType) { + const recordsAction = createNewENSAction( + rapActionType, + ensActionParameters + ); + actions = actions.concat(recordsAction); + } + } } if (ensActionParameters.setReverseRecord) { @@ -111,6 +126,74 @@ export const createSetNameENSRap = async ( return newRap; }; +export const createTransferENSRap = async ( + ensActionParameters: ENSActionParameters +) => { + let actions: RapENSAction[] = []; + + const { + clearRecords, + records, + setAddress, + transferControl, + toAddress, + } = ensActionParameters; + + if (clearRecords) { + const emptyRecords = Object.keys(records ?? {}).reduce( + (records, recordKey) => ({ + ...records, + // Use zero address for ETH record as an empty string throws an error + [recordKey]: recordKey === ENS_RECORDS.ETH ? AddressZero : '', + }), + {} + ); + + let newRecords: Records = emptyRecords; + if (setAddress && toAddress) { + newRecords = { + ...newRecords, + ETH: toAddress, + }; + } + + const ensRegistrationRecords = formatRecordsForTransaction(newRecords); + const validRecords = + !isEmpty(emptyRecords) && + recordsForTransactionAreValid(ensRegistrationRecords); + if (validRecords) { + const txType = getTransactionTypeForRecords(ensRegistrationRecords); + if (txType) { + const rapActionType = getRapActionTypeForTxType(txType); + if (rapActionType) { + const recordsAction = createNewENSAction(rapActionType, { + ...ensActionParameters, + records: newRecords, + }); + actions = actions.concat(recordsAction); + } + } + } + } else if (setAddress) { + const setName = createNewENSAction(RapActionTypes.setAddrENS, { + ...ensActionParameters, + records: { ETH: toAddress }, + }); + actions = actions.concat(setName); + } + if (transferControl && toAddress) { + const transferControl = createNewENSAction(RapActionTypes.reclaimENS, { + ...ensActionParameters, + toAddress, + }); + actions = actions.concat(transferControl); + } + + // create the overall rap + const newRap = createNewRap(actions); + return newRap; +}; + export const createCommitENSRap = async ( ENSActionParameters: ENSActionParameters ) => { diff --git a/src/redux/data.ts b/src/redux/data.ts index e10763973ff..ffe403cd97f 100644 --- a/src/redux/data.ts +++ b/src/redux/data.ts @@ -23,6 +23,7 @@ import { decrementNonce, incrementNonce } from './nonceManager'; import { AppGetState, AppState } from './store'; import { uniqueTokensRefreshState } from './uniqueTokens'; import { uniswapUpdateLiquidityTokens } from './uniswapLiquidity'; +import { fetchWalletENSAvatars, fetchWalletNames } from './wallets'; import { AssetTypes, NativeCurrencyKeys, @@ -1378,6 +1379,13 @@ export const dataWatchPendingTransactions = ( internalType: tx.type, }); } + if (tx?.ensRegistration) { + const fetchWalletENSData = async () => { + await dispatch(fetchWalletENSAvatars()); + dispatch(fetchWalletNames()); + }; + fetchWalletENSData(); + } const minedAt = Math.floor(Date.now() / 1000); txStatusesDidChange = true; let receipt; diff --git a/src/redux/ensRegistration.ts b/src/redux/ensRegistration.ts index a03b8deeb88..21af1f7ccf0 100644 --- a/src/redux/ensRegistration.ts +++ b/src/redux/ensRegistration.ts @@ -381,15 +381,18 @@ export const saveCommitRegistrationParameters = ( ensRegistration: { registrations, currentRegistrationName }, settings: { accountAddress }, } = getState(); + const registrationName = + (registrationParameters as RegistrationParameters)?.name || + currentRegistrationName; const lcAccountAddress = accountAddress.toLowerCase(); const accountRegistrations = registrations?.[lcAccountAddress] || {}; - const registration = accountRegistrations[currentRegistrationName] || {}; + const registration = accountRegistrations[registrationName] || {}; const updatedEnsRegistrationManager = { registrations: { ...registrations, [lcAccountAddress]: { ...accountRegistrations, - [currentRegistrationName]: { + [registrationName]: { ...registration, ...registrationParameters, }, diff --git a/src/redux/showcaseTokens.ts b/src/redux/showcaseTokens.ts index dd22a58f5a5..d27954717aa 100644 --- a/src/redux/showcaseTokens.ts +++ b/src/redux/showcaseTokens.ts @@ -98,6 +98,7 @@ export const showcaseTokensLoadState = () => async ( // if web data is enabled, fetch values from cloud const pref = await getWebDataEnabled(accountAddress, network); + if (pref) { const showcaseTokensFromCloud = (await getPreference( 'showcase', diff --git a/src/redux/wallets.ts b/src/redux/wallets.ts index cbf949f896b..de6f21ddc87 100644 --- a/src/redux/wallets.ts +++ b/src/redux/wallets.ts @@ -11,6 +11,7 @@ import { } from '../handlers/localstorage/walletNames'; import WalletBackupTypes from '../helpers/walletBackupTypes'; import WalletTypes from '../helpers/walletTypes'; +import { fetchENSAvatar } from '../hooks/useENSAvatar'; import { hasKey } from '../model/keychain'; import { PreferenceActionType, setPreference } from '../model/preferences'; import { @@ -39,7 +40,7 @@ import { import { settingsUpdateAccountAddress } from './settings'; import { updateWebDataEnabled } from './showcaseTokens'; import { AppGetState, AppState } from './store'; -import { fetchImages, fetchReverseRecord } from '@rainbow-me/handlers/ens'; +import { fetchReverseRecord } from '@rainbow-me/handlers/ens'; import { WalletLoadingState } from '@rainbow-me/helpers/walletLoadingStates'; import { lightModeThemeColors } from '@rainbow-me/styles'; @@ -396,40 +397,56 @@ export const getWalletENSAvatars = async ( | undefined; let promises: Promise<{ account: RainbowAccount; - avatarChanged: boolean; + ensChanged: boolean; key: string; }>[] = []; walletKeys.forEach(key => { const wallet = wallets![key]; const innerPromises = wallet?.addresses?.map(async account => { - const ens = - (await fetchReverseRecord(account.address)) || - walletNames[account.address]; + const ens = await fetchReverseRecord(account.address); + const currentENSName = walletNames[account.address]; if (ens) { - const images = await fetchImages(ens); - const newImage = - typeof images?.avatarUrl === 'string' && - images?.avatarUrl !== account?.image - ? images?.avatarUrl - : account.image; + const isNewEnsName = currentENSName !== ens; + const avatar = await fetchENSAvatar(ens); + const newImage = avatar?.imageUrl || null; return { account: { ...account, image: newImage, + label: isNewEnsName ? ens : account.label, }, - avatarChanged: newImage !== account.image, + ensChanged: newImage !== account.image || isNewEnsName, + key, + }; + } else if (currentENSName) { + // if user had an ENS but now is gone + return { + account: { + ...account, + image: + account.image?.startsWith('~') || + account.image?.startsWith('file') + ? account.image + : null, // if the user had an ens but the image it was a local image + label: '', + }, + ensChanged: true, key, }; } else { - return { account, avatarChanged: false, key }; + return { + account, + ensChanged: false, + key, + }; } }); promises = promises.concat(innerPromises); }); const newAccounts = await Promise.all(promises); - newAccounts.forEach(({ account, key, avatarChanged }) => { - if (!avatarChanged) return; + newAccounts.forEach(({ account, key, ensChanged }) => { + if (!ensChanged) return; const addresses = wallets?.[key]?.addresses; const index = addresses?.findIndex( ({ address }) => address === account.address diff --git a/src/references/ens-intro-marquee-names.json b/src/references/ens-intro-marquee-names.json index d02414837fb..73d75118b30 100644 --- a/src/references/ens-intro-marquee-names.json +++ b/src/references/ens-intro-marquee-names.json @@ -14,5 +14,7 @@ "cantino.eth", "jstn.eth", "notben.eth", - "sarahguo.eth" -] \ No newline at end of file + "sarahguo.eth", + "osdnk.eth", + "skillet.eth" +] diff --git a/src/references/ens/ENSPublicResolver.json b/src/references/ens/ENSPublicResolver.json index fff98324196..fb4bbe7bbb5 100644 --- a/src/references/ens/ENSPublicResolver.json +++ b/src/references/ens/ENSPublicResolver.json @@ -633,26 +633,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "constant": false, - "inputs": [ - { - "internalType": "bytes32", - "name": "node", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "a", - "type": "address" - } - ], - "name": "setAddr", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, { "constant": false, "inputs": [ diff --git a/src/screens/AvatarBuilder.js b/src/screens/AvatarBuilder.js index 960bcea80f4..8fdcc6be257 100644 --- a/src/screens/AvatarBuilder.js +++ b/src/screens/AvatarBuilder.js @@ -101,7 +101,7 @@ const AvatarBuilder = ({ route: { params } }) => { }, [params.initialAccountColor, width, colors.avatarBackgrounds.length]); return ( - + } - {lang.t('wallet.label')} + + {lang.t('wallet.label')} + {!watchOnly && ( diff --git a/src/screens/ENSAssignRecordsSheet.tsx b/src/screens/ENSAssignRecordsSheet.tsx index 349e21c2c58..38188dfcc25 100644 --- a/src/screens/ENSAssignRecordsSheet.tsx +++ b/src/screens/ENSAssignRecordsSheet.tsx @@ -51,8 +51,10 @@ import { textRecordFields, } from '@rainbow-me/helpers/ens'; import { + useAccountProfile, useDimensions, useENSModifiedRegistration, + useENSRecords, useENSRegistration, useENSRegistrationCosts, useENSRegistrationForm, @@ -60,17 +62,20 @@ import { useENSSearch, useKeyboardHeight, usePersistentDominantColorFromImage, + useWalletSectionsData, } from '@rainbow-me/hooks'; import Routes from '@rainbow-me/routes'; const BottomActionHeight = ios ? 281 : 250; const BottomActionHeightSmall = 215; +const ExtraBottomPadding = 55; export default function ENSAssignRecordsSheet() { const { params } = useRoute(); const { colors } = useTheme(); const { isSmallPhone } = useDimensions(); const { name } = useENSRegistration(); + const { hasNFTs } = useWalletSectionsData(); const { images: { avatarUrl: initialAvatarUrl }, } = useENSModifiedRegistration({ @@ -93,14 +98,17 @@ export default function ENSAssignRecordsSheet() { ].map(fieldName => textRecordFields[fieldName] as TextRecordField), [] ); - const { profileQuery, isLoading } = useENSRegistrationForm({ + + const { isLoading } = useENSRegistrationForm({ defaultFields, initializeForm: true, }); + const { data: { records } = {} } = useENSRecords(name); + const isEmptyProfile = isEmpty(records); + const displayTitleLabel = params.mode !== REGISTRATION_MODES.EDIT || !isLoading; - const isEmptyProfile = isEmpty(profileQuery.data?.records); useENSRegistrationCosts({ name, @@ -173,19 +181,22 @@ export default function ENSAssignRecordsSheet() { as={ScrollView} background="body" contentContainerStyle={{ - paddingBottom: bottomActionHeight + 20, + paddingBottom: bottomActionHeight + ExtraBottomPadding, }} flexGrow={1} scrollEnabled={android} + testID={`ens-${REGISTRATION_MODES.EDIT.toLowerCase()}-records-sheet`} > { delayNext(); navigate(fromRoute); @@ -279,11 +294,13 @@ export function ENSAssignRecordsBottomActions({ navigate(Routes.ENS_CONFIRM_REGISTER_SHEET, { longFormHeight: mode === REGISTRATION_MODES.EDIT - ? ENSConfirmUpdateSheetHeight + ? ENSConfirmUpdateSheetHeight + + (accountENS !== name ? 50 : 0) + + (values.avatar ? 70 : 0) : ENSConfirmRegisterSheetHeight + (values.avatar ? 70 : 0), }); }); - }, [mode, navigate, submit, values.avatar]); + }, [accountENS, mode, name, navigate, submit, values.avatar]); const navigateToAdditionalRecords = useCallback(() => { android && Keyboard.dismiss(); @@ -293,11 +310,11 @@ export function ENSAssignRecordsBottomActions({ const [visible, setVisible] = useState(false); useEffect(() => { if (mode === REGISTRATION_MODES.EDIT) { - setTimeout(() => setVisible(profileQuery.isSuccess), 200); + setTimeout(() => setVisible(isSuccess), 200); } else { setVisible(defaultVisible); } - }, [defaultVisible, mode, profileQuery.isSuccess]); + }, [defaultVisible, mode, isSuccess]); const bottomActionHeight = isSmallPhone ? BottomActionHeightSmall @@ -331,6 +348,7 @@ export function ENSAssignRecordsBottomActions({ as={Animated.View} background="body" style={[animatedStyle, { position: 'absolute', width: '100%' }]} + testID="ens-assign-records-sheet" > @@ -364,7 +382,7 @@ export function ENSAssignRecordsBottomActions({ {lang.t('profiles.create.back')} )} - {isEmpty && mode === REGISTRATION_MODES.CREATE ? ( + {isEmptyForm && mode === REGISTRATION_MODES.CREATE ? ( (); const { name: ensName, mode } = useENSRegistration(); const { + changedRecords, images: { avatarUrl: initialAvatarUrl }, } = useENSModifiedRegistration(); const { isSmallPhone } = useDimensions(); @@ -143,7 +146,8 @@ export default function ENSConfirmRegisterSheet() { }); const [sendReverseRecord, setSendReverseRecord] = useState( - !accountProfile.accountENS + accountProfile.accountENS !== ensName && + (!isEmpty(changedRecords) || mode === REGISTRATION_MODES.EDIT) ); const { step, secondsSinceCommitConfirmed } = useENSRegistrationStepHandler( false @@ -215,8 +219,20 @@ export default function ENSConfirmRegisterSheet() { } /> ), - [REGISTRATION_STEPS.EDIT]: null, + [REGISTRATION_STEPS.EDIT]: ( + + ), [REGISTRATION_STEPS.SET_NAME]: null, + [REGISTRATION_STEPS.TRANSFER]: null, [REGISTRATION_STEPS.RENEW]: ( action(accentColor)} + secondsSinceCommitConfirmed={secondsSinceCommitConfirmed} /> ), [REGISTRATION_STEPS.WAIT_ENS_COMMITMENT]: ( @@ -247,7 +264,10 @@ export default function ENSConfirmRegisterSheet() { registrationCostsData, accentColor, sendReverseRecord, + accountProfile.accountENS, + ensName, name, + secondsSinceCommitConfirmed, onMountSecondsSinceCommitConfirmed, action, ] @@ -335,6 +355,7 @@ export default function ENSConfirmRegisterSheet() { testID={step} /> ), + [REGISTRATION_STEPS.TRANSFER]: null, [REGISTRATION_STEPS.WAIT_COMMIT_CONFIRMATION]: null, [REGISTRATION_STEPS.WAIT_ENS_COMMITMENT]: null, }), @@ -352,6 +373,7 @@ export default function ENSConfirmRegisterSheet() { useFocusEffect( useCallback(() => { + Keyboard.dismiss(); blurFields(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []) diff --git a/src/screens/ENSIntroSheet.tsx b/src/screens/ENSIntroSheet.tsx index 191d1fa2be0..c94eb060f9e 100644 --- a/src/screens/ENSIntroSheet.tsx +++ b/src/screens/ENSIntroSheet.tsx @@ -3,15 +3,13 @@ import { useRoute } from '@react-navigation/core'; import lang from 'i18n-js'; import React, { useCallback, useMemo } from 'react'; import { InteractionManager } from 'react-native'; -import { - ContextMenuButton, - MenuActionConfig, -} from 'react-native-ios-context-menu'; +import { MenuActionConfig } from 'react-native-ios-context-menu'; import LinearGradient from 'react-native-linear-gradient'; import ActivityIndicator from '../components/ActivityIndicator'; import IntroMarquee from '../components/ens-registration/IntroMarquee/IntroMarquee'; import { SheetActionButton } from '../components/sheet'; import { useNavigation } from '../navigation/Navigation'; +import ContextMenuButton from '@/components/native-context-menu/contextMenu'; import { Bleed, Box, @@ -28,14 +26,13 @@ import { import { REGISTRATION_MODES } from '@rainbow-me/helpers/ens'; import { useAccountENSDomains, - useAccountProfile, - useAccountSettings, useDimensions, + useENSAvatar, + useENSRecords, useENSRegistration, } from '@rainbow-me/hooks'; import Routes from '@rainbow-me/routes'; import { useTheme } from '@rainbow-me/theme'; -import { showActionSheetWithOptions } from '@rainbow-me/utils'; enum AnotherENSEnum { search = 'search', @@ -49,9 +46,20 @@ export default function ENSIntroSheet() { const { width: deviceWidth, height: deviceHeight } = useDimensions(); const { colors } = useTheme(); const { params } = useRoute(); - const { accountAddress } = useAccountSettings(); - const { data: domains, isLoading, isSuccess } = useAccountENSDomains(); - const { accountENS } = useAccountProfile(); + + const { + isLoading, + isFetched, + nonPrimaryDomains, + ownedDomains, + uniqueDomain, + } = useAccountENSDomains(); + const { data: ensRecords } = useENSRecords(uniqueDomain?.name || '', { + enabled: Boolean(uniqueDomain?.name), + }); + const { data: ensAvatar } = useENSAvatar(uniqueDomain?.name || '', { + enabled: Boolean(uniqueDomain?.name), + }); // We are not using `isSmallPhone` from `useDimensions` here as we // want to explicitly set a min height. @@ -60,25 +68,13 @@ export default function ENSIntroSheet() { const contentHeight = params?.contentHeight; const contentWidth = Math.min(deviceWidth - 72, 300); - const { ownedDomains, primaryDomain, nonPrimaryDomains } = useMemo(() => { - const ownedDomains = domains?.filter( - ({ owner }) => owner?.id?.toLowerCase() === accountAddress.toLowerCase() - ); - return { - nonPrimaryDomains: - ownedDomains?.filter(({ name }) => accountENS !== name) || [], - ownedDomains, - primaryDomain: ownedDomains?.find(({ name }) => accountENS === name), - }; - }, [accountAddress, accountENS, domains]); - - const uniqueDomain = useMemo(() => { - return primaryDomain - ? primaryDomain - : nonPrimaryDomains?.length === 1 - ? nonPrimaryDomains?.[0] - : null; - }, [nonPrimaryDomains, primaryDomain]); + const profileExists = useMemo( + () => + Object.keys(ensRecords?.coinAddresses || {}).length > 1 || + ensAvatar?.imageUrl || + Object.keys(ensRecords?.records || {}).length > 0, + [ensAvatar?.imageUrl, ensRecords?.coinAddresses, ensRecords?.records] + ); const { navigate } = useNavigation(); const { startRegistration } = useENSRegistration(); @@ -138,28 +134,6 @@ export default function ENSIntroSheet() { }; }, []); - const onPressAndroidActions = useCallback(() => { - const androidActions = [ - lang.t('profiles.intro.my_ens_names'), - lang.t('profiles.intro.search_new_ens'), - ] as const; - - showActionSheetWithOptions( - { - options: androidActions, - showSeparators: true, - title: '', - }, - (idx: number) => { - if (idx === 0) { - handleSelectExistingName(); - } else if (idx === 1) { - handleNavigateToSearch(); - } - } - ); - }, [handleNavigateToSearch, handleSelectExistingName]); - const handlePressMenuItem = useCallback( ({ nativeEvent: { actionKey } }) => { if (actionKey === AnotherENSEnum.my_ens) { @@ -176,6 +150,7 @@ export default function ENSIntroSheet() { background="body" paddingTop={{ custom: topPadding }} style={{ height: contentHeight }} + {...(android && { paddingBottom: { custom: 20 } })} testID="ens-intro-sheet" > @@ -248,7 +223,7 @@ export default function ENSIntroSheet() { )} - {isSuccess && ( + {isFetched && ( <> {ownedDomains?.length === 0 ? ( 0 ? ( { + finishRegistration(`${searchQuery}${ENS_DOMAIN}`); + }, [finishRegistration, searchQuery]); + useFocusEffect( useCallback(() => { debouncedSearchQuery.length >= 3 && @@ -88,6 +95,11 @@ export default function ENSSearchSheet() { }, [debouncedSearchQuery, startRegistration]) ); + const showSearchSection = !isPending && (isAvailable || isRegistered); + const showFinishButton = isPending && !isRegistered; + const showContinueButton = isAvailable; + const showClearButton = isRegistered || isInvalid; + return ( )} - {(isAvailable || isRegistered) && ( + {isPending && ( + + + + {lang.t('profiles.search.already_registering_name')} + + + + )} + {showSearchSection && ( {isRegistered ? ( - + registrationData?.expirationDate ? ( + + ) : null ) : ( )} - - {isRegistered ? ( - - {lang.t('profiles.search.registered_on', { - content: registrationData?.registrationDate, - })} - - ) : ( + {isRegistered ? ( + registrationData?.registrationDate ? ( + + + {lang.t('profiles.search.registered_on', { + content: registrationData?.registrationDate, + })} + + + ) : null + ) : ( + {registrationCostsDataIsAvailable ? ( )} - )} - + + )} )} - {isAvailable && ( + {showFinishButton && ( + + )} + {showContinueButton && ( )} - {(isRegistered || isInvalid) && ( + {showClearButton && ( { setSearchQuery(''); diff --git a/src/screens/ExplainSheet.js b/src/screens/ExplainSheet.js index d6360325623..65b7f67d409 100644 --- a/src/screens/ExplainSheet.js +++ b/src/screens/ExplainSheet.js @@ -151,27 +151,32 @@ const BACKUP_EXPLAINER = lang.t('back_up.explainers.backup', { }); const ENS_PRIMARY_NAME_EXPLAINER = - 'Setting a primary ENS name makes your Ethereum address point to your .eth name, enabling dapps to find and display it when you connect your wallet.'; + 'People will be able to type your .eth name into dApps instead of your full Ethereum address when they want to send something to you, and dApps will be able to use your .eth name and profile to display information about you. This is also known as setting your ENS name to “primary.”'; const ENS_ON_CHAIN_DATA_WARNING_EXPLAINER = 'The data you provide here will be stored on the Ethereum blockchain – meaning it will be visible to everyone and accessible by anyone. Do not share any data you are uncomfortable with publicizing.'; const ENS_ON_CHAIN_DATA_WARNING_TITLE = 'Heads up!'; -const ENS_PRIMARY_NAME_TITLE = 'What is a primary ENS name?'; +const ENS_PRIMARY_NAME_TITLE = 'What does it mean to set my ENS name?'; -const ENS_MANAGER_TITLE = `Who is the .eth manager?`; +const ENS_MANAGER_TITLE = `What is the .eth manager?`; -const ENS_MANAGER_EXPLAINER = `The manager of a .eth name registration. The manager may transfer ownership, set a resolver or TTL, as well as create or reassign subdomains`; +const ENS_MANAGER_EXPLAINER = `The manager of a .eth name is able to set and update its records, create subdomains of that name or manage its configuration. The manager is set by the owner of the .eth name and is also known as the ENS name’s controller.`; -const ENS_OWNER_TITLE = `Who is the .eth owner?`; +const ENS_OWNER_TITLE = `What is the .eth name owner?`; -const ENS_OWNER_EXPLAINER = `The owner of a .eth name registration. The owner may transfer the registration or reclaim ownership of the name in the registry if required.`; +const ENS_OWNER_EXPLAINER = `The owner of a .eth name has full and ultimate control over that name. They can transfer ownership of the name and allow someone else to manage the name for them if they choose (setting records etc). The owner is also known as the ENS name’s registrant.`; const ENS_RESOLVER_TITLE = `What is a .eth resolver?`; const ENS_RESOLVER_EXPLAINER = `A resolver is a contract that maps from name to the resource (e.g., cryptocurrency addresses, content hash, etc). Resolvers are pointed to by the resolver field of the registry.`; +const ENS_CONFIGURATION_TITLE = 'What do these options mean?'; + +const ENS_CONFIGURATION_EXPLAINER = + 'When sending an ENS name to someone else and making them the new ENS name owner, you may want to configure it for them in advance and save them a future transaction. Rainbow allows you to clear any profile information currently set for the name, configure the ENS name to point to the recipient’s address and make the recipient address the manager of the name.'; + const OPTIMISM_APP_ICON_EXPLAINER = lang.t('explain.optimism_app_icon.text'); export const explainers = (params, colors) => ({ @@ -234,19 +239,19 @@ export const explainers = (params, colors) => ({ }), }, ens_primary_name: { - extraHeight: -70, + extraHeight: 50, emoji: '❓', text: ENS_PRIMARY_NAME_EXPLAINER, title: ENS_PRIMARY_NAME_TITLE, }, ens_manager: { - extraHeight: -80, + extraHeight: -30, emoji: '❓', text: ENS_MANAGER_EXPLAINER, title: ENS_MANAGER_TITLE, }, ens_owner: { - extraHeight: -80, + extraHeight: 0, emoji: '❓', text: ENS_OWNER_EXPLAINER, title: ENS_OWNER_TITLE, @@ -257,6 +262,12 @@ export const explainers = (params, colors) => ({ text: ENS_RESOLVER_EXPLAINER, title: ENS_RESOLVER_TITLE, }, + ens_configuration: { + extraHeight: android ? 100 : 80, + emoji: '❓', + text: ENS_CONFIGURATION_EXPLAINER, + title: ENS_CONFIGURATION_TITLE, + }, ensOnChainDataWarning: { extraHeight: -30, emoji: '✋', diff --git a/src/screens/ProfileSheet.tsx b/src/screens/ProfileSheet.tsx index 8ec3e8ba166..8e0e3fea591 100644 --- a/src/screens/ProfileSheet.tsx +++ b/src/screens/ProfileSheet.tsx @@ -18,11 +18,10 @@ import { maybeSignUri } from '@rainbow-me/handlers/imgix'; import { useAccountSettings, useDimensions, - useENSProfile, - useENSProfileImages, - useENSResolveName, + useENSAddress, + useENSAvatar, + useENSFirstTransactionTimestamp, useExternalWalletSectionsData, - useFirstTransactionTimestamp, usePersistentDominantColorFromImage, } from '@rainbow-me/hooks'; import { sharedCoolModalTopOffset } from '@rainbow-me/navigation/config'; @@ -45,25 +44,25 @@ export default function ProfileSheet() { const contentHeight = deviceHeight - sharedCoolModalTopOffset; const ensName = params?.address; - const { isSuccess } = useENSProfile(ensName); - const { data: images, isFetched: isImagesFetched } = useENSProfileImages( + const { data: profileAddress, isSuccess: isAddressSuccess } = useENSAddress( ensName ); - const avatarUrl = images?.avatarUrl; + const { data: avatar, isFetched: isAvatarFetched } = useENSAvatar(ensName); - const { data: profileAddress } = useENSResolveName(ensName); + const isPreview = name === Routes.PROFILE_PREVIEW_SHEET; - // Prefetch first transaction timestamp - useFirstTransactionTimestamp({ - ensName, - }); + // Prefetch first transaction timestamp unless already fetched for intro marquee + const { + isSuccess: hasFirstTxTimestampFetched, + } = useENSFirstTransactionTimestamp(name, { enabled: !isPreview }); // Prefetch asset list const { isSuccess: hasListFetched, briefSectionsData, } = useExternalWalletSectionsData({ - address: profileAddress, + address: profileAddress || undefined, + infinite: true, }); const colorIndex = useMemo( @@ -72,7 +71,7 @@ export default function ProfileSheet() { ); const { result: dominantColor, state } = usePersistentDominantColorFromImage( - maybeSignUri(avatarUrl || '') || '' + maybeSignUri(avatar?.imageUrl ?? '') ?? '' ); const wrapperStyle = useMemo(() => ({ height: contentHeight }), [ @@ -82,14 +81,13 @@ export default function ProfileSheet() { const accentColor = // Set accent color when ENS images have fetched & dominant // color is not loading. - isImagesFetched && state !== 1 && typeof colorIndex === 'number' + isAvatarFetched && state !== 1 && typeof colorIndex === 'number' ? dominantColor || colors.avatarBackgrounds[colorIndex] || colors.appleBlue : colors.skeleton; - const enableZoomableImages = - !params.isPreview && name !== Routes.PROFILE_PREVIEW_SHEET; + const enableZoomableImages = !isPreview; useEffect(() => { if (profileAddress && accountAddress) { @@ -106,21 +104,22 @@ export default function ProfileSheet() { - + - {!isSuccess || !hasListFetched ? ( + {!isPreview && + (!isAddressSuccess || + !hasListFetched || + !hasFirstTxTimestampFetched) ? ( - + - ) : !params.isPreview ? ( + ) : ( - ) : ( - )} diff --git a/src/screens/QRScannerScreen.js b/src/screens/QRScannerScreen.js index 9985a12f10f..2c7306e8207 100644 --- a/src/screens/QRScannerScreen.js +++ b/src/screens/QRScannerScreen.js @@ -4,7 +4,6 @@ import { View } from 'react-native'; import { useIsEmulator } from 'react-native-device-info'; import { useSharedValue } from 'react-native-reanimated'; import { DiscoverSheet } from '../components/discover-sheet'; -import { FabWrapper, SearchFab } from '../components/fab'; import { BackButton, Header, HeaderHeight } from '../components/header'; import { Centered } from '../components/layout'; import { @@ -65,34 +64,12 @@ export default function QRScannerScreen() { const androidSheetPosition = useSharedValue(0); return ( - <> - - {ios ? : null} - - - - {android && ( - - - - - )} - {initializeCamera && !isEmulator && ( - - )} - - {android ? ( - - ) : ( + + {ios ? : null} + + + + {android && ( )} - - - dsRef.current?.onFabSearch?.current()} - /> - + {initializeCamera && !isEmulator && ( + + )} + + {android ? ( + + ) : ( + + + + + )} + + ); } diff --git a/src/screens/SelectENSSheet.tsx b/src/screens/SelectENSSheet.tsx index 36d08d0a766..aacac647ae1 100644 --- a/src/screens/SelectENSSheet.tsx +++ b/src/screens/SelectENSSheet.tsx @@ -16,33 +16,41 @@ import { useForegroundColor, } from '@rainbow-me/design-system'; import { + prefetchENSAvatar, + prefetchENSCover, + prefetchENSRecords, useAccountENSDomains, - useAccountProfile, - useAccountSettings, + useENSAvatar, } from '@rainbow-me/hooks'; import { ImgixImage } from '@rainbow-me/images'; import { useNavigation } from '@rainbow-me/navigation'; +import { useTheme } from '@rainbow-me/theme'; import { deviceUtils } from '@rainbow-me/utils'; export const SelectENSSheetHeight = 400; const deviceHeight = deviceUtils.dimensions.height; const rowHeight = 40; +const rowPadding = 19; const maxListHeight = deviceHeight - 220; export default function SelectENSSheet() { - const { data: accountENSDomains, isSuccess } = useAccountENSDomains(); - const { accountAddress } = useAccountSettings(); - const { accountENS } = useAccountProfile(); + const { + isSuccess, + nonPrimaryDomains, + primaryDomain, + } = useAccountENSDomains(); const secondary06 = useForegroundColor('secondary06'); - const secondary30 = useForegroundColor('secondary30'); const { goBack } = useNavigation(); const { params } = useRoute(); const handleSelectENS = useCallback( ensName => { + prefetchENSAvatar(ensName); + prefetchENSCover(ensName); + prefetchENSRecords(ensName); goBack(); params?.onSelectENS(ensName); }, @@ -50,23 +58,16 @@ export default function SelectENSSheet() { ); const ownedDomains = useMemo(() => { - const domains = accountENSDomains - ?.filter( - ({ owner, name }) => - owner?.id?.toLowerCase() === accountAddress.toLowerCase() && - accountENS !== name - ) - ?.sort((a, b) => (a.name > b.name ? 1 : -1)); - - const primaryDomain = accountENSDomains?.find( - ({ name }) => accountENS === name + const sortedNonPrimaryDomains = nonPrimaryDomains?.sort((a, b) => + a.name > b.name ? 1 : -1 ); - if (primaryDomain) domains?.unshift(primaryDomain); - return domains; - }, [accountAddress, accountENS, accountENSDomains]); + if (primaryDomain) sortedNonPrimaryDomains.unshift(primaryDomain); - let listHeight = (rowHeight + 40) * (ownedDomains?.length || 0); + return sortedNonPrimaryDomains; + }, [primaryDomain, nonPrimaryDomains]); + + let listHeight = (rowHeight + rowPadding) * (ownedDomains?.length || 0) + 21; let scrollEnabled = false; if (listHeight > maxListHeight) { listHeight = maxListHeight; @@ -91,26 +92,7 @@ export default function SelectENSSheet() { justifyContent="center" width={{ custom: rowHeight }} > - {item.images.avatarUrl ? ( - - ) : ( - - - 􀉭 - - - )} + @@ -123,7 +105,7 @@ export default function SelectENSSheet() { ); }, - [handleSelectENS, secondary06, secondary30] + [handleSelectENS, secondary06] ); return ( @@ -137,7 +119,9 @@ export default function SelectENSSheet() { {isSuccess && ( } + ItemSeparatorComponent={() => ( + + )} as={FlatList} contentContainerStyle={{ paddingBottom: 50, @@ -159,3 +143,29 @@ export default function SelectENSSheet() { ); } + +function ENSAvatar({ name }: { name: string }) { + const { colors } = useTheme(); + + const { data: avatar } = useENSAvatar(name); + + if (avatar?.imageUrl) { + return ( + + ); + } + + return ( + + + 􀉭 + + + ); +} diff --git a/src/screens/SelectUniqueTokenSheet.tsx b/src/screens/SelectUniqueTokenSheet.tsx index 9f9ba184225..ff17b0e1555 100644 --- a/src/screens/SelectUniqueTokenSheet.tsx +++ b/src/screens/SelectUniqueTokenSheet.tsx @@ -1,18 +1,14 @@ import { useNavigation, useRoute } from '@react-navigation/core'; -import React, { useContext, useEffect, useMemo } from 'react'; -import { Animated as RNAnimated } from 'react-native'; -import { useMemoOne } from 'use-memo-one'; -import { RecyclerAssetListScrollPositionContext } from '../components/asset-list/RecyclerAssetList2/core/Contexts'; -import RawMemoRecyclerAssetList from '../components/asset-list/RecyclerAssetList2/core/RawRecyclerList'; -import { StickyHeaderManager } from '../components/asset-list/RecyclerAssetList2/core/StickyHeaders'; -import useMemoBriefSectionData from '../components/asset-list/RecyclerAssetList2/core/useMemoBriefSectionData'; +import React, { useCallback, useContext, useEffect } from 'react'; +import RecyclerAssetList2 from '../components/asset-list/RecyclerAssetList2'; import { SheetHandle } from '../components/sheet'; import { ModalContext } from '../react-native-cool-modals/NativeStackView'; import { Box } from '@rainbow-me/design-system'; import { UniqueAsset } from '@rainbow-me/entities'; +import { useWalletSectionsData } from '@rainbow-me/hooks'; export default function SelectUniqueTokenSheet() { - const { params } = useRoute(); + const { params } = useRoute(); const { goBack } = useNavigation(); const { layout } = useContext(ModalContext) || {}; @@ -20,23 +16,16 @@ export default function SelectUniqueTokenSheet() { setTimeout(() => layout?.(), 300); }, [layout]); - const { - memoizedResult: briefSectionsData, - additionalData, - } = useMemoBriefSectionData({ type: 'select-nft' }); - const position = useMemoOne(() => new RNAnimated.Value(0), []); - - const extendedState = useMemo( - () => ({ - additionalData, - onPressUniqueToken: (asset: UniqueAsset) => { - /* @ts-expect-error No types for `param` yet */ - params.onSelect?.(asset); - goBack(); - }, - }), - [additionalData, goBack, params] + const handlePressUniqueToken = useCallback( + (asset: UniqueAsset) => { + params.onSelect?.(asset); + goBack(); + }, + [goBack, params] ); + const { briefSectionsData: walletBriefSectionsData } = useWalletSectionsData({ + type: 'select-nft', + }); return ( @@ -44,15 +33,11 @@ export default function SelectUniqueTokenSheet() { {/* @ts-expect-error JavaScript component */} - - - - - + ); } diff --git a/src/screens/SendConfirmationSheet.js b/src/screens/SendConfirmationSheet.tsx similarity index 50% rename from src/screens/SendConfirmationSheet.js rename to src/screens/SendConfirmationSheet.tsx index f81db019ed1..966f9ed1ffd 100644 --- a/src/screens/SendConfirmationSheet.js +++ b/src/screens/SendConfirmationSheet.tsx @@ -1,8 +1,15 @@ +import { AddressZero } from '@ethersproject/constants'; import { useRoute } from '@react-navigation/native'; import { toChecksumAddress } from 'ethereumjs-util'; import lang from 'i18n-js'; -import { capitalize } from 'lodash'; -import React, { Fragment, useCallback, useEffect } from 'react'; +import { capitalize, isEmpty } from 'lodash'; +import React, { + Fragment, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; import { Keyboard, StatusBar } from 'react-native'; import { useSafeArea } from 'react-native-safe-area-context'; import ContactRowInfoButton from '../components/ContactRowInfoButton'; @@ -10,15 +17,20 @@ import Divider from '../components/Divider'; import L2Disclaimer from '../components/L2Disclaimer'; import Pill from '../components/Pill'; import TouchableBackdrop from '../components/TouchableBackdrop'; -import { ButtonPressAnimation } from '../components/animations'; +import ButtonPressAnimation from '../components/animations/ButtonPressAnimation'; +import Callout from '../components/callout/Callout'; import { CoinIcon } from '../components/coin-icon'; import RequestVendorLogoIcon from '../components/coin-icon/RequestVendorLogoIcon'; import { ContactAvatar } from '../components/contacts'; import ImageAvatar from '../components/contacts/ImageAvatar'; -import { Centered, Column, Row, RowWithMargins } from '../components/layout'; +import CheckboxField from '../components/fields/CheckboxField'; +import { GasSpeedButton } from '../components/gas'; +import ENSCircleIcon from '../components/icons/svg/ENSCircleIcon'; +import { Centered, Column, Row } from '../components/layout'; import { SendButton } from '../components/send'; import { SheetTitle, SlackSheet } from '../components/sheet'; -import { Text, TruncatedText } from '../components/text'; +import { Text as OldText, TruncatedText } from '../components/text'; +import { ENSProfile } from '../entities/ens'; import { address } from '../utils/abbreviations'; import { addressHashedColorIndex, @@ -27,12 +39,24 @@ import { import useExperimentalFlag, { PROFILES, } from '@rainbow-me/config/experimentalHooks'; -import { AssetTypes } from '@rainbow-me/entities'; +import { Box, Inset, Stack, Text } from '@rainbow-me/design-system'; +import { AssetTypes, UniqueAsset } from '@rainbow-me/entities'; +import { + estimateENSReclaimGasLimit, + estimateENSSetAddressGasLimit, + estimateENSSetRecordsGasLimit, + formatRecordsForTransaction, +} from '@rainbow-me/handlers/ens'; +import svgToPngIfNeeded from '@rainbow-me/handlers/svgs'; +import { estimateGasLimit } from '@rainbow-me/handlers/web3'; import { removeFirstEmojiFromString, returnStringFirstEmoji, } from '@rainbow-me/helpers/emojiHandler'; -import { convertAmountToNativeDisplay } from '@rainbow-me/helpers/utilities'; +import { + add, + convertAmountToNativeDisplay, +} from '@rainbow-me/helpers/utilities'; import { isENSAddressFormat, isValidDomainFormat, @@ -43,7 +67,8 @@ import { useColorForAsset, useContacts, useDimensions, - useENSProfileImages, + useENSAvatar, + useGas, useUserAccounts, useWallets, } from '@rainbow-me/hooks'; @@ -51,57 +76,144 @@ import { useNavigation } from '@rainbow-me/navigation'; import Routes from '@rainbow-me/routes'; import styled from '@rainbow-me/styled-components'; import { position } from '@rainbow-me/styles'; +import { useTheme } from '@rainbow-me/theme'; +import { getUniqueTokenType, promiseUtils } from '@rainbow-me/utils'; import logger from 'logger'; const Container = styled(Centered).attrs({ direction: 'column', -})(({ deviceHeight, height }) => ({ +})(({ deviceHeight, height }: { deviceHeight: number; height: number }) => ({ ...(height && { height: height + deviceHeight }), ...position.coverAsObject, })); -const CheckboxContainer = styled(Row)({ - height: 20, - width: 20, -}); - -const CheckboxBorder = styled.View(({ checked, color, theme: { colors } }) => ({ - ...(checked ? { backgroundColor: color } : {}), - borderColor: colors.alpha(colors.blueGreyDark, checked ? 0 : 0.15), - borderRadius: 7, - borderWidth: 2, - height: 20, - position: 'absolute', - width: 20, -})); - -const CheckboxLabelText = styled(Text).attrs({ - direction: 'column', - letterSpacing: 'roundedMedium', - lineHeight: 'looserLoose', - size: 'lmedium', - weight: 'bold', -})({ - flexShrink: 1, -}); - -const Checkmark = styled(Text).attrs(({ checked, theme: { colors } }) => ({ - align: 'center', - color: checked ? colors.whiteLabel : colors.transparent, - lineHeight: 'normal', - size: 'smaller', - weight: 'bold', -}))({ - width: '100%', -}); - const SendButtonWrapper = styled(Column).attrs({ align: 'center', })({ height: 56, }); -export const SendConfirmationSheetHeight = android ? 651 : 540; +export type Checkbox = { + checked: boolean; + id: + | 'clear-records' + | 'set-address' + | 'transfer-control' + | 'not-sending-to-exchange' + | 'has-wallet-that-supports'; + label: string; +}; + +const hasClearProfileInfo = (ensProfile?: ENSProfile) => + isEmpty({ ...ensProfile?.data?.records, ...ensProfile?.data?.coinAddresses }); +const doesNamePointToRecipient = ( + ensProfile?: ENSProfile, + recipientAddress?: string +) => + ensProfile?.data?.address?.toLowerCase() === recipientAddress?.toLowerCase(); +const isRegistrant = (ensProfile?: ENSProfile) => ensProfile?.isRegistrant; + +const gasOffset = 120; +const checkboxOffset = 44; + +function getDefaultCheckboxes({ + isENS, + ensProfile, + network, + toAddress, +}: { + isENS: boolean; + ensProfile: ENSProfile; + network: string; + toAddress: string; +}): Checkbox[] { + if (isENS) { + return [ + !hasClearProfileInfo(ensProfile) && + ensProfile?.isOwner && { + checked: false, + id: 'clear-records', + label: lang.t( + 'wallet.transaction.checkboxes.clear_profile_information' + ), + }, + !doesNamePointToRecipient(ensProfile, toAddress) && + ensProfile?.isOwner && { + checked: false, + id: 'set-address', + label: lang.t( + 'wallet.transaction.checkboxes.point_name_to_recipient' + ), + }, + isRegistrant(ensProfile) && + ensProfile?.data?.owner?.address?.toLowerCase() !== + toAddress.toLowerCase() && { + checked: false, + id: 'transfer-control', + label: lang.t('wallet.transaction.checkboxes.transfer_control'), + }, + ].filter(Boolean) as Checkbox[]; + } + return [ + { + checked: false, + id: 'not-sending-to-exchange', + label: lang.t( + 'wallet.transaction.checkboxes.im_not_sending_to_an_exchange' + ), + }, + { + checked: false, + id: 'has-wallet-that-supports', + label: lang.t( + 'wallet.transaction.checkboxes.has_a_wallet_that_supports', + { + networkName: capitalize(network), + } + ), + }, + ]; +} + +export function getSheetHeight({ + asset, + ensProfile, + profilesEnabled, + shouldShowChecks, + isL2, + toAddress, +}: { + asset?: UniqueAsset; + ensProfile?: ENSProfile; + profilesEnabled: boolean; + shouldShowChecks: boolean; + isL2: boolean; + toAddress: string; +}) { + let height = android ? 440 : 377; + if (shouldShowChecks) height = height + 104; + if (isL2) height = height + 59; + if (profilesEnabled && asset && getUniqueTokenType(asset) === 'ENS') { + height = height + gasOffset; + if (!hasClearProfileInfo(ensProfile) && ensProfile?.isOwner) { + height = height + checkboxOffset; + } + if ( + !doesNamePointToRecipient(ensProfile, toAddress) && + ensProfile?.isOwner + ) { + height = height + checkboxOffset; + } + if ( + isRegistrant(ensProfile) && + ensProfile?.data?.owner?.address?.toLowerCase() !== + toAddress.toLowerCase() + ) { + height = height + checkboxOffset; + } + } + return height; +} const ChevronDown = () => { const { colors } = useTheme(); @@ -113,7 +225,7 @@ const ChevronDown = () => { position="absolute" width={50} > - { weight="semibold" > 􀆈 - - + { weight="semibold" > 􀆈 - - - ); -}; - -const Checkbox = ({ activeColor, checked, id, label, onPress }) => { - const { colors } = useTheme(); - - const handlePress = useCallback(() => { - onPress({ checked: !checked, id, label }); - }, [checked, id, label, onPress]); - return ( - - - - - - 􀆅 - - - {label} - - - + ); }; export default function SendConfirmationSheet() { const { colors, isDarkMode } = useTheme(); - const { nativeCurrency } = useAccountSettings(); + const { accountAddress, nativeCurrency } = useAccountSettings(); const { goBack, navigate, setParams } = useNavigation(); const { height: deviceHeight, @@ -191,13 +272,14 @@ export default function SendConfirmationSheet() { amountDetails, asset, callback, + ensProfile, isL2, isNft, network, to, toAddress, }, - } = useRoute(); + } = useRoute(); const [ alreadySentTransactionsTotal, @@ -212,16 +294,20 @@ export default function SendConfirmationSheet() { const { userAccounts, watchedAccounts } = useUserAccounts(); const { walletNames } = useWallets(); const isSendingToUserAccount = useMemo(() => { + // @ts-expect-error From JavaScript hook const found = userAccounts?.find(account => { return account.address.toLowerCase() === toAddress?.toLowerCase(); }); return !!found; }, [toAddress, userAccounts]); + const { isSufficientGas, isValidGas, updateTxFee } = useGas(); + useEffect(() => { if (!isSendingToUserAccount) { let sends = 0; let sendsCurrentNetwork = 0; + // @ts-expect-error From JavaScript hook transactions.forEach(tx => { if (tx.to?.toLowerCase() === toAddress?.toLowerCase()) { sends++; @@ -243,33 +329,110 @@ export default function SendConfirmationSheet() { return contacts?.[toAddress?.toLowerCase()]; }, [contacts, toAddress]); - const [checkboxes, setCheckboxes] = useState([ - { - checked: false, - label: lang.t( - 'wallet.transaction.checkboxes.im_not_sending_to_an_exchange' - ), - }, - { - checked: false, - label: lang.t( - 'wallet.transaction.checkboxes.has_a_wallet_that_supports', - { - networkName: capitalize(network), + const uniqueTokenType = getUniqueTokenType(asset); + const isENS = uniqueTokenType === 'ENS' && profilesEnabled; + + const [checkboxes, setCheckboxes] = useState( + getDefaultCheckboxes({ ensProfile, isENS, network, toAddress }) + ); + + useEffect(() => { + if (isENS) { + const promises = [ + estimateGasLimit( + { + address: accountAddress, + amount: 0, + asset: asset, + recipient: toAddress, + }, + true + ), + ]; + const sendENSOptions = Object.fromEntries( + checkboxes.map(option => [option.id, option.checked]) + ) as { + [key in Checkbox['id']]: Checkbox['checked']; + }; + const cleanENSName = asset?.name?.split(' ')?.[0] ?? asset?.name; + + if (sendENSOptions['clear-records']) { + let records = Object.keys({ + ...(ensProfile?.data?.coinAddresses ?? {}), + ...(ensProfile?.data?.records ?? {}), + }).reduce((records, recordKey) => { + return { + ...records, + [recordKey]: '', + }; + }, {}); + if (sendENSOptions['set-address']) { + records = { ...records, ETH: toAddress }; + } else { + records = { ...records, ETH: AddressZero }; } - ), - }, + promises.push( + estimateENSSetRecordsGasLimit({ + name: cleanENSName, + ownerAddress: accountAddress, + records, + }) + ); + } else if (sendENSOptions['set-address']) { + promises.push( + estimateENSSetAddressGasLimit({ + name: cleanENSName, + ownerAddress: accountAddress, + records: formatRecordsForTransaction({ ETH: toAddress }), + }) + ); + } + if (sendENSOptions['transfer-control']) { + promises.push( + estimateENSReclaimGasLimit({ + name: cleanENSName, + ownerAddress: accountAddress, + toAddress, + }) + ); + } + promiseUtils + .PromiseAllWithFails(promises) + .then(gasLimits => { + const gasLimit = gasLimits.reduce(add, 0); + updateTxFee(gasLimit, null); + }) + .catch(e => { + logger.sentry('Error calculating gas limit', e); + updateTxFee(null, null); + }); + } + }, [ + accountAddress, + asset, + checkboxes, + ensProfile?.data?.coinAddresses, + ensProfile?.data?.records, + isENS, + toAddress, + updateTxFee, ]); const handleCheckbox = useCallback( checkbox => { const newCheckboxesState = [...checkboxes]; - newCheckboxesState[checkbox.id] = checkbox; + newCheckboxesState[checkbox.index] = checkbox; setCheckboxes(newCheckboxesState); }, [checkboxes] ); + const handleENSConfigurationPress = useCallback(() => { + navigate(Routes.EXPLAIN_SHEET, { + type: 'ens_configuration', + }); + }, [navigate]); + const handleL2DisclaimerPress = useCallback(() => { navigate(Routes.EXPLAIN_SHEET, { type: asset.type, @@ -301,19 +464,38 @@ export default function SendConfirmationSheet() { }, [setParams, shouldShowChecks]); const canSubmit = - !shouldShowChecks || - checkboxes.filter(check => check.checked === false).length === 0; + isSufficientGas && + isValidGas && + (!shouldShowChecks || + checkboxes.filter(check => check.checked === false).length === 0); + + const insufficientEth = isSufficientGas === false && isValidGas; const handleSubmit = useCallback(async () => { if (!canSubmit) return; try { setIsAuthorizing(true); - await callback(); + if (isENS) { + const clearRecords = checkboxes.some( + ({ checked, id }) => checked && id === 'clear-records' + ); + const setAddress = checkboxes.some( + ({ checked, id }) => checked && id === 'set-address' + ); + const transferControl = checkboxes.some( + ({ checked, id }) => checked && id === 'transfer-control' + ); + await callback({ + ens: { clearRecords, setAddress, transferControl }, + }); + } else { + await callback(); + } } catch (e) { logger.sentry('TX submit failed', e); setIsAuthorizing(false); } - }, [callback, canSubmit]); + }, [callback, canSubmit, checkboxes, isENS]); const existingAccount = useMemo(() => { let existingAcct = null; @@ -350,23 +532,34 @@ export default function SendConfirmationSheet() { contact?.color || addressHashedColorIndex(toAddress); - let realSheetHeight = !shouldShowChecks - ? SendConfirmationSheetHeight - 150 - : SendConfirmationSheetHeight; - - if (!isL2) { - realSheetHeight -= 80; - } - - const { data: images } = useENSProfileImages(to, { + const { data: avatar } = useENSAvatar(to, { enabled: isENSAddressFormat(to), }); const accountImage = profilesEnabled - ? images?.avatarUrl || existingAccount?.image + ? avatar?.imageUrl || existingAccount?.image : existingAccount?.image; - const contentHeight = realSheetHeight - (isL2 ? 50 : 30); + const imageUrl = svgToPngIfNeeded( + asset.image_thumbnail_url || asset.image_url, + true + ); + + let contentHeight = + getSheetHeight({ + ensProfile, + isL2, + profilesEnabled, + shouldShowChecks, + toAddress, + }) - 30; + if (shouldShowChecks) contentHeight = contentHeight + 150; + if (isL2) contentHeight = contentHeight + 60; + if (isENS) { + contentHeight = + contentHeight + checkboxes.length * checkboxOffset + gasOffset; + } + return ( } {ios && } + {/* @ts-expect-error JavaScript component */} {lang.t('wallet.transaction.sending_title')} - + - + {isNft ? ( + // @ts-expect-error JavaScript component + {/* @ts-expect-error – JS component */} - {lang.t('account.tx_to_lowercase')} - + @@ -472,7 +668,7 @@ export default function SendConfirmationSheet() { network={network} scaleTo={0.75} > - {' 􀍡'} - + - + @@ -512,50 +708,100 @@ export default function SendConfirmationSheet() { )} + {/* @ts-expect-error JavaScript component */} - {isL2 && ( - - - + {(isL2 || isENS || shouldShowChecks) && ( + + + {isL2 && ( + + {/* @ts-expect-error JavaScript component */} + + + )} + {isENS && checkboxes.length > 0 && ( + + + 􀅵 + + } + before={ + + + + } + > + {lang.t('wallet.transaction.ens_configuration_options')} + + + )} + {(isENS || shouldShowChecks) && checkboxes.length > 0 && ( + + + {checkboxes.map((check, i) => ( + + handleCheckbox({ + ...check, + checked: !check.checked, + index: i, + }) + } + testID={check.id} + /> + ))} + + + )} + + )} - - {shouldShowChecks && - checkboxes.map((check, i) => ( - - ))} - + {/* @ts-expect-error JavaScript component */} + {isENS && ( + /* @ts-expect-error JavaScript component */ + + )} diff --git a/src/screens/SendSheet.js b/src/screens/SendSheet.js index 7ae222b194f..efe9a681991 100644 --- a/src/screens/SendSheet.js +++ b/src/screens/SendSheet.js @@ -23,7 +23,6 @@ import { SendHeader, } from '../components/send'; import { SheetActionButton } from '../components/sheet'; -import { prefetchENSProfileImages } from '../hooks/useENSProfileImages'; import { WrappedAlert as Alert } from '@/helpers/alert'; import { analytics } from '@rainbow-me/analytics'; import { PROFILES, useExperimentalFlag } from '@rainbow-me/config'; @@ -47,11 +46,15 @@ import { isENSAddressFormat, } from '@rainbow-me/helpers/validators'; import { + prefetchENSAvatar, + prefetchENSCover, useAccountSettings, useCoinListEditOptions, useColorForAsset, useContacts, useCurrentNonce, + useENSProfile, + useENSRegistrationActionHandler, useGas, useMaxInputBalance, usePrevious, @@ -63,7 +66,7 @@ import { useUpdateAssetOnchainBalance, useUserAccounts, } from '@rainbow-me/hooks'; -import { sendTransaction } from '@rainbow-me/model/wallet'; +import { loadWallet, sendTransaction } from '@rainbow-me/model/wallet'; import { useNavigation } from '@rainbow-me/navigation/Navigation'; import { parseGasParamsForTransaction } from '@rainbow-me/parsers'; import { chainAssets, rainbowTokenList } from '@rainbow-me/references'; @@ -76,7 +79,11 @@ import { formatInputDecimals, lessThan, } from '@rainbow-me/utilities'; -import { deviceUtils, ethereumUtils } from '@rainbow-me/utils'; +import { + deviceUtils, + ethereumUtils, + getUniqueTokenType, +} from '@rainbow-me/utils'; import logger from 'logger'; const sheetHeight = deviceUtils.dimensions.height - (android ? 30 : 10); @@ -131,6 +138,10 @@ export default function SendSheet(props) { const { sendableUniqueTokens } = useSendableUniqueTokens(); const { accountAddress, nativeCurrency, network } = useAccountSettings(); + const { action: transferENS } = useENSRegistrationActionHandler({ + step: 'TRANSFER', + }); + const savings = useSendSavingsAccount(); const { hiddenCoinsObj, pinnedCoinsObj } = useCoinListEditOptions(); const [toAddress, setToAddress] = useState(); @@ -191,6 +202,17 @@ export default function SendSheet(props) { colorForAsset = colors.appleBlue; } + const uniqueTokenType = isNft ? getUniqueTokenType(selected) : undefined; + const isENS = uniqueTokenType === 'ENS'; + + const ensName = selected.uniqueId + ? selected.uniqueId?.split(' ')?.[0] + : selected.uniqueId; + const ensProfile = useENSProfile(ensName, { + enabled: isENS, + supportedRecordsOnly: false, + }); + const isL2 = useMemo(() => { return isL2Network(currentNetwork); }, [currentNetwork]); @@ -461,156 +483,198 @@ export default function SendSheet(props) { ] ); - const onSubmit = useCallback(async () => { - const validTransaction = - isValidAddress && - amountDetails.isSufficientBalance && - isSufficientGas && - isValidGas; - if (!selectedGasFee?.gasFee?.estimatedFee || !validTransaction) { - logger.sentry('preventing tx submit for one of the following reasons:'); - logger.sentry('selectedGasFee ? ', selectedGasFee); - logger.sentry('selectedGasFee.maxFee ? ', selectedGasFee?.maxFee); - logger.sentry('validTransaction ? ', validTransaction); - logger.sentry('isValidGas ? ', isValidGas); - captureEvent('Preventing tx submit'); - return false; - } - - let submitSuccess = false; - let updatedGasLimit = null; + const onSubmit = useCallback( + async ({ + ens: { setAddress, transferControl, clearRecords } = {}, + } = {}) => { + const wallet = await loadWallet(undefined, true, currentProvider); + if (!wallet) return; + + const validTransaction = + isValidAddress && + amountDetails.isSufficientBalance && + isSufficientGas && + isValidGas; + if (!selectedGasFee?.gasFee?.estimatedFee || !validTransaction) { + logger.sentry('preventing tx submit for one of the following reasons:'); + logger.sentry('selectedGasFee ? ', selectedGasFee); + logger.sentry('selectedGasFee.maxFee ? ', selectedGasFee?.maxFee); + logger.sentry('validTransaction ? ', validTransaction); + logger.sentry('isValidGas ? ', isValidGas); + captureEvent('Preventing tx submit'); + return false; + } - // Attempt to update gas limit before sending ERC20 / ERC721 - if (!isNativeAsset(selected.address, currentNetwork)) { - try { - // Estimate the tx with gas limit padding before sending - updatedGasLimit = await estimateGasLimit( - { - address: accountAddress, - amount: amountDetails.assetAmount, - asset: selected, - recipient: toAddress, - }, - true, - currentProvider, - currentNetwork - ); + let submitSuccess = false; + let updatedGasLimit = null; + + // Attempt to update gas limit before sending ERC20 / ERC721 + if (!isNativeAsset(selected.address, currentNetwork)) { + try { + // Estimate the tx with gas limit padding before sending + updatedGasLimit = await estimateGasLimit( + { + address: accountAddress, + amount: amountDetails.assetAmount, + asset: selected, + recipient: toAddress, + }, + true, + currentProvider, + currentNetwork + ); - if (!lessThan(updatedGasLimit, gasLimit)) { - if (currentNetwork === Network.optimism) { - updateTxFeeForOptimism(updatedGasLimit); - } else { - updateTxFee(updatedGasLimit, null); + if (!lessThan(updatedGasLimit, gasLimit)) { + if (currentNetwork === Network.optimism) { + updateTxFeeForOptimism(updatedGasLimit); + } else { + updateTxFee(updatedGasLimit, null); + } } - } - // eslint-disable-next-line no-empty - } catch (e) {} - } - - const gasLimitToUse = - updatedGasLimit && !lessThan(updatedGasLimit, gasLimit) - ? updatedGasLimit - : gasLimit; + // eslint-disable-next-line no-empty + } catch (e) {} + } - const gasParams = parseGasParamsForTransaction(selectedGasFee); - const txDetails = { - amount: amountDetails.assetAmount, - asset: selected, - from: accountAddress, - gasLimit: gasLimitToUse, - network: currentNetwork, - nonce: await getNextNonce(), - to: toAddress, - ...gasParams, - }; + let nextNonce; - try { - const signableTransaction = await createSignableTransaction(txDetails); - if (!signableTransaction.to) { - logger.sentry('txDetails', txDetails); - logger.sentry('signableTransaction', signableTransaction); - logger.sentry('"to" field is missing!'); - const e = new Error('Transaction missing TO field'); - captureException(e); - Alert.alert(lang.t('wallet.transaction.alert.invalid_transaction')); - submitSuccess = false; - } else { - const { result: txResult } = await sendTransaction({ - provider: currentProvider, - transaction: signableTransaction, + if ( + isENS && + toAddress && + (clearRecords || setAddress || transferControl) + ) { + const { nonce } = await transferENS(() => null, { + clearRecords, + name: ensName, + records: { + ...(ensProfile?.data?.records || {}), + ...(ensProfile?.data?.coinAddresses || {}), + }, + setAddress, + toAddress, + transferControl, + wallet, }); - const { hash, nonce } = txResult; - const { data, value } = signableTransaction; - if (!isEmpty(hash)) { - submitSuccess = true; - txDetails.hash = hash; - txDetails.nonce = nonce; - txDetails.network = currentNetwork; - txDetails.data = data; - txDetails.value = value; - txDetails.txTo = signableTransaction.to; - await dispatch( - dataAddNewTransaction(txDetails, null, false, currentProvider) - ); - } + nextNonce = nonce + 1; } - } catch (error) { - logger.sentry('TX Details', txDetails); - logger.sentry('SendSheet onSubmit error'); - logger.sentry(error); - captureException(error); - submitSuccess = false; - } - return submitSuccess; - }, [ - accountAddress, - amountDetails.assetAmount, - amountDetails.isSufficientBalance, - currentNetwork, - currentProvider, - dataAddNewTransaction, - dispatch, - gasLimit, - getNextNonce, - isSufficientGas, - isValidAddress, - isValidGas, - selected, - selectedGasFee, - toAddress, - updateTxFee, - updateTxFeeForOptimism, - ]); - const submitTransaction = useCallback(async () => { - if (Number(amountDetails.assetAmount) <= 0) { - logger.sentry('amountDetails.assetAmount ? ', amountDetails?.assetAmount); - captureEvent('Preventing tx submit due to amount <= 0'); - return false; - } - const submitSuccessful = await onSubmit(); - analytics.track('Sent transaction', { - assetName: selected?.name || '', - assetType: selected?.type || '', - isRecepientENS: recipient.slice(-4).toLowerCase() === '.eth', - }); + const gasLimitToUse = + updatedGasLimit && !lessThan(updatedGasLimit, gasLimit) + ? updatedGasLimit + : gasLimit; + + const gasParams = parseGasParamsForTransaction(selectedGasFee); + const txDetails = { + amount: amountDetails.assetAmount, + asset: selected, + from: accountAddress, + gasLimit: gasLimitToUse, + network: currentNetwork, + nonce: nextNonce ?? (await getNextNonce()), + to: toAddress, + ...gasParams, + }; - if (submitSuccessful) { - goBack(); - navigate(Routes.WALLET_SCREEN); - InteractionManager.runAfterInteractions(() => { - navigate(Routes.PROFILE_SCREEN); + try { + const signableTransaction = await createSignableTransaction(txDetails); + if (!signableTransaction.to) { + logger.sentry('txDetails', txDetails); + logger.sentry('signableTransaction', signableTransaction); + logger.sentry('"to" field is missing!'); + const e = new Error('Transaction missing TO field'); + captureException(e); + Alert.alert(lang.t('wallet.transaction.alert.invalid_transaction')); + submitSuccess = false; + } else { + const { result: txResult } = await sendTransaction({ + existingWallet: wallet, + provider: currentProvider, + transaction: signableTransaction, + }); + const { hash, nonce } = txResult; + const { data, value } = signableTransaction; + if (!isEmpty(hash)) { + submitSuccess = true; + txDetails.hash = hash; + txDetails.nonce = nonce; + txDetails.network = currentNetwork; + txDetails.data = data; + txDetails.value = value; + txDetails.txTo = signableTransaction.to; + await dispatch( + dataAddNewTransaction(txDetails, null, false, currentProvider) + ); + } + } + } catch (error) { + logger.sentry('TX Details', txDetails); + logger.sentry('SendSheet onSubmit error'); + logger.sentry(error); + captureException(error); + submitSuccess = false; + } + return submitSuccess; + }, + [ + accountAddress, + amountDetails.assetAmount, + amountDetails.isSufficientBalance, + currentNetwork, + currentProvider, + dataAddNewTransaction, + dispatch, + ensName, + ensProfile?.data?.coinAddresses, + ensProfile?.data?.records, + gasLimit, + getNextNonce, + isENS, + isSufficientGas, + isValidAddress, + isValidGas, + selected, + selectedGasFee, + toAddress, + transferENS, + updateTxFee, + updateTxFeeForOptimism, + ] + ); + + const submitTransaction = useCallback( + async (...args) => { + if (Number(amountDetails.assetAmount) <= 0) { + logger.sentry( + 'amountDetails.assetAmount ? ', + amountDetails?.assetAmount + ); + captureEvent('Preventing tx submit due to amount <= 0'); + return false; + } + const submitSuccessful = await onSubmit(...args); + analytics.track('Sent transaction', { + assetName: selected?.name || '', + assetType: selected?.type || '', + isRecepientENS: recipient.slice(-4).toLowerCase() === '.eth', }); - } - }, [ - amountDetails.assetAmount, - goBack, - navigate, - onSubmit, - recipient, - selected?.name, - selected?.type, - ]); + + if (submitSuccessful) { + goBack(); + navigate(Routes.WALLET_SCREEN); + InteractionManager.runAfterInteractions(() => { + navigate(Routes.PROFILE_SCREEN); + }); + } + }, + [ + amountDetails.assetAmount, + goBack, + navigate, + onSubmit, + recipient, + selected?.name, + selected?.type, + ] + ); const validateRecipient = useCallback( async toAddress => { @@ -647,7 +711,10 @@ export default function SendSheet(props) { if (currentNetwork === Network.polygon) { nativeToken = 'MATIC'; } - if ( + if (isENS && !ensProfile.isSuccess) { + label = lang.t('button.confirm_exchange.loading'); + disabled = true; + } else if ( isEmpty(gasFeeParamsBySpeed) || !selectedGasFee || isEmpty(selectedGasFee?.gasFee) || @@ -676,6 +743,8 @@ export default function SendSheet(props) { amountDetails.assetAmount, amountDetails.isSufficientBalance, currentNetwork, + isENS, + ensProfile.isSuccess, gasFeeParamsBySpeed, selectedGasFee, isSufficientGas, @@ -712,9 +781,11 @@ export default function SendSheet(props) { amountDetails: amountDetails, asset: selected, callback: submitTransaction, + ensProfile, isL2, isNft, network: currentNetwork, + profilesEnabled, to: recipient, toAddress, }); @@ -723,10 +794,12 @@ export default function SendSheet(props) { assetInputRef, buttonDisabled, currentNetwork, + ensProfile, isL2, isNft, nativeCurrencyInputRef, navigate, + profilesEnabled, recipient, selected, submitTransaction, @@ -749,7 +822,8 @@ export default function SendSheet(props) { setRecipient(text); setNickname(text); if (profilesEnabled && isENSAddressFormat(text)) { - prefetchENSProfileImages(text); + prefetchENSAvatar(text); + prefetchENSCover(text); } }, [profilesEnabled] diff --git a/src/screens/SpeedUpAndCancelSheet.js b/src/screens/SpeedUpAndCancelSheet.js index efd345f0819..9dcd421147b 100644 --- a/src/screens/SpeedUpAndCancelSheet.js +++ b/src/screens/SpeedUpAndCancelSheet.js @@ -27,6 +27,10 @@ import { } from '../components/sheet'; import { Emoji, Text } from '../components/text'; import { WrappedAlert as Alert } from '@/helpers/alert'; +import { + removeRegistrationByName, + saveCommitRegistrationParameters, +} from '@/redux/ensRegistration'; import { GasFeeTypes, TransactionStatusTypes } from '@rainbow-me/entities'; import { getFlashbotsProvider, @@ -132,7 +136,7 @@ export default function SpeedUpAndCancelSheet() { const calculatingGasLimit = useRef(false); const speedUrgentSelected = useRef(false); const { - params: { type, tx, accentColor, onSendTransactionCallback }, + params: { type, tx, accentColor }, } = useRoute(); const [ready, setReady] = useState(false); @@ -190,6 +194,10 @@ export default function SpeedUpAndCancelSheet() { minGasPrice, ]); + const cancelCommitTransactionHash = useCallback(() => { + dispatch(removeRegistrationByName(tx?.ensRegistrationName)); + }, [dispatch, tx?.ensRegistrationName]); + const handleCancellation = useCallback(async () => { try { const newGasParams = getNewTransactionGasParams(); @@ -206,6 +214,9 @@ export default function SpeedUpAndCancelSheet() { transaction: cancelTxPayload, }); + if (tx?.ensRegistrationName) { + cancelCommitTransactionHash(tx?.ensRegistrationName); + } const updatedTx = { ...tx }; // Update the hash on the copy of the original tx updatedTx.hash = hash; @@ -224,6 +235,7 @@ export default function SpeedUpAndCancelSheet() { } }, [ accountAddress, + cancelCommitTransactionHash, currentProvider, dispatch, getNewTransactionGasParams, @@ -232,6 +244,18 @@ export default function SpeedUpAndCancelSheet() { tx, ]); + const saveCommitTransactionHash = useCallback( + hash => { + dispatch( + saveCommitRegistrationParameters({ + commitTransactionHash: hash, + name: tx?.ensRegistrationName, + }) + ); + }, + [dispatch, tx?.ensRegistrationName] + ); + const handleSpeedUp = useCallback(async () => { try { const newGasParams = getNewTransactionGasParams(); @@ -250,7 +274,9 @@ export default function SpeedUpAndCancelSheet() { provider: currentProvider, transaction: fasterTxPayload, }); - onSendTransactionCallback?.(hash); + if (tx?.ensRegistrationName) { + saveCommitTransactionHash(hash); + } const updatedTx = { ...tx }; // Update the hash on the copy of the original tx updatedTx.hash = hash; @@ -275,7 +301,7 @@ export default function SpeedUpAndCancelSheet() { getNewTransactionGasParams, goBack, nonce, - onSendTransactionCallback, + saveCommitTransactionHash, to, tx, value, diff --git a/src/screens/TransactionConfirmationScreen.js b/src/screens/TransactionConfirmationScreen.js index 613149b6259..aa40f062dfd 100644 --- a/src/screens/TransactionConfirmationScreen.js +++ b/src/screens/TransactionConfirmationScreen.js @@ -233,14 +233,13 @@ export default function TransactionConfirmationScreen() { const profileInfo = getAccountProfileInfo( selectedWallet, walletNames, - currentNetwork, address ); return { ...profileInfo, address, }; - }, [currentNetwork, walletConnector?._accounts, walletNames, wallets]); + }, [walletConnector?._accounts, walletNames, wallets]); const getNextNonce = useCurrentNonce(accountInfo.address, currentNetwork); diff --git a/src/screens/WalletConnectApprovalSheet.js b/src/screens/WalletConnectApprovalSheet.js index 7aa54ccea43..df865fb119f 100644 --- a/src/screens/WalletConnectApprovalSheet.js +++ b/src/screens/WalletConnectApprovalSheet.js @@ -151,7 +151,6 @@ export default function WalletConnectApprovalSheet() { const approvalAccountInfo = getAccountProfileInfo( approvalAccount.wallet, walletNames, - approvalNetwork, approvalAccount.address ); return { @@ -161,12 +160,7 @@ export default function WalletConnectApprovalSheet() { approvalAccountInfo.accountName || approvalAccount.address, }; - }, [ - walletNames, - approvalNetwork, - approvalAccount.wallet, - approvalAccount.address, - ]); + }, [walletNames, approvalAccount.wallet, approvalAccount.address]); const approvalNetworkInfo = useMemo(() => { const value = networkInfo[approvalNetwork]?.value; diff --git a/src/screens/WalletScreen.js b/src/screens/WalletScreen.js index 87e4b63d0e5..d329004f322 100644 --- a/src/screens/WalletScreen.js +++ b/src/screens/WalletScreen.js @@ -1,6 +1,7 @@ import { useRoute } from '@react-navigation/core'; import { compact, isEmpty, keys } from 'lodash'; import React, { useEffect, useMemo, useState } from 'react'; +import { InteractionManager } from 'react-native'; import Animated from 'react-native-reanimated'; import { useValue } from 'react-native-redash/src/v1'; import { useDispatch, useSelector } from 'react-redux'; @@ -18,6 +19,7 @@ import { useRemoveFirst } from '@/navigation/useRemoveFirst'; import useExperimentalFlag, { PROFILES, } from '@rainbow-me/config/experimentalHooks'; +import { prefetchENSIntroData } from '@rainbow-me/handlers/ens'; import networkInfo from '@rainbow-me/helpers/networkInfo'; import { useAccountEmptyState, @@ -30,7 +32,6 @@ import { usePortfolios, useTrackENSProfile, useUserAccounts, - useWalletENSAvatar, useWallets, useWalletSectionsData, } from '@rainbow-me/hooks'; @@ -80,7 +81,7 @@ export default function WalletScreen() { const loadAccountLateData = useLoadAccountLateData(); const loadGlobalLateData = useLoadGlobalLateData(); const initializeDiscoverData = useInitializeDiscoverData(); - const { updateWalletENSAvatars } = useWalletENSAvatar(); + const walletReady = useSelector( ({ appState: { walletReady } }) => walletReady ); @@ -164,12 +165,6 @@ export default function WalletScreen() { userAccounts, ]); - useEffect(() => { - if (profilesEnabled) { - trackENSProfile(); - } - }, [profilesEnabled, trackENSProfile]); - useEffect(() => { if ( !isEmpty(portfolios) && @@ -209,9 +204,17 @@ export default function WalletScreen() { ]); useEffect(() => { - if (walletReady) updateWalletENSAvatars(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [walletReady]); + if (walletReady && profilesEnabled) { + InteractionManager.runAfterInteractions(() => { + // We are not prefetching intro profiles data on Android + // as the RPC call queue is considerably slower. + if (ios) { + prefetchENSIntroData(); + } + trackENSProfile(); + }); + } + }, [profilesEnabled, trackENSProfile, walletReady]); // Show the exchange fab only for supported networks // (mainnet & rinkeby) diff --git a/src/styles/colors.ts b/src/styles/colors.ts index ff56d40b276..4b73e2fb809 100644 --- a/src/styles/colors.ts +++ b/src/styles/colors.ts @@ -25,7 +25,7 @@ const darkModeColors = { darkGrey: '#333333', darkModeDark: '#404656', exchangeFallback: 'rgba(60, 66, 82, 0.8)', - green: '#00D146', + green: '#4BD166', grey: '#333333', grey20: '#333333', lighterGrey: '#12131A', @@ -194,7 +194,7 @@ const getColorsByTheme = (darkMode?: boolean) => { appleBlueTintToAppleBlue: ['#15B1FE', base.appleBlue], blueToGreen: ['#4764F7', '#23D67F'], checkmarkAnimation: ['#1FC24A10', '#1FC24A10', '#1FC24A00'], - ens: ['#513eff', '#3e80ff'], + ens: ['#456AFF', '#5FA9EE'], lighterGrey: [buildRgba('#ECF1F5', 0.15), buildRgba('#DFE4EB', 0.5)], lightestGrey: ['#FFFFFF', '#F2F4F7'], lightestGreyReverse: ['#F2F4F7', '#FFFFFF'], @@ -228,6 +228,10 @@ const getColorsByTheme = (darkMode?: boolean) => { vividRainbowTint: ['#FFFAF1', '#FFF5FB', '#F0FEFF'], warning: ['#FFD963', '#FFB200'], warningTint: ['#FFFDF6', '#FFFBF2'], + white80ToTransparent: [ + buildRgba(base.whiteLabel, 0.8), + buildRgba(base.whiteLabel, 0), + ], whiteButton: ['#FFFFFF', '#F7F9FA'], }; @@ -302,7 +306,7 @@ const getColorsByTheme = (darkMode?: boolean) => { appleBlueTintToAppleBlue: ['#2FC3FF', base.appleBlue], blueToGreen: ['#4764F7', '#23D67F'], checkmarkAnimation: ['#1FC24A10', '#1FC24A10', '#1FC24A00'], - ens: ['#513eff', '#3e80ff'], + ens: ['#456AFF', '#5FA9EE'], lighterGrey: [buildRgba('#1F222A', 0.8), buildRgba('#1F222A', 0.6)], lightestGrey: [buildRgba('#1F222A', 0.8), buildRgba('#1F222A', 0.3)], lightestGreyReverse: [ @@ -342,6 +346,10 @@ const getColorsByTheme = (darkMode?: boolean) => { vividRainbowTint: ['#201C19', '#201723', '#112028'], warning: ['#FFD963', '#FFB200'], warningTint: ['#201F1E', '#201C18'], + white80ToTransparent: [ + buildRgba(base.whiteLabel, 0.8), + buildRgba(base.whiteLabel, 0), + ], whiteButton: ['#404656', buildRgba('#404656', 0.8)], }; diff --git a/src/utils/ens.ts b/src/utils/ens.ts index 072eed425dd..b0d6836044a 100644 --- a/src/utils/ens.ts +++ b/src/utils/ens.ts @@ -44,8 +44,8 @@ export function getENSNFTAvatarUrl( return avatarUrl; } -export function isENSNFTRecord(avatar: string) { - return avatar.includes('eip155:1'); +export function isENSNFTRecord(avatar?: string) { + return avatar?.includes('eip155:1'); } export function normalizeENS(name: string) { diff --git a/src/utils/index.ts b/src/utils/index.ts index a765b4957ce..c9191a9820a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -28,6 +28,7 @@ export { default as gasUtils } from './gas'; export { default as getBlocksFromTimestamps } from './getBlocksFromTimestamps'; export { default as getDominantColorFromImage } from './getDominantColorFromImage'; export { default as getTokenMetadata } from './getTokenMetadata'; +export { getUniqueTokenFormat, getUniqueTokenType } from './uniqueTokens'; export { default as getUrlForTrustIconFallback } from './getUrlForTrustIconFallback'; export { default as haptics } from './haptics'; export { default as isETH } from './isETH'; diff --git a/src/utils/uniqueTokens.ts b/src/utils/uniqueTokens.ts new file mode 100644 index 00000000000..9e0e6c0b170 --- /dev/null +++ b/src/utils/uniqueTokens.ts @@ -0,0 +1,57 @@ +import { UniqueAsset } from '@rainbow-me/entities'; +import isSupportedUriExtension from '@rainbow-me/helpers/isSupportedUriExtension'; +import supportedUriExtensions from '@rainbow-me/helpers/supportedUriExtensions'; + +export const uniqueTokenTypes = { + ENS: 'ENS', + NFT: 'NFT', + POAP: 'POAP', +} as const; +export type UniqueTokenType = keyof typeof uniqueTokenTypes; + +export const uniqueTokenFormats = { + '3d': '3d', + 'audio': 'audio', + 'image': 'image', + 'video': 'video', +} as const; +export type UniqueTokenFormat = keyof typeof uniqueTokenFormats; + +export function getUniqueTokenType(asset: UniqueAsset) { + const { familyName, uniqueId } = asset; + if (asset.isPoap) return uniqueTokenTypes.POAP; + if (familyName === 'ENS' && uniqueId !== 'Unknown ENS name') { + return uniqueTokenTypes.ENS; + } + return uniqueTokenTypes.NFT; +} + +export function getUniqueTokenFormat(asset: UniqueAsset) { + const { animation_url, image_url } = asset; + const assetUrl = animation_url || image_url || ''; + if ( + isSupportedUriExtension( + assetUrl, + supportedUriExtensions.SUPPORTED_3D_EXTENSIONS + ) + ) { + return uniqueTokenFormats['3d']; + } + if ( + isSupportedUriExtension( + assetUrl, + supportedUriExtensions.SUPPORTED_AUDIO_EXTENSIONS + ) + ) { + return uniqueTokenFormats.audio; + } + if ( + isSupportedUriExtension( + assetUrl, + supportedUriExtensions.SUPPORTED_VIDEO_EXTENSIONS + ) + ) { + return uniqueTokenFormats.video; + } + return uniqueTokenFormats.image; +} diff --git a/yarn.lock b/yarn.lock index 2934e529fcc..3a52650c56f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9607,6 +9607,11 @@ eventemitter3@4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" +eventemitter3@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + events@1.1.1, events@^1.0.2: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" @@ -14575,11 +14580,24 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" +p-queue@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-7.2.0.tgz#e1430e4432f09b43aa8b4913d4c2ff7fdd685479" + integrity sha512-Kvv7p13M46lTYLQ/PsZdaj/1Vj6u/8oiIJgyQyx4oVkOfHdd7M2EZvXigDvcsSzRwanCzQirV5bJPQFoSQt5MA== + dependencies: + eventemitter3 "^4.0.7" + p-timeout "^5.0.2" + p-timeout@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-5.0.2.tgz#d12964c4b2f988e15f72b455c2c428d82a0ec0a0" integrity sha512-sEmji9Yaq+Tw+STwsGAE56hf7gMy9p0tQfJojIAamB7WHJYJKf1qlsg9jqBWG8q9VCxKPhZaP/AcXwEoBcYQhQ== +p-timeout@^5.0.2: + version "5.1.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-5.1.0.tgz#b3c691cf4415138ce2d9cfe071dba11f0fee085b" + integrity sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew== + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.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 J7h{jO{|74~A-@0s literal 0 HcmV?d00001 diff --git a/src/assets/discover-profiles-card@3x.png b/src/assets/discover-profiles-card@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..d25a2248205be446299a758fac328e82b5a432d9 GIT binary patch literal 317249 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 literal 0 HcmV?d00001 diff --git a/src/components/animations/HourglassAnimation.tsx b/src/components/animations/HourglassAnimation.tsx index 70cda52f21f..4ae75988ef0 100644 --- a/src/components/animations/HourglassAnimation.tsx +++ b/src/components/animations/HourglassAnimation.tsx @@ -9,8 +9,8 @@ import Animated, { } from 'react-native-reanimated'; import { Path, Svg } from 'react-native-svg'; import { + BackgroundProvider, Box, - useColorMode, useForegroundColor, } from '@rainbow-me/design-system'; @@ -32,8 +32,6 @@ const sandConfig: AnimationConfigOptions = { const AnimatedPath = Animated.createAnimatedComponent(Path); export default function HourglassAnimation() { - const { colorMode } = useColorMode(); - const darkMode = colorMode !== 'light'; const accentColor = useForegroundColor('accent'); const rotateHourglass = useDerivedValue(() => withRepeat( @@ -70,44 +68,49 @@ export default function HourglassAnimation() { })); return ( - - - - - - - - - - + + {({ backgroundColor }) => ( + + + + + + + + + + + + )} + ); } diff --git a/src/components/asset-list/AssetListHeader.js b/src/components/asset-list/AssetListHeader.js index 2a61bd33b0f..ab4dce48a1f 100644 --- a/src/components/asset-list/AssetListHeader.js +++ b/src/components/asset-list/AssetListHeader.js @@ -55,19 +55,33 @@ const TotalAmountSkeleton = styled(Skeleton)({ }); const WalletSelectButton = ({ - truncatedAccountName, + accountName, onChangeWallet, deviceWidth, textWidth, maxWidth, }) => { const { colors } = useTheme(); + + const truncated = textWidth > maxWidth - 6; + + const truncatedAccountName = useMemo(() => { + if (textWidth > 0) { + if (truncated && accountName?.endsWith('.eth')) { + return accountName.slice(0, -4); + } + return accountName; + } + return ''; + }, [accountName, textWidth, truncated]); + return ( {truncatedAccountName} @@ -127,18 +141,6 @@ const AssetListHeader = ({ measure(); }, [accountName]); - const truncated = textWidth > maxWidth - 6; - - const truncatedAccountName = useMemo(() => { - if (textWidth > 0) { - if (truncated && accountName?.endsWith('.eth')) { - return accountName.slice(0, -4); - } - return accountName; - } - return ''; - }, [accountName, textWidth, truncated]); - return ( )} diff --git a/src/components/asset-list/RecyclerAssetList2/core/ExternalENSProfileScrollView.tsx b/src/components/asset-list/RecyclerAssetList2/core/ExternalENSProfileScrollView.tsx index 858db41f056..8ff31172e1d 100644 --- a/src/components/asset-list/RecyclerAssetList2/core/ExternalENSProfileScrollView.tsx +++ b/src/components/asset-list/RecyclerAssetList2/core/ExternalENSProfileScrollView.tsx @@ -18,6 +18,7 @@ import { ProfileSheetConfigContext } from '../../../../screens/ProfileSheet'; import ProfileSheetHeader from '../../../ens-profile/ProfileSheetHeader'; import ImagePreviewOverlay from '../../../images/ImagePreviewOverlay'; import { StickyHeaderContext } from './StickyHeaders'; +import { useAndroidScrollViewGestureHandler } from '@/hooks'; const extraPadding = { paddingBottom: 144 }; const ExternalENSProfileScrollViewWithRef = React.forwardRef< @@ -58,12 +59,15 @@ const ExternalENSProfileScrollViewWithRef = React.forwardRef< [yPosition] ); + const { onScroll: onAndroidScroll } = useAndroidScrollViewGestureHandler(); + const handleScroll = useCallback( event => { onScroll(event); + android && onAndroidScroll(event); runOnUI(scrollHandler)(event.nativeEvent.contentOffset.y); }, - [onScroll, scrollHandler] + [onAndroidScroll, onScroll, scrollHandler] ); useImperativeHandle(ref, () => scrollViewRef.current!); @@ -75,6 +79,7 @@ const ExternalENSProfileScrollViewWithRef = React.forwardRef< return ( } diff --git a/src/components/asset-list/RecyclerAssetList2/core/useMemoBriefSectionData.ts b/src/components/asset-list/RecyclerAssetList2/core/useMemoBriefSectionData.ts index e9301cf0dc8..3393465a3c5 100644 --- a/src/components/asset-list/RecyclerAssetList2/core/useMemoBriefSectionData.ts +++ b/src/components/asset-list/RecyclerAssetList2/core/useMemoBriefSectionData.ts @@ -37,17 +37,18 @@ export default function useMemoBriefSectionData({ } = {}) { let sectionsDataToUse: any[]; - if (externalAddress) { - // `externalAddress` is a static prop, so hooks will always execute in order. + if (type === 'ens-profile') { + // `type` is a static prop, so hooks will always execute in order. // eslint-disable-next-line react-hooks/rules-of-hooks sectionsDataToUse = useExternalWalletSectionsData({ address: externalAddress, + type, }).briefSectionsData; } else if (!briefSectionsData) { // briefSectionsData is an optional thing - we might send it from the tree // so we run it only once for a tree // eslint-disable-next-line react-hooks/rules-of-hooks - sectionsDataToUse = useWalletSectionsData().briefSectionsData!; + sectionsDataToUse = useWalletSectionsData({ type }).briefSectionsData!; } else { sectionsDataToUse = briefSectionsData; } diff --git a/src/components/asset-list/RecyclerAssetList2/index.tsx b/src/components/asset-list/RecyclerAssetList2/index.tsx index 4fd1ed7f272..a8330900558 100644 --- a/src/components/asset-list/RecyclerAssetList2/index.tsx +++ b/src/components/asset-list/RecyclerAssetList2/index.tsx @@ -5,17 +5,20 @@ import { RecyclerAssetListScrollPositionContext } from './core/Contexts'; import RawMemoRecyclerAssetList from './core/RawRecyclerList'; import { StickyHeaderManager } from './core/StickyHeaders'; import useMemoBriefSectionData from './core/useMemoBriefSectionData'; +import { UniqueAsset } from '@rainbow-me/entities'; export type AssetListType = 'wallet' | 'ens-profile' | 'select-nft'; function RecyclerAssetList({ walletBriefSectionsData, externalAddress, + onPressUniqueToken, type = 'wallet', }: { walletBriefSectionsData: any[]; /** An "external address" is an address that is not the current account address. */ externalAddress?: string; + onPressUniqueToken?: (asset: UniqueAsset) => void; type?: AssetListType; }) { const { @@ -30,11 +33,8 @@ function RecyclerAssetList({ const position = useMemoOne(() => new RNAnimated.Value(0), []); const extendedState = useMemo( - () => ({ - additionalData, - externalAddress, - }), - [additionalData, externalAddress] + () => ({ additionalData, externalAddress, onPressUniqueToken }), + [additionalData, externalAddress, onPressUniqueToken] ); return ( diff --git a/src/components/buttons/MiniButton.js b/src/components/buttons/MiniButton.js index 292f16287c6..93283868a01 100644 --- a/src/components/buttons/MiniButton.js +++ b/src/components/buttons/MiniButton.js @@ -113,7 +113,7 @@ export default function MiniButton({ > - {android && content} - - {ios && content} + /> + {content} ); diff --git a/src/components/callout/Callout.tsx b/src/components/callout/Callout.tsx new file mode 100644 index 00000000000..4b086be6b26 --- /dev/null +++ b/src/components/callout/Callout.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import LinearGradient from 'react-native-linear-gradient'; +import { Box, Inline, Inset, Text } from '@rainbow-me/design-system'; +import { useTheme } from '@rainbow-me/theme'; + +export default function Callout({ + after, + before, + children, +}: { + after?: React.ReactNode; + before?: React.ReactNode; + children: string; +}) { + const { colors } = useTheme(); + return ( + + + + + {before} + + {children} + + + {after} + + + + ); +} diff --git a/src/components/change-wallet/AddressRow.js b/src/components/change-wallet/AddressRow.js index 35c17172d8d..49c94a4887c 100644 --- a/src/components/change-wallet/AddressRow.js +++ b/src/components/change-wallet/AddressRow.js @@ -163,7 +163,7 @@ export default function AddressRow({ const onMenuItemPress = useMemo(() => { return getOnMenuItemPress(walletId, address, label); - }, [address, getOnMenuItemPress, walletId]); + }, [address, getOnMenuItemPress, label, walletId]); const handlePressMenuItem = useCallback( e => { @@ -215,7 +215,12 @@ export default function AddressRow({ )} {cleanedUpLabel || ens ? ( - + {cleanedUpLabel || ens} ) : ( @@ -225,6 +230,7 @@ export default function AddressRow({ firstSectionLength={6} size="smaller" style={sx.accountLabel} + testID={`change-wallet-address-row-address-${address}`} truncationLength={4} weight="medium" /> diff --git a/src/components/coin-row/CollectiblesSendRow.js b/src/components/coin-row/CollectiblesSendRow.tsx similarity index 68% rename from src/components/coin-row/CollectiblesSendRow.js rename to src/components/coin-row/CollectiblesSendRow.tsx index 85415d3b98c..3ceab84f1cb 100644 --- a/src/components/coin-row/CollectiblesSendRow.js +++ b/src/components/coin-row/CollectiblesSendRow.tsx @@ -1,9 +1,8 @@ -import PropTypes from 'prop-types'; import React, { Fragment, useMemo } from 'react'; -import { TouchableWithoutFeedback } from 'react-native'; +import { PressableProps, TouchableWithoutFeedback } from 'react-native'; import { buildAssetUniqueIdentifier } from '../../helpers/assets'; import { useTheme } from '../../theme/ThemeContext'; -import { deviceUtils, magicMemo } from '../../utils'; +import { deviceUtils, getUniqueTokenType, magicMemo } from '../../utils'; import Divider from '../Divider'; import { ButtonPressAnimation } from '../animations'; import { RequestVendorLogoIcon } from '../coin-icon'; @@ -11,6 +10,8 @@ import { Centered } from '../layout'; import { TruncatedText } from '../text'; import CoinName from './CoinName'; import CoinRow from './CoinRow'; +import { UniqueAsset } from '@rainbow-me/entities'; +import svgToPngIfNeeded from '@rainbow-me/handlers/svgs'; import { padding } from '@rainbow-me/styles'; const dividerHeight = 22; @@ -27,7 +28,13 @@ const selectedStyles = { : padding.object(15)), }; -const BottomRow = ({ selected, subtitle }) => { +const BottomRow = ({ + selected, + subtitle, +}: { + selected: boolean; + subtitle: string; +}) => { const { colors } = useTheme(); return ( @@ -46,7 +53,15 @@ const BottomRow = ({ selected, subtitle }) => { ); }; -const TopRow = ({ id, name, selected }) => { +const TopRow = ({ + id, + name, + selected, +}: { + id: number; + name: string; + selected: boolean; +}) => { const { colors } = useTheme(); return ( @@ -61,22 +76,25 @@ const TopRow = ({ id, name, selected }) => { }; const UniqueTokenCoinIcon = magicMemo( - ({ - collection: { name }, - background, - image_thumbnail_url, - selected, - shouldPrioritizeImageLoading, - ...props - }) => { + asset => { + const { + collection: { name }, + background, + image_thumbnail_url, + image_url, + selected, + shouldPrioritizeImageLoading, + ...props + } = asset; const { colors } = useTheme(); + const imageUrl = svgToPngIfNeeded(image_thumbnail_url || image_url, true); return ( - buildAssetUniqueIdentifier(props.item) !== - buildAssetUniqueIdentifier(nextProps.item); - -// eslint-disable-next-line react/display-name const CollectiblesSendRow = React.memo( ({ disablePressAnimation, @@ -108,15 +114,26 @@ const CollectiblesSendRow = React.memo( selected, testID, ...props + }: { + disablePressAnimation?: boolean; + item: UniqueAsset; + isFirstRow?: boolean; + onPress: PressableProps['onPress']; + selected?: boolean; + testID: string; }) => { const { colors } = useTheme(); + + const uniqueTokenType = getUniqueTokenType(item); + const isENS = uniqueTokenType === 'ENS'; + const subtitle = useMemo( () => - item.name + item.name && !isENS ? `${item.collection.name} #${item.id}` : item.collection.name, - [item.collection.name, item.id, item.name] + [isENS, item.collection.name, item.id, item.name] ); const Wrapper = disablePressAnimation @@ -127,10 +144,12 @@ const CollectiblesSendRow = React.memo( {isFirstRow && ( + {/* @ts-expect-error JavaScript component */} )} + {/* @ts-expect-error JavaScript component */} ); }, - arePropsEqual + (props, nextProps) => + buildAssetUniqueIdentifier(props.item) !== + buildAssetUniqueIdentifier(nextProps.item) ); -CollectiblesSendRow.propTypes = { - isFirstRow: PropTypes.bool, - item: PropTypes.object, - onPress: PropTypes.func, - selected: PropTypes.bool, - subtitle: PropTypes.string, -}; - +CollectiblesSendRow.displayName = 'CollectiblesSendRow'; +// @ts-expect-error CollectiblesSendRow.dividerHeight = dividerHeight; +// @ts-expect-error CollectiblesSendRow.selectedHeight = selectedHeight; export default CollectiblesSendRow; diff --git a/src/components/contacts/ContactRow.js b/src/components/contacts/ContactRow.js index fbfcea13653..fe947ddeab6 100644 --- a/src/components/contacts/ContactRow.js +++ b/src/components/contacts/ContactRow.js @@ -19,11 +19,7 @@ import { isENSAddressFormat, isValidDomainFormat, } from '@rainbow-me/helpers/validators'; -import { - useContacts, - useDimensions, - useENSProfileImages, -} from '@rainbow-me/hooks'; +import { useContacts, useDimensions, useENSAvatar } from '@rainbow-me/hooks'; import styled from '@rainbow-me/styled-components'; import { margin } from '@rainbow-me/styles'; import { @@ -111,7 +107,7 @@ const ContactRow = ( const [ensName, setENSName] = useState(initialENSName); - const { data: images } = useENSProfileImages(ensName, { + const { data: ensAvatar } = useENSAvatar(ensName, { enabled: profilesEnabled && Boolean(ensName), }); @@ -161,7 +157,7 @@ const ContactRow = ( } }, [accountType, address, ensName, nickname, onPress, showcaseItem]); - const imageAvatar = profilesEnabled ? images?.avatarUrl : image; + const imageAvatar = profilesEnabled ? ensAvatar?.imageUrl : image; const emoji = useMemo(() => (address ? addressHashedEmoji(address) : ''), [ address, diff --git a/src/components/contacts/ImageAvatar.js b/src/components/contacts/ImageAvatar.js index 68ae4185682..cb04a011004 100644 --- a/src/components/contacts/ImageAvatar.js +++ b/src/components/contacts/ImageAvatar.js @@ -45,6 +45,11 @@ const sizeConfigs = (colors, isDarkMode) => ({ dimensions: 20, textSize: 'small', }, + smedium: { + dimensions: 36, + shadow: [[0, 4, android ? 5 : 12, colors.shadow, 0.4]], + textSize: 'large', + }, }); const Avatar = styled(ImgixImage)(({ dimensions }) => ({ @@ -85,5 +90,6 @@ const ImageAvatar = ({ image, size = 'medium', ...props }) => { ); }; +const arePropsEqual = (prev, next) => prev.image === next.image; -export default React.memo(ImageAvatar); +export default React.memo(ImageAvatar, arePropsEqual); diff --git a/src/components/discover-sheet/DPICard.tsx b/src/components/discover-sheet/DPICard.tsx new file mode 100644 index 00000000000..732a61a4c7e --- /dev/null +++ b/src/components/discover-sheet/DPICard.tsx @@ -0,0 +1,168 @@ +import lang from 'i18n-js'; +import React, { useCallback } from 'react'; +import { ViewStyle } from 'react-native'; +import LinearGradient from 'react-native-linear-gradient'; +import { CustomShadow } from '../../design-system/layout/shadow'; +import { useNavigation } from '../../navigation/Navigation'; +import { ButtonPressAnimation } from '../animations'; +import { CoinIcon } from '../coin-icon'; +import { analytics } from '@rainbow-me/analytics'; +import { + AccentColorProvider, + Bleed, + Box, + ColorModeProvider, + Column, + Columns, + Cover, + Heading, + Inset, + Stack, + Text, + useForegroundColor, +} from '@rainbow-me/design-system'; +import { useAccountSettings } from '@rainbow-me/hooks'; +import store from '@rainbow-me/redux/store'; +import { DPI_ADDRESS } from '@rainbow-me/references'; +import Routes from '@rainbow-me/routes'; +import { useTheme } from '@rainbow-me/theme'; +import { ethereumUtils } from '@rainbow-me/utils'; + +const ButtonShadow: ViewStyle = { + shadowColor: 'black', + shadowOffset: { height: 4, width: 0 }, + shadowOpacity: 0.15, + shadowRadius: 6, +}; + +const CardShadow: CustomShadow = { + custom: { + android: { + color: 'accent', + elevation: 24, + opacity: 0.5, + }, + ios: [ + { + blur: 24, + color: 'accent', + offset: { x: 0, y: 8 }, + opacity: 0.35, + }, + ], + }, +}; + +export default function DPICard() { + const { nativeCurrency } = useAccountSettings(); + const { navigate } = useNavigation(); + const { colors } = useTheme(); + + const handlePress = useCallback(() => { + const asset = ethereumUtils.formatGenericAsset( + store.getState().data?.genericAssets?.[DPI_ADDRESS], + nativeCurrency + ); + + analytics.track('Pressed DPI Button', { category: 'discover' }); + + navigate(Routes.TOKEN_INDEX_SHEET, { + asset, + backgroundOpacity: 1, + cornerRadius: 39, + fromDiscover: true, + type: 'token_index', + }); + }, [nativeCurrency, navigate]); + + const shadow = useForegroundColor('shadow'); + const shadowColor = useForegroundColor({ + custom: { + dark: shadow, + light: '#8360F7', + }, + }); + + return ( + + + + + + + + + + + {lang.t('discover.dpi.title')} + + + {lang.t('discover.dpi.body')} + + + + + {/* @ts-expect-error JavaScript component */} + + + + + + + + + + 􀦌 {lang.t('discover.dpi.view')} + + + + + + + + + + + + + ); +} diff --git a/src/components/discover-sheet/DiscoverHome.js b/src/components/discover-sheet/DiscoverHome.js index 00f39d08cd9..3e123331741 100644 --- a/src/components/discover-sheet/DiscoverHome.js +++ b/src/components/discover-sheet/DiscoverHome.js @@ -1,12 +1,15 @@ import React from 'react'; import useExperimentalFlag, { PROFILES } from '../../config/experimentalHooks'; import BottomSpacer from './BottomSpacer'; +import DPICard from './DPICard'; +import ENSCreateProfileCard from './ENSCreateProfileCard'; +import ENSSearchCard from './ENSSearchCard'; +import GasCard from './GasCard'; import Lists from './ListsSection'; import PulseIndex from './PulseIndexSection'; -import RegisterENS from './RegisterENSSection'; -// import Strategies from './StrategiesSection'; import TopMoversSection from './TopMoversSection'; import UniswapPools from './UniswapPoolsSection'; +import { Columns, Inset, Stack } from '@rainbow-me/design-system'; import { useAccountSettings } from '@rainbow-me/hooks'; export default function DiscoverHome() { @@ -14,12 +17,31 @@ export default function DiscoverHome() { const profilesEnabled = useExperimentalFlag(PROFILES); return ( - - {profilesEnabled && } - - - {/* */} - {accountAddress ? : null} + + + {profilesEnabled ? ( + + + + + + + + + + + ) : ( + + + + + )} + + + {accountAddress ? : null} + + + ); diff --git a/src/components/discover-sheet/DiscoverSearch.js b/src/components/discover-sheet/DiscoverSearch.js index a192871f4e0..99d51427b4a 100644 --- a/src/components/discover-sheet/DiscoverSearch.js +++ b/src/components/discover-sheet/DiscoverSearch.js @@ -62,7 +62,31 @@ export default function DiscoverSearch() { searchQueryForSearch ); const currencyList = useMemo(() => { - let list = [...swapCurrencyList, ...ensResults]; + // order: + // 1. favorites + // 2. verified + // 3. profiles + // 4. unverified + // 5. low liquidity + let list = swapCurrencyList; + const listKeys = swapCurrencyList.map(item => item.key); + + const profilesSecond = + (listKeys[0] === 'favorites' && listKeys[1] !== 'verified') || + listKeys[0] === 'verified'; + const profilesThird = + listKeys[1] === 'verified' || + listKeys[0] === 'unfilteredFavorites' || + (listKeys[0] === 'favorites' && listKeys[1] === 'verified'); + + if (profilesSecond) { + list.splice(1, 0, ...ensResults); + } else if (profilesThird) { + list.splice(2, 0, ...ensResults); + } else { + list = [...ensResults, ...swapCurrencyList]; + } + // ONLY FOR e2e!!! Fake tokens with same symbols break detox e2e tests if (IS_TESTING === 'true') { let symbols = []; diff --git a/src/components/discover-sheet/DiscoverSearchContainer.js b/src/components/discover-sheet/DiscoverSearchContainer.js index 1bca14451c3..de554586d1f 100644 --- a/src/components/discover-sheet/DiscoverSearchContainer.js +++ b/src/components/discover-sheet/DiscoverSearchContainer.js @@ -143,7 +143,7 @@ export default forwardRef(function DiscoverSearchContainer( return ( <> - + ({ + custom: { + android: { + elevation: 24, + opacity: 0.5, + }, + ios: [ + { + blur: 24, + offset: { x: 0, y: 8 }, + opacity: colorMode === 'dark' ? 0.3 : 0.1, + }, + { + blur: 6, + offset: { x: 0, y: 2 }, + opacity: 0.02, + }, + ], + }, + }), + [colorMode] + ); + + const cardStyle = useMemo( + () => ({ + borderColor: `rgba(0, 0, 0, ${colorMode === 'dark' ? '0' : '0.1'})`, + borderWidth: CARD_BORDER_WIDTH, + overflow: 'hidden' as 'hidden', + }), + [colorMode] + ); + + const handlePress = useCallback(() => { + if (!isReadOnlyWallet || enableActionsOnReadOnlyWallet) { + navigate(Routes.REGISTER_ENS_NAVIGATOR, { + fromDiscover: true, + }); + } else { + watchingAlert(); + } + }, [isReadOnlyWallet, navigate]); + + const { uniqueDomain } = useAccountENSDomains(); + + useEffect(() => { + if (uniqueDomain?.name) { + prefetchENSAvatar(uniqueDomain.name); + prefetchENSRecords(uniqueDomain.name); + } + }, [uniqueDomain]); + + useEffect(() => { + // Preload intro screen preview marquee ENS images + ImgixImage.preload( + ensIntroMarqueeNames.map(name => ({ uri: ensAvatarUrl(name) })) + ); + }, []); + + const shadow = useForegroundColor('shadow'); + const shadowColor = useForegroundColor({ + custom: { + dark: shadow, + light: shadow, + }, + }); + + return ( + + + + + + + + {/* @ts-expect-error JavaScript component */} + + + + + + + {lang.t('discover.ens_create_profile.title')} + + + {lang.t('discover.ens_create_profile.body')} + + + + + + 􀜍 + + + + + + + + + + + + ); +} diff --git a/src/components/discover-sheet/ENSSearchCard.tsx b/src/components/discover-sheet/ENSSearchCard.tsx new file mode 100644 index 00000000000..0278643f46c --- /dev/null +++ b/src/components/discover-sheet/ENSSearchCard.tsx @@ -0,0 +1,249 @@ +import MaskedView from '@react-native-masked-view/masked-view'; +import lang from 'i18n-js'; +import React, { useCallback, useEffect } from 'react'; +import LinearGradient from 'react-native-linear-gradient'; +import Animated, { + useAnimatedStyle, + useSharedValue, + withSpring, +} from 'react-native-reanimated'; +import { CustomShadow } from '../../design-system/layout/shadow'; +import { useNavigation } from '../../navigation/Navigation'; +import { ButtonPressAnimation } from '../animations'; +import { enableActionsOnReadOnlyWallet } from '@rainbow-me/config'; +import { + AccentColorProvider, + Box, + ColorModeProvider, + Cover, + Heading, + Inset, + Stack, + Text, + useForegroundColor, +} from '@rainbow-me/design-system'; +import { REGISTRATION_MODES } from '@rainbow-me/helpers/ens'; +import { + useDimensions, + useENSPendingRegistrations, + useWallets, +} from '@rainbow-me/hooks'; +import Routes from '@rainbow-me/routes'; +import { useTheme } from '@rainbow-me/theme'; +import { watchingAlert } from '@rainbow-me/utils'; + +const CardShadow: CustomShadow = { + custom: { + android: { + color: 'accent', + elevation: 24, + opacity: 0.5, + }, + ios: [ + { + blur: 24, + color: 'accent', + offset: { x: 0, y: 8 }, + opacity: 0.35, + }, + ], + }, +}; + +const springConfig = { + damping: 20, + mass: 1, + stiffness: 300, +}; + +export default function ENSSearchCard() { + const { width: deviceWidth } = useDimensions(); + const { pendingRegistrations } = useENSPendingRegistrations(); + const { navigate } = useNavigation(); + const { colors } = useTheme(); + const { isReadOnlyWallet } = useWallets(); + + const pendingBadgeProgress = useSharedValue(0); + + useEffect(() => { + if (pendingRegistrations?.length > 0) { + // This setTimeout prevents the badge from appearing before the number in the badge is updated. + setTimeout(() => { + pendingBadgeProgress.value = withSpring(1, springConfig); + }, 0); + } else { + pendingBadgeProgress.value = withSpring(0, springConfig); + } + }, [pendingBadgeProgress, pendingRegistrations?.length]); + + const handlePress = useCallback(() => { + if (!isReadOnlyWallet || enableActionsOnReadOnlyWallet) { + navigate(Routes.REGISTER_ENS_NAVIGATOR, { + fromDiscover: true, + mode: REGISTRATION_MODES.SEARCH, + }); + } else { + watchingAlert(); + } + }, [isReadOnlyWallet, navigate]); + + const shadow = useForegroundColor('shadow'); + const shadowColor = useForegroundColor({ + custom: { + dark: shadow, + light: '#4C79FD', + }, + }); + + const pendingBadgeStyle = useAnimatedStyle(() => { + return { + opacity: 1 * pendingBadgeProgress.value, + transform: [ + { + scale: 1 * pendingBadgeProgress.value, + }, + ], + }; + }, [pendingRegistrations]); + + const searchIconStyle = useAnimatedStyle(() => { + return { + opacity: 1 * (1 - pendingBadgeProgress.value), + transform: [ + { + scale: 1 * (1 - pendingBadgeProgress.value), + }, + ], + }; + }, [pendingRegistrations]); + + return ( + + + + + + + + + + + + + {pendingRegistrations?.length} + + + + + + + + + + + 􀊫 + + + } + style={{ height: '100%', width: '100%' }} + > + + + + + + + + + + + + + + + + {lang.t('discover.ens_search.mini_title')} + + + + {/* RN seems to treat "a .eth" as a URL and prevents line breaks between "a" and ".eth". ​ is a zero width character that allows a line break. */} + {lang.t('discover.ens_search.title_part_1')}​ + {lang.t('discover.ens_search.title_part_2')} + + + + + + + + + + + ); +} diff --git a/src/components/discover-sheet/GasCard.tsx b/src/components/discover-sheet/GasCard.tsx new file mode 100644 index 00000000000..c1cbd4ac95c --- /dev/null +++ b/src/components/discover-sheet/GasCard.tsx @@ -0,0 +1,361 @@ +// @ts-expect-error +import AnimateNumber from '@bankify/react-native-animate-number'; +import { useIsFocused } from '@react-navigation/native'; +import lang from 'i18n-js'; +import { isNaN } from 'lodash'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import Animated, { + Easing, + useAnimatedStyle, + useSharedValue, + withSequence, + withSpring, + withTiming, +} from 'react-native-reanimated'; +import { ButtonPressAnimation } from '../animations'; +import { + AccentColorProvider, + Box, + Heading, + Inset, + Stack, + Text, + useColorMode, + useForegroundColor, +} from '@rainbow-me/design-system'; +import { add } from '@rainbow-me/helpers/utilities'; +import { useDimensions, useGas } from '@rainbow-me/hooks'; +import { useTheme } from '@rainbow-me/theme'; +import { gasUtils } from '@rainbow-me/utils'; + +type AnimationConfigOptions = { + duration: number; + easing: Animated.EasingFunction; +}; + +const containerConfig = { + damping: 15, + mass: 1, + stiffness: 200, +}; +const pulseConfig = { + damping: 66, + mass: 1, + stiffness: 333, +}; +const fadeOutConfig: AnimationConfigOptions = { + duration: 600, + easing: Easing.bezierFn(0.76, 0, 0.24, 1), +}; + +export default function GasCard() { + const { colorMode } = useColorMode(); + const { width: deviceWidth } = useDimensions(); + const { + currentBlockParams, + gasFeeParamsBySpeed, + startPollingGasFees, + stopPollingGasFees, + } = useGas(); + const isFocused = useIsFocused(); + const [lastKnownGwei, setLastKnownGwei] = useState(''); + const { colors } = useTheme(); + + const container = useSharedValue(1); + const opacity = useSharedValue(0); + const scale = useSharedValue(0); + + const cardShadow = useMemo( + () => ({ + custom: { + android: { + elevation: 24, + opacity: 0.5, + }, + ios: [ + { + blur: 24, + offset: { x: 0, y: 8 }, + opacity: colorMode === 'dark' ? 0.3 : 0.1, + }, + { + blur: 6, + offset: { x: 0, y: 2 }, + opacity: 0.02, + }, + ], + }, + }), + [colorMode] + ); + + // Listen to gas prices + useEffect(() => { + if (isFocused) { + startPollingGasFees(); + } else { + stopPollingGasFees(); + } + }, [isFocused, startPollingGasFees, stopPollingGasFees]); + + const { NORMAL } = gasUtils; + + const currentGwei = useMemo( + () => + add( + currentBlockParams?.baseFeePerGas?.gwei, + gasFeeParamsBySpeed[NORMAL]?.maxPriorityFeePerGas?.gwei + ), + [NORMAL, currentBlockParams?.baseFeePerGas?.gwei, gasFeeParamsBySpeed] + ); + const isCurrentGweiLoaded = currentGwei && Number(currentGwei) > 0; + + const renderGweiText = useCallback( + animatedNumber => { + const priceText = + animatedNumber === 0 + ? isCurrentGweiLoaded + ? Math.round(Number(currentGwei)) + : Math.round(Number(lastKnownGwei)) || '􀖇' + : animatedNumber; + return ( + + {priceText} + + ); + }, + [currentGwei, isCurrentGweiLoaded, lastKnownGwei] + ); + + const formatGasPrice = useCallback(animatedValue => { + if (animatedValue === null || isNaN(animatedValue)) { + return 0; + } else { + return Math.round(animatedValue); + } + }, []); + + const handlePress = useCallback(() => { + opacity.value = 0; + scale.value = 0; + container.value = withSequence( + withSpring(1.1, containerConfig), + withSpring(1, pulseConfig) + ); + opacity.value = withSequence( + withSpring(1, pulseConfig), + withTiming(0, fadeOutConfig), + withTiming(0, { duration: 0 }) + ); + scale.value = withSequence( + withSpring(1, pulseConfig), + withTiming(1, fadeOutConfig), + withTiming(0, { duration: 0 }) + ); + }, [container, opacity, scale]); + + const cardColor = useForegroundColor({ + custom: { + dark: '#22232A', + light: '#FFFFFF', + }, + }); + const grey = useForegroundColor('secondary60'); + const green = useForegroundColor({ + custom: { + dark: colors.green, + light: '#1DB847', + }, + }); + const blue = useForegroundColor({ + custom: { + dark: '#5FA9EE', + light: '#3157D3', + }, + }); + const orange = useForegroundColor({ + custom: { + dark: '#FF983D', + light: '#FF801F', + }, + }); + const pink = useForegroundColor({ + custom: { + dark: colors.pink, + light: '#FF5CA0', + }, + }); + + const getColorForGwei = (currentGwei: string, lastKnownGwei: string) => { + 'worklet'; + const gwei = + Math.round(Number(currentGwei)) || Math.round(Number(lastKnownGwei)); + + if (!gwei) { + return grey; + } else if (gwei < 40) { + return green; + } else if (gwei < 100) { + return blue; + } else if (gwei < 200) { + return orange; + } else { + return pink; + } + }; + + const getCurrentPriceComparison = ( + currentGwei: string, + lastKnownGwei: string + ) => { + const gwei = + Math.round(Number(currentGwei)) || Math.round(Number(lastKnownGwei)); + + if (!gwei) { + return lang.t('discover.gas.loading'); + } else if (gwei < 30) { + return lang.t('discover.gas.very_low'); + } else if (gwei < 40) { + return lang.t('discover.gas.low'); + } else if (gwei < 100) { + return lang.t('discover.gas.average'); + } else if (gwei < 200) { + return lang.t('discover.gas.high'); + } else { + return lang.t('discover.gas.surging'); + } + }; + + useEffect(() => { + if ( + isCurrentGweiLoaded && + Math.round(Number(currentGwei)) !== Math.round(Number(lastKnownGwei)) + ) { + setLastKnownGwei(currentGwei); + opacity.value = 0; + scale.value = 0; + container.value = withSequence( + withSpring(1.1, containerConfig), + withSpring(1, pulseConfig) + ); + opacity.value = withSequence( + withSpring(1, pulseConfig), + withTiming(0, fadeOutConfig), + withTiming(0, { duration: 0 }) + ); + scale.value = withSequence( + withSpring(1, pulseConfig), + withTiming(1, fadeOutConfig), + withTiming(0, { duration: 0 }) + ); + } + }, [ + container, + currentGwei, + isCurrentGweiLoaded, + lastKnownGwei, + opacity, + scale, + ]); + + const containerStyle = useAnimatedStyle(() => { + return { + transform: [ + { + scale: 1 * container.value, + }, + ], + }; + }, [currentGwei, lastKnownGwei]); + + const pulseStyle = useAnimatedStyle(() => { + const color = getColorForGwei(currentGwei, lastKnownGwei); + + return { + backgroundColor: color, + borderRadius: 24, + height: '100%', + opacity: 0.08 * opacity.value, + transform: [ + { + scale: 1 * scale.value, + }, + ], + width: '100%', + }; + }, [currentGwei, lastKnownGwei]); + + return ( + + + + + + + + + + + 1 - --t * t * t * t} + value={currentGwei || lastKnownGwei} + /> + + {!isCurrentGweiLoaded && !lastKnownGwei + ? '' + : lang.t('discover.gas.gwei')} + + + + + + + {lang.t('discover.gas.network_fees')} + + + {getCurrentPriceComparison(currentGwei, lastKnownGwei)} + + + + + + + + + + + ); +} diff --git a/src/components/discover-sheet/ListsSection.js b/src/components/discover-sheet/ListsSection.js index ec6687a6786..482c7b43fc5 100644 --- a/src/components/discover-sheet/ListsSection.js +++ b/src/components/discover-sheet/ListsSection.js @@ -243,7 +243,14 @@ export default function ListSection() { ); return ( - + {lang.t('discover.lists.lists_title')} diff --git a/src/components/discover-sheet/PulseIndexSection.js b/src/components/discover-sheet/PulseIndexSection.js index 7f7576d6ecc..74565455d6b 100644 --- a/src/components/discover-sheet/PulseIndexSection.js +++ b/src/components/discover-sheet/PulseIndexSection.js @@ -154,6 +154,7 @@ const PulseIndex = () => { { - if (!isReadOnlyWallet || enableActionsOnReadOnlyWallet) { - navigate(Routes.REGISTER_ENS_NAVIGATOR, { - fromDiscover: true, - }); - } else { - watchingAlert(); - } - }, [isReadOnlyWallet, navigate]); - - useEffect(() => { - // Preload intro screen preview marquee ENS images - ImgixImage.preload( - ensIntroMarqueeNames.map(name => ({ uri: ensAvatarUrl(name) })) - ); - }, []); - - const shadow = useForegroundColor('shadow'); - const shadowColor = useForegroundColor({ - custom: { - dark: shadow, - light: colors.gradients.ens[1], - }, - }); - - return ( - - - - - - - - - - - - - {lang.t('profiles.banner.register_name')} - - - {lang.t('profiles.banner.and_create_ens_profile')} - - - - - - - 􀯼 - - - - - - - - ); -} diff --git a/src/components/discover-sheet/TopMoversSection.js b/src/components/discover-sheet/TopMoversSection.js index a2f0417d360..dc70025951d 100644 --- a/src/components/discover-sheet/TopMoversSection.js +++ b/src/components/discover-sheet/TopMoversSection.js @@ -89,7 +89,7 @@ export default function TopMoversSection() { ); return ( - + {(gainerItems?.length > 0 || loserItems?.length > 0) && ( @@ -106,6 +106,7 @@ export default function TopMoversSection() { {gainerItems?.length !== 0 && ( + 🐋 diff --git a/src/components/ens-profile/ActionButtons/MoreButton.tsx b/src/components/ens-profile/ActionButtons/MoreButton.tsx index dd6f2829ebe..b564980cea1 100644 --- a/src/components/ens-profile/ActionButtons/MoreButton.tsx +++ b/src/components/ens-profile/ActionButtons/MoreButton.tsx @@ -1,23 +1,30 @@ +import { useRoute } from '@react-navigation/core'; import lang from 'i18n-js'; import React, { useCallback, useMemo } from 'react'; -import { Keyboard } from 'react-native'; -import { - ContextMenuButton, - MenuActionConfig, -} from 'react-native-ios-context-menu'; +import { Keyboard, Share } from 'react-native'; +import { MenuActionConfig } from 'react-native-ios-context-menu'; import { showDeleteContactActionSheet } from '../../contacts'; import More from '../MoreButton/MoreButton'; -import { useClipboard, useContacts } from '@rainbow-me/hooks'; +import ContextMenuButton from '@/components/native-context-menu/contextMenu'; +import { + useClipboard, + useContacts, + useWallets, + useWatchWallet, +} from '@rainbow-me/hooks'; import { useNavigation } from '@rainbow-me/navigation'; +import { RAINBOW_PROFILES_BASE_URL } from '@rainbow-me/references'; import Routes from '@rainbow-me/routes'; -import { ethereumUtils, showActionSheetWithOptions } from '@rainbow-me/utils'; +import { ethereumUtils } from '@rainbow-me/utils'; import { formatAddressForDisplay } from '@rainbow-me/utils/abbreviations'; const ACTIONS = { ADD_CONTACT: 'add-contact', COPY_ADDRESS: 'copy-address', ETHERSCAN: 'etherscan', + OPEN_WALLET: 'open-wallet', REMOVE_CONTACT: 'remove-contact', + SHARE: 'share', }; export default function MoreButton({ @@ -27,14 +34,23 @@ export default function MoreButton({ address?: string; ensName?: string; }) { + const { switchToWalletWithAddress, selectedWallet } = useWallets(); + const { isWatching } = useWatchWallet({ address }); const { navigate } = useNavigation(); const { setClipboard } = useClipboard(); const { contacts, onRemoveContact } = useContacts(); + const { + params: { setIsSearchModeEnabled }, + } = useRoute(); + const isSelectedWallet = useMemo(() => { + const visibleWallet = selectedWallet.addresses.find( + (wallet: { visible: boolean }) => wallet.visible + ); - const contact = useMemo( - () => (address ? contacts[address.toLowerCase()] : undefined), - [address, contacts] - ); + return visibleWallet.address.toLowerCase() === address?.toLowerCase(); + }, [selectedWallet.addresses, address]); + + const contact = address ? contacts[address.toLowerCase()] : undefined; const formattedAddress = useMemo( () => (address ? formatAddressForDisplay(address, 4, 4) : ''), @@ -43,6 +59,14 @@ export default function MoreButton({ const menuItems = useMemo(() => { return [ + isWatching && { + actionKey: ACTIONS.OPEN_WALLET, + actionTitle: lang.t('profiles.details.open_wallet'), + icon: { + iconType: 'SYSTEM', + iconValue: 'iphone.and.arrow.forward', + }, + }, { actionKey: ACTIONS.COPY_ADDRESS, actionTitle: lang.t('profiles.details.copy_address'), @@ -77,11 +101,26 @@ export default function MoreButton({ iconValue: 'link', }, }, - ] as MenuActionConfig[]; - }, [contact, formattedAddress]); + { + actionKey: ACTIONS.SHARE, + actionTitle: lang.t('profiles.details.share'), + icon: { + iconType: 'SYSTEM', + iconValue: 'square.and.arrow.up', + }, + }, + ].filter(Boolean) as MenuActionConfig[]; + }, [isWatching, formattedAddress, contact]); const handlePressMenuItem = useCallback( - ({ nativeEvent: { actionKey } }) => { + async ({ nativeEvent: { actionKey } }) => { + if (actionKey === ACTIONS.OPEN_WALLET) { + if (!isSelectedWallet) { + setIsSearchModeEnabled?.(false); + switchToWalletWithAddress(address); + } + navigate(Routes.WALLET_SCREEN); + } if (actionKey === ACTIONS.COPY_ADDRESS) { setClipboard(address); } @@ -105,31 +144,34 @@ export default function MoreButton({ }); android && Keyboard.dismiss(); } + if (actionKey === ACTIONS.SHARE) { + const walletDisplay = ensName || address; + const shareLink = `${RAINBOW_PROFILES_BASE_URL}/${walletDisplay}`; + Share.share(android ? { message: shareLink } : { url: shareLink }); + } }, - [address, contact, ensName, navigate, onRemoveContact, setClipboard] + [ + address, + contact, + ensName, + isSelectedWallet, + navigate, + onRemoveContact, + setClipboard, + setIsSearchModeEnabled, + switchToWalletWithAddress, + ] ); - const handleAndroidPress = useCallback(() => { - const actionSheetOptions = menuItems - .map(item => item?.actionTitle) - .filter(Boolean) as any; - - showActionSheetWithOptions( - { - options: actionSheetOptions, - }, - async (buttonIndex: number) => { - const actionKey = menuItems[buttonIndex]?.actionKey; - handlePressMenuItem({ nativeEvent: { actionKey } }); - } - ); - }, [handlePressMenuItem, menuItems]); - + const menuConfig = useMemo( + () => ({ menuItems, ...(ios && { menuTitle: '' }) }), + [menuItems] + ); return ( (); const { enableZoomableImages } = useContext(ProfileSheetConfigContext); const { layout } = useContext(ModalContext) || {}; - + const profilesEnabled = useExperimentalFlag(PROFILES); const ensName = defaultEnsName || params?.address; - const { data: profile } = useENSProfile(ensName); - const { data: images, isFetched: isImagesFetched } = useENSProfileImages( - ensName - ); - const profileAddress = profile?.primary?.address ?? ''; - const { navigate } = useNavigation(); - const { data: uniqueTokens } = useFetchUniqueTokens({ - address: profileAddress, - }); - - const handleSelectNFT = useCallback( - (uniqueToken: UniqueAsset) => { - navigate(Routes.EXPANDED_ASSET_SHEET, { - asset: uniqueToken, - backgroundOpacity: 1, - cornerRadius: 'device', - external: true, - springDamping: 1, - topOffset: 0, - transitionDuration: 0.25, - type: 'unique_token', - }); - }, - [navigate] - ); - - const getUniqueToken = useCallback( - (avatarOrCover: string) => { - const { contractAddress, tokenId } = parseENSNFTRecord(avatarOrCover); - const uniqueToken = uniqueTokens?.find( - token => - token.asset_contract.address === contractAddress && - token.id === tokenId - ); - return uniqueToken; - }, - [uniqueTokens] - ); - - const avatarUrl = images?.avatarUrl; - - const { enableZoomOnPressAvatar, onPressAvatar } = useMemo(() => { - const avatar = profile?.records?.avatar; - - const isNFTAvatar = avatar && isENSNFTRecord(avatar); - const avatarUniqueToken = isNFTAvatar && getUniqueToken(avatar); - - const onPressAvatar = avatarUniqueToken - ? () => handleSelectNFT(avatarUniqueToken) - : undefined; - - const enableZoomOnPressAvatar = enableZoomableImages && !onPressAvatar; - - return { - enableZoomOnPressAvatar, - onPressAvatar, - }; - }, [ - enableZoomableImages, - getUniqueToken, - handleSelectNFT, - profile?.records?.avatar, - ]); - const coverUrl = images?.coverUrl; - - const { enableZoomOnPressCover, onPressCover } = useMemo(() => { - const cover = profile?.records?.cover; - - const isNFTCover = cover && isENSNFTRecord(cover); - const coverUniqueToken = isNFTCover && getUniqueToken(cover); - - const onPressCover = coverUniqueToken - ? () => handleSelectNFT(coverUniqueToken) - : undefined; + const { data: profileAddress } = useENSAddress(ensName, { + enabled: profilesEnabled, + }); + const { data: { coinAddresses, records } = {} } = useENSRecords(ensName, { + enabled: profilesEnabled, + }); + const { data: avatar, isFetched: isAvatarFetched } = useENSAvatar(ensName, { + enabled: profilesEnabled, + }); + const { data: cover, isFetched: isCoverFetched } = useENSCover(ensName, { + enabled: profilesEnabled, + }); + const isImagesFetched = isAvatarFetched && isCoverFetched; - const enableZoomOnPressCover = enableZoomableImages && !onPressCover; + const { data: uniqueTokens } = useFetchUniqueTokens({ + address: profileAddress ?? '', + // Don't want to refetch tokens if we already have them. + staleTime: Infinity, + }); - return { - coverUrl, - enableZoomOnPressCover, - onPressCover, - }; - }, [ - coverUrl, - enableZoomableImages, - getUniqueToken, - handleSelectNFT, - profile?.records?.cover, - ]); + const avatarUrl = avatar?.imageUrl; + const { onPress: onPressAvatar } = useOpenENSNFTHandler({ + uniqueTokens, + value: records?.avatar, + }); + const enableZoomOnPressAvatar = enableZoomableImages && !onPressAvatar; - const { data: firstTransactionTimestamp } = useFirstTransactionTimestamp({ - ensName, + const coverUrl = maybeSignUri(cover?.imageUrl || undefined, { w: 400 }); + const { onPress: onPressCover } = useOpenENSNFTHandler({ + uniqueTokens, + value: records?.header, }); + const enableZoomOnPressCover = enableZoomableImages && !onPressCover; + + const { data: firstTransactionTimestamp } = useENSFirstTransactionTimestamp( + ensName + ); const emoji = useMemo( () => (profileAddress ? addressHashedEmoji(profileAddress) : ''), @@ -174,7 +119,7 @@ export default function ProfileSheetHeader({ {!isLoading && ( @@ -189,10 +134,8 @@ export default function ProfileSheetHeader({ <> {isLoading ? ( - ) : profile?.records?.description ? ( - + ) : records?.description ? ( + ) : null} @@ -200,12 +143,12 @@ export default function ProfileSheetHeader({ ) : ( <> - {profile?.records && ( + {records && ( - Since {format(firstTransactionTimestamp, 'MMM yyyy')} + {`${lang.t(`profiles.records.since`)} ${format( + firstTransactionTimestamp, + 'MMM yyyy' + )}`} )} @@ -135,11 +139,12 @@ function Tag({ {symbol ? `${symbol} ` : ''} - {children} + {children as string} diff --git a/src/components/ens-registration/ConfirmContent/EditContent.tsx b/src/components/ens-registration/ConfirmContent/EditContent.tsx new file mode 100644 index 00000000000..4dc438ee330 --- /dev/null +++ b/src/components/ens-registration/ConfirmContent/EditContent.tsx @@ -0,0 +1,77 @@ +import lang from 'i18n-js'; +import React from 'react'; +import { Switch } from 'react-native-gesture-handler'; +import ButtonPressAnimation from '../../animations/ButtonPressAnimation'; +import { + Divider, + Inline, + Inset, + Row, + Rows, + Text, +} from '@rainbow-me/design-system'; +import { useNavigation } from '@rainbow-me/navigation'; +import Routes from '@rainbow-me/routes'; +import { colors } from '@rainbow-me/styles'; + +const EditContent = ({ + accentColor, + sendReverseRecord, + setSendReverseRecord, + showReverseRecordSwitch, +}: { + accentColor: any; + sendReverseRecord: boolean; + setSendReverseRecord: React.Dispatch> | null; + showReverseRecordSwitch?: boolean; +}) => { + const { navigate } = useNavigation(); + const openPrimaryENSNameHelper = () => { + navigate(Routes.EXPLAIN_SHEET, { type: 'ens_primary_name' }); + }; + + if (!showReverseRecordSwitch) + return ( + + + + + + ); + return ( + + + + + + + + {`${lang.t('profiles.confirm.set_ens_name')} `} + + + + 􀅵 + + + + + setSendReverseRecord?.(sendReverseRecord => !sendReverseRecord) + } + testID="ens-reverse-record-switch" + thumbColor={colors.white} + trackColor={{ false: colors.white, true: accentColor }} + value={sendReverseRecord} + /> + + + + + ); +}; + +export default EditContent; diff --git a/src/components/ens-registration/ConfirmContent/RegisterContent.tsx b/src/components/ens-registration/ConfirmContent/RegisterContent.tsx index a4c395044cf..dde633bfeda 100644 --- a/src/components/ens-registration/ConfirmContent/RegisterContent.tsx +++ b/src/components/ens-registration/ConfirmContent/RegisterContent.tsx @@ -71,7 +71,7 @@ const RegisterContent = ({ onPress={openPrimaryENSNameHelper} scaleTo={0.9} > - + 􀅵 diff --git a/src/components/ens-registration/ConfirmContent/RenewContent.tsx b/src/components/ens-registration/ConfirmContent/RenewContent.tsx index 2cdee1347c9..42b4e08e068 100644 --- a/src/components/ens-registration/ConfirmContent/RenewContent.tsx +++ b/src/components/ens-registration/ConfirmContent/RenewContent.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { RegistrationReviewRows } from '../../../components/ens-registration'; import { Divider, Inset, Stack } from '@rainbow-me/design-system'; import { ENS_DOMAIN, REGISTRATION_MODES } from '@rainbow-me/helpers/ens'; -import { useDimensions, useENSProfile } from '@rainbow-me/hooks'; +import { useDimensions, useENSRegistrant } from '@rainbow-me/hooks'; import { timeUnits } from '@rainbow-me/references'; const RenewContent = ({ @@ -19,8 +19,8 @@ const RenewContent = ({ }) => { const { isSmallPhone } = useDimensions(); - const ensProfile = useENSProfile(name + ENS_DOMAIN, { enabled: true }); - const expiryDate = ensProfile?.data?.registration?.expiryDate || 0; + const { data: { registration } = {} } = useENSRegistrant(name + ENS_DOMAIN); + const expiryDate = registration?.expiryDate || 0; const newExpiryDateFormatted = format( new Date( diff --git a/src/components/ens-registration/ConfirmContent/WaitCommitmentConfirmationContent.tsx b/src/components/ens-registration/ConfirmContent/WaitCommitmentConfirmationContent.tsx index 696f68905a8..696b8491e11 100644 --- a/src/components/ens-registration/ConfirmContent/WaitCommitmentConfirmationContent.tsx +++ b/src/components/ens-registration/ConfirmContent/WaitCommitmentConfirmationContent.tsx @@ -19,11 +19,14 @@ import { useDimensions } from '@rainbow-me/hooks'; const WaitCommitmentConfirmationContent = ({ accentColor, action, + secondsSinceCommitConfirmed, }: { accentColor: any; action: () => void; + secondsSinceCommitConfirmed: number; }) => { const { isSmallPhone } = useDimensions(); + const speedUpEnabled = secondsSinceCommitConfirmed === -1; return ( <> @@ -50,7 +53,7 @@ const WaitCommitmentConfirmationContent = ({ - + null}> { export default function IntroMarquee() { const { navigate } = useNavigation(); - const [introMarqueeProfiles, setIntroMarqueeProfiles] = useState<{ - [name: string]: string | undefined; - }>({}); const handlePressENS = useCallback( (ensName: string) => { + const data = queryClient.getQueryData< + ReturnType['data'] + >(ensRecordsQueryKey({ name: ensName })); + const description = data?.records?.description || ''; navigate(Routes.PROFILE_PREVIEW_SHEET, { address: ensName, descriptionProfilePreviewHeight: estimateDescriptionProfilePreviewHeight( - introMarqueeProfiles[ensName] + description ), fromDiscover: true, }); }, - [introMarqueeProfiles, navigate] + [navigate] ); const renderItem = useCallback( @@ -53,22 +51,6 @@ export default function IntroMarquee() { [] ); - useEffect(() => { - const getProfiles = async () => { - const profiles: { [name: string]: string | undefined } = {}; - await Promise.all( - ensIntroMarqueeNames.map(async name => { - prefetchENSProfileImages({ name }); - prefetchENSProfile({ name }); - const records = await fetchRecords(name); - profiles[name] = records?.description; - }) - ); - setIntroMarqueeProfiles(profiles as any); - }; - if (IS_TESTING !== 'true') getProfiles(); - }, []); - const items = useMemo( () => ensIntroMarqueeNames.map((name, index) => ({ @@ -82,6 +64,7 @@ export default function IntroMarquee() { return ( void; }) => { - const { navigate } = useNavigation(); - const { startRegistration } = useENSRegistration(); - - const onFinish = useCallback( - async (name: string) => { - startRegistration(name, REGISTRATION_MODES.CREATE); - android && Keyboard.dismiss(); - setTimeout(() => { - navigate(Routes.ENS_CONFIRM_REGISTER_SHEET, {}); - }, 100); - }, - [navigate, startRegistration] - ); + const { finishRegistration } = useENSPendingRegistrations(); const onRemove = useCallback( async (name: string) => { @@ -74,7 +55,7 @@ const PendingRegistration = ({ onFinish(registration.name)} + onPress={() => finishRegistration(registration.name)} scaleTo={0.9} > ({ @@ -42,10 +40,12 @@ const RegistrationAvatar = ({ hasSeenExplainSheet, onChangeAvatarUrl, onShowExplainSheet, + enableNFTs, }: { hasSeenExplainSheet: boolean; onChangeAvatarUrl: (url: string) => void; onShowExplainSheet: () => void; + enableNFTs: boolean; }) => { const { images: { avatarUrl: initialAvatarUrl }, @@ -55,8 +55,9 @@ const RegistrationAvatar = ({ values, onBlurField, onRemoveField, + setDisabled, } = useENSRegistrationForm(); - const { navigate } = useNavigation(); + const { name } = useENSRegistration(); const [avatarUpdateAllowed, setAvatarUpdateAllowed] = useState(true); const [avatarUrl, setAvatarUrl] = useState( @@ -64,14 +65,17 @@ const RegistrationAvatar = ({ ); useEffect(() => { if (avatarUpdateAllowed) { - setAvatarUrl( - typeof initialAvatarUrl === 'string' ? initialAvatarUrl : values?.avatar - ); + const avatarUrl = + typeof initialAvatarUrl === 'string' + ? initialAvatarUrl + : values?.avatar; + setAvatarUrl(avatarUrl); + onChangeAvatarUrl(avatarUrl ?? ''); } }, [initialAvatarUrl, avatarUpdateAllowed]); // eslint-disable-line react-hooks/exhaustive-deps // We want to allow avatar state update when the screen is first focussed. - useFocusEffect(useCallback(() => setAvatarUpdateAllowed(true), [])); + useEffect(() => setAvatarUpdateAllowed(true), [setAvatarUpdateAllowed, name]); const setAvatarMetadata = useSetRecoilState(avatarMetadataAtom); @@ -86,11 +90,12 @@ const RegistrationAvatar = ({ image?: Image & { tmpPath?: string }; }) => { setAvatarMetadata(image); - setAvatarUrl(image?.tmpPath || asset?.image_thumbnail_url || ''); - // We want to disallow future avatar state changes (i.e. when upload successful) - // to avoid avatar flashing (from temp URL to uploaded URL). - setAvatarUpdateAllowed(false); - onChangeAvatarUrl(image?.path || asset?.image_thumbnail_url || ''); + setAvatarUrl( + image?.tmpPath || asset?.lowResUrl || asset?.image_thumbnail_url || '' + ); + onChangeAvatarUrl( + image?.path || asset?.lowResUrl || asset?.image_thumbnail_url || '' + ); if (asset) { const standard = asset.asset_contract?.schema_name || ''; const contractAddress = asset.asset_contract?.address || ''; @@ -104,6 +109,9 @@ const RegistrationAvatar = ({ }), }); } else if (image?.tmpPath) { + // We want to disallow future avatar state changes (i.e. when upload successful) + // to avoid avatar flashing (from temp URL to uploaded URL). + setAvatarUpdateAllowed(false); onBlurField({ key: 'avatar', value: image.tmpPath, @@ -113,39 +121,40 @@ const RegistrationAvatar = ({ [onBlurField, onChangeAvatarUrl, setAvatarMetadata] ); - const { ContextMenu } = useSelectImageMenu({ + const { + ContextMenu, + handleSelectImage, + handleSelectNFT, + } = useSelectImageMenu({ imagePickerOptions: { cropperCircleOverlay: true, cropping: true, + height: 400, + width: 400, }, - menuItems: ['library', 'nft'], + menuItems: enableNFTs ? ['library', 'nft'] : ['library'], onChangeImage, onRemoveImage: () => { onRemoveField({ key: 'avatar' }); setAvatarUrl(''); onChangeAvatarUrl(''); setAvatarMetadata(undefined); + setDisabled(false); }, onUploadError: () => { onBlurField({ key: 'avatar', value: '' }); setAvatarUrl(''); }, + onUploading: () => setDisabled(true), onUploadSuccess: ({ data }: { data: UploadImageReturnData }) => { onBlurField({ key: 'avatar', value: data.url }); + setDisabled(false); }, showRemove: Boolean(avatarUrl), testID: 'avatar', uploadToIPFS: true, }); - const handleSelectNFT = useCallback(() => { - navigate(Routes.SELECT_UNIQUE_TOKEN_SHEET, { - onSelect: (asset: any) => onChangeImage?.({ asset }), - springDamping: 1, - topOffset: 0, - }); - }, [navigate, onChangeImage]); - return ( @@ -166,7 +175,9 @@ const RegistrationAvatar = ({ ) : ( {children}} > diff --git a/src/components/ens-registration/RegistrationCover/RegistrationCover.tsx b/src/components/ens-registration/RegistrationCover/RegistrationCover.tsx index 308e0155156..671011a388a 100644 --- a/src/components/ens-registration/RegistrationCover/RegistrationCover.tsx +++ b/src/components/ens-registration/RegistrationCover/RegistrationCover.tsx @@ -1,4 +1,3 @@ -import { useFocusEffect } from '@react-navigation/core'; import ConditionalWrap from 'conditional-wrap'; import lang from 'i18n-js'; import React, { useCallback, useEffect, useState } from 'react'; @@ -13,6 +12,7 @@ import { UniqueAsset } from '@rainbow-me/entities'; import { UploadImageReturnData } from '@rainbow-me/handlers/pinata'; import { useENSModifiedRegistration, + useENSRegistration, useENSRegistrationForm, useSelectImageMenu, } from '@rainbow-me/hooks'; @@ -27,9 +27,11 @@ export const coverMetadataAtom = atom({ const RegistrationCover = ({ hasSeenExplainSheet, onShowExplainSheet, + enableNFTs, }: { hasSeenExplainSheet: boolean; onShowExplainSheet: () => void; + enableNFTs: boolean; }) => { const { images: { coverUrl: initialCoverUrl }, @@ -38,52 +40,50 @@ const RegistrationCover = ({ isLoading, onBlurField, onRemoveField, + setDisabled, values, } = useENSRegistrationForm(); - + const { name } = useENSRegistration(); const [coverUpdateAllowed, setCoverUpdateAllowed] = useState(true); - const [coverUrl, setCoverUrl] = useState(initialCoverUrl || values?.cover); + const [coverUrl, setCoverUrl] = useState(initialCoverUrl || values?.header); + useEffect(() => { if (coverUpdateAllowed) { setCoverUrl( - typeof initialCoverUrl === 'string' ? initialCoverUrl : values?.cover + typeof initialCoverUrl === 'string' ? initialCoverUrl : values?.header ); } - }, [initialCoverUrl, coverUpdateAllowed, values, coverUrl]); + }, [initialCoverUrl, coverUpdateAllowed]); // eslint-disable-line react-hooks/exhaustive-deps // We want to allow cover state update when the screen is first focussed. - useFocusEffect(useCallback(() => setCoverUpdateAllowed(true), [])); + useEffect(() => setCoverUpdateAllowed(true), [setCoverUpdateAllowed, name]); const accentColor = useForegroundColor('accent'); const setCoverMetadata = useSetRecoilState(coverMetadataAtom); - - const { ContextMenu } = useSelectImageMenu({ - imagePickerOptions: { - cropping: true, - height: 500, - width: 1500, - }, - menuItems: ['library', 'nft'], - onChangeImage: ({ + const onChangeImage = useCallback( + ({ asset, image, }: { asset?: UniqueAsset; image?: Image & { tmpPath?: string }; }) => { - // We want to disallow future avatar state changes (i.e. when upload successful) - // to avoid avatar flashing (from temp URL to uploaded URL). - setCoverUpdateAllowed(false); setCoverMetadata(image); - setCoverUrl(image?.tmpPath); + setCoverUrl( + image?.tmpPath || + asset?.image_url || + asset?.lowResUrl || + asset?.image_thumbnail_url || + '' + ); if (asset) { const standard = asset.asset_contract?.schema_name || ''; const contractAddress = asset.asset_contract?.address || ''; const tokenId = asset.id; onBlurField({ - key: 'cover', + key: 'header', value: stringifyENSNFTRecord({ contractAddress, standard, @@ -91,19 +91,40 @@ const RegistrationCover = ({ }), }); } else if (image?.tmpPath) { + // We want to disallow future avatar state changes (i.e. when upload successful) + // to avoid avatar flashing (from temp URL to uploaded URL). + setCoverUpdateAllowed(false); onBlurField({ - key: 'cover', + key: 'header', value: image.tmpPath, }); } }, + [onBlurField, setCoverMetadata] + ); + + const { ContextMenu, handleSelectImage } = useSelectImageMenu({ + imagePickerOptions: { + cropping: true, + height: 500, + width: 1500, + }, + menuItems: enableNFTs ? ['library', 'nft'] : ['library'], + onChangeImage: onChangeImage, onRemoveImage: () => { - onRemoveField({ key: 'cover' }); + onRemoveField({ key: 'header' }); setCoverUrl(''); setCoverMetadata(undefined); + setDisabled(false); + }, + onUploadError: () => { + onBlurField({ key: 'header', value: '' }); + setCoverUrl(''); }, + onUploading: () => setDisabled(true), onUploadSuccess: ({ data }: { data: UploadImageReturnData }) => { - onBlurField({ key: 'cover', value: data.url }); + onBlurField({ key: 'header', value: data.url }); + setDisabled(false); }, showRemove: Boolean(coverUrl), testID: 'cover', @@ -121,11 +142,17 @@ const RegistrationCover = ({ } return ( {children}} > - + {type === 'increment' ? '􀁍' : '􀁏'} @@ -110,7 +104,7 @@ export default function RegistrationReviewRows({ return ( - + @@ -135,7 +129,7 @@ export default function RegistrationReviewRows({ type="decrement" /> - + {duration > 1 ? lang.t('profiles.confirm.duration_plural', { @@ -180,13 +174,20 @@ export default function RegistrationReviewRows({ - {registrationFee ? ( - - {registrationFee} - - ) : ( - - )} + + {registrationFee ? ( + + {registrationFee} + + ) : ( + + )} + @@ -197,24 +198,7 @@ export default function RegistrationReviewRows({ - {networkFee ? ( - - {networkFee} - - ) : ( - - )} - - - - {mode === REGISTRATION_MODES.CREATE && ( - - - - {lang.t('profiles.confirm.estimated_total_eth')} - - - + {networkFee ? ( - {estimatedCostETH} ETH + {networkFee} ) : ( )} + + + + + {mode === REGISTRATION_MODES.CREATE && ( + + + + {lang.t('profiles.confirm.estimated_total_eth')} + + + + + {networkFee ? ( + + {estimatedCostETH} ETH + + ) : ( + + )} + )} @@ -238,13 +248,15 @@ export default function RegistrationReviewRows({ - {totalCost ? ( - - {totalCost} - - ) : ( - - )} + + {totalCost ? ( + + {totalCost} + + ) : ( + + )} + diff --git a/src/components/ens-registration/SearchInput/SearchInput.tsx b/src/components/ens-registration/SearchInput/SearchInput.tsx index d346b9efa06..410081c34a5 100644 --- a/src/components/ens-registration/SearchInput/SearchInput.tsx +++ b/src/components/ens-registration/SearchInput/SearchInput.tsx @@ -102,6 +102,7 @@ const SearchInput = ({ {isLoading ? ( @@ -127,7 +128,7 @@ const SearchInput = ({ {selectedFields.map( - ({ - label, - inputProps, - placeholder, - startsWith, - validations, - id, - key, - }) => ( + ({ label, inputProps, placeholder, startsWith, id, key }) => ( handleLayout(e, key)}> ) @@ -118,11 +118,17 @@ export default function TextRecordsForm({ function Field({ defaultValue, ...props }: InlineFieldProps) { const [value, setValue] = useState(defaultValue); + const [isTouched, setIsTouched] = useState(false); - // Set / clear values when the screen comes to focus / unfocus. useEffect(() => { + // If the field is touched, we don't want to set the default value again. + if (isTouched) return; + setValue(defaultValue); - }, [defaultValue]); + }, [defaultValue, isTouched]); + + // Set fields to be not touched when screen gets out of focus. + useFocusEffect(useCallback(() => () => setIsTouched(false), [])); return ( <> @@ -131,6 +137,7 @@ function Field({ defaultValue, ...props }: InlineFieldProps) { {...props} onChangeText={text => { props.onChangeText(text); + setIsTouched(true); setValue(text); }} value={value} diff --git a/src/components/ens-registration/index.tsx b/src/components/ens-registration/index.tsx index 060cc076a8a..0736cb2e1f8 100644 --- a/src/components/ens-registration/index.tsx +++ b/src/components/ens-registration/index.tsx @@ -10,4 +10,5 @@ export { default as WaitCommitmentConfirmationContent } from './ConfirmContent/W export { default as WaitENSConfirmationContent } from './ConfirmContent/WaitENSConfirmationContent'; export { default as RegisterContent } from './ConfirmContent/RegisterContent'; export { default as CommitContent } from './ConfirmContent/CommitContent'; +export { default as EditContent } from './ConfirmContent/EditContent'; export { default as RenewContent } from './ConfirmContent/RenewContent'; diff --git a/src/components/expanded-state/ContactProfileState.js b/src/components/expanded-state/ContactProfileState.js index d2512edb435..30c07555ffe 100644 --- a/src/components/expanded-state/ContactProfileState.js +++ b/src/components/expanded-state/ContactProfileState.js @@ -16,7 +16,7 @@ import { import { useAccountSettings, useContacts, - useENSProfileImages, + useENSAvatar, usePersistentDominantColorFromImage, } from '@rainbow-me/hooks'; import { @@ -78,11 +78,8 @@ const ContactProfileState = ({ address, color, contact, ens, nickname }) => { android && Keyboard.dismiss(); }, [goBack]); - const { data: images } = useENSProfileImages(ens, { - enabled: Boolean(ens), - }); - - const avatarUrl = profilesEnabled ? images?.avatarUrl : undefined; + const { data: avatar } = useENSAvatar(ens, { enabled: Boolean(ens) }); + const avatarUrl = profilesEnabled ? avatar?.imageUrl : undefined; const { result: dominantColor } = usePersistentDominantColorFromImage( maybeSignUri(avatarUrl || '') || '' diff --git a/src/components/expanded-state/UniqueTokenExpandedState.tsx b/src/components/expanded-state/UniqueTokenExpandedState.tsx index 1a82c60bf76..bc13881cedd 100644 --- a/src/components/expanded-state/UniqueTokenExpandedState.tsx +++ b/src/components/expanded-state/UniqueTokenExpandedState.tsx @@ -1,4 +1,5 @@ import { BlurView } from '@react-native-community/blur'; +import { useFocusEffect } from '@react-navigation/core'; import c from 'chroma-js'; import lang from 'i18n-js'; import React, { ReactNode, useCallback, useMemo, useRef } from 'react'; @@ -9,9 +10,7 @@ import Animated, { useSharedValue, } from 'react-native-reanimated'; import URL from 'url-parse'; -import { CardSize } from '../../components/unique-token/CardSize'; import useWallets from '../../hooks/useWallets'; -import { lightModeThemeColors } from '../../styles/colors'; import L2Disclaimer from '../L2Disclaimer'; import Link from '../Link'; import { ButtonPressAnimation } from '../animations'; @@ -25,7 +24,7 @@ import { } from '../sheet'; import { ToastPositionContainer, ToggleStateToast } from '../toasts'; import { UniqueTokenAttributes, UniqueTokenImage } from '../unique-token'; -import AdvancedSection from './ens/AdvancedSection'; +import { CardSize } from '../unique-token/CardSize'; import ConfigurationSection from './ens/ConfigurationSection'; import ProfileInfoSection from './ens/ProfileInfoSection'; import { @@ -69,10 +68,11 @@ import { import { useNavigation, useUntrustedUrlOpener } from '@rainbow-me/navigation'; import Routes from '@rainbow-me/routes'; import styled from '@rainbow-me/styled-components'; -import { position } from '@rainbow-me/styles'; +import { lightModeThemeColors, position } from '@rainbow-me/styles'; import { useTheme } from '@rainbow-me/theme'; import { buildRainbowUrl, + getUniqueTokenType, magicMemo, safeAreaInsetValues, } from '@rainbow-me/utils'; @@ -210,12 +210,6 @@ const Markdown = ({ ); }; -export enum UniqueTokenType { - NFT = 'NFT', - ENS = 'ENS', - POAP = 'POAP', -} - interface UniqueTokenExpandedStateProps { asset: UniqueAsset; external: boolean; @@ -248,7 +242,7 @@ const UniqueTokenExpandedState = ({ const { accountAddress, accountENS } = useAccountProfile(); const { height: deviceHeight, width: deviceWidth } = useDimensions(); - const { navigate } = useNavigation(); + const { navigate, setOptions } = useNavigation(); const { colors, isDarkMode } = useTheme(); const { isReadOnlyWallet } = useWallets(); @@ -272,24 +266,32 @@ const UniqueTokenExpandedState = ({ urlSuffixForAsset, } = asset; - const uniqueTokenType = useMemo(() => { - if (asset.isPoap) return UniqueTokenType.POAP; - if (familyName === 'ENS' && uniqueId !== 'Unknown ENS name') { - return UniqueTokenType.ENS; - } - return UniqueTokenType.NFT; - }, [asset.isPoap, familyName, uniqueId]); + const uniqueTokenType = getUniqueTokenType(asset); // Create deterministic boolean flags from the `uniqueTokenType` (for easier readability). - const isPoap = uniqueTokenType === UniqueTokenType.POAP; - const isENS = uniqueTokenType === UniqueTokenType.ENS; - const isNFT = uniqueTokenType === UniqueTokenType.NFT; + const isPoap = uniqueTokenType === 'POAP'; + const isENS = uniqueTokenType === 'ENS'; + const isNFT = uniqueTokenType === 'NFT'; // Fetch the ENS profile if the unique token is an ENS name. - const cleanENSName = isENS && uniqueId ? uniqueId?.split(' ')?.[0] : uniqueId; - const ensProfile = useENSProfile(cleanENSName, { enabled: isENS }); + const cleanENSName = isENS + ? uniqueId + ? uniqueId?.split(' ')?.[0] + : uniqueId + : ''; + const ensProfile = useENSProfile(cleanENSName, { + enabled: isENS, + }); const ensData = ensProfile.data; + useFocusEffect( + useCallback(() => { + if (uniqueTokenType === 'ENS') { + setOptions({ limitActiveModals: false }); + } + }, [setOptions, uniqueTokenType]) + ); + const profileInfoSectionAvailable = useMemo(() => { const available = Object.keys(ensData?.records || {}).some( key => key !== ENS_RECORDS.avatar @@ -528,6 +530,8 @@ const UniqueTokenExpandedState = ({ )}`} nftShadows onPress={handlePressEdit} + // @ts-expect-error JavaScript component + testID="edit" textColor={textColor} weight="heavy" /> @@ -547,6 +551,8 @@ const UniqueTokenExpandedState = ({ } nftShadows onPress={handlePressMarketplaceName} + // @ts-expect-error JavaScript component + testID="unique-expanded-state-send" textColor={textColor} weight="heavy" /> @@ -596,10 +602,10 @@ const UniqueTokenExpandedState = ({ @@ -692,15 +698,6 @@ const UniqueTokenExpandedState = ({ registrant={ensData?.registrant} /> -