diff --git a/src/apollo/queries.ts b/src/apollo/queries.ts index f5263e80dc8..c1db2a255cb 100644 --- a/src/apollo/queries.ts +++ b/src/apollo/queries.ts @@ -256,6 +256,9 @@ export type EnsAccountRegistratonsData = { registrations: { domain: { name: string; + owner: { + id: string; + }; }; }[]; }; @@ -267,6 +270,9 @@ export const ENS_ACCOUNT_REGISTRATIONS = gql` registrations(first: 99, orderBy: registrationDate) { domain { name + owner { + id + } } } } diff --git a/src/components/discover-sheet/DiscoverSearch.js b/src/components/discover-sheet/DiscoverSearch.js index 63825190e3e..a8b5e6c92cf 100644 --- a/src/components/discover-sheet/DiscoverSearch.js +++ b/src/components/discover-sheet/DiscoverSearch.js @@ -62,6 +62,14 @@ export default function DiscoverSearch() { ensResults, ]); + const currencyListDataLength = useMemo( + () => + uniswapCurrencyList?.[0]?.data?.length || + 0 + ensResults?.[0]?.data?.length || + 0, + [ensResults, uniswapCurrencyList] + ); + useHardwareBackOnFocus(() => { cancelSearch(); // prevent other back handlers from firing @@ -179,7 +187,10 @@ export default function DiscoverSearch() { }, [isSearchModeEnabled]); return ( - + - - - - - + {avatarUrl ?? ( + + + + + + )} diff --git a/src/components/ens-registration/RegistrationReviewRows/RegistrationReviewRows.tsx b/src/components/ens-registration/RegistrationReviewRows/RegistrationReviewRows.tsx index 3fa12f370e6..a9de3c68557 100644 --- a/src/components/ens-registration/RegistrationReviewRows/RegistrationReviewRows.tsx +++ b/src/components/ens-registration/RegistrationReviewRows/RegistrationReviewRows.tsx @@ -21,11 +21,13 @@ function StepButton({ onLongPressEnded, onPress, type, + disabled, }: { onPress: () => void; onLongPress: () => void; onLongPressEnded: () => void; type: 'increment' | 'decrement'; + disabled: boolean; }) { return ( - + {type === 'increment' ? '􀁍' : '􀁏'} @@ -120,6 +122,7 @@ export default function RegistrationReviewRows({ @@ -529,7 +530,7 @@ const UniqueTokenExpandedState = ({ registrationDate={ ensData?.registration.registrationDate } - showEditButton={hasEditButton} + showExtendDuration={hasExtendDurationButton} /> )} diff --git a/src/components/expanded-state/unique-token/ENSBriefTokenInfoRow.tsx b/src/components/expanded-state/unique-token/ENSBriefTokenInfoRow.tsx index 70b810b8f49..d1177dd8860 100644 --- a/src/components/expanded-state/unique-token/ENSBriefTokenInfoRow.tsx +++ b/src/components/expanded-state/unique-token/ENSBriefTokenInfoRow.tsx @@ -13,13 +13,13 @@ import Routes from '@rainbow-me/routes'; export default function ENSBriefTokenInfoRow({ expiryDate, registrationDate, - showEditButton, + showExtendDuration, ensName, }: { expiryDate?: number; registrationDate?: number; ensName: string; - showEditButton?: boolean; + showExtendDuration?: boolean; }) { const { colors } = useTheme(); const { navigate } = useNavigation(); @@ -31,9 +31,10 @@ export default function ENSBriefTokenInfoRow({ }, []); const handlePressEditExpiryDate = useCallback(() => { - startRegistration(ensName, REGISTRATION_MODES.RENEW); + const cleanENSName = ensName?.split(' ')?.[0] ?? ensName; + startRegistration(cleanENSName, REGISTRATION_MODES.RENEW); navigate(Routes.ENS_CONFIRM_REGISTER_SHEET, { - ensName, + ensName: cleanENSName, longFormHeight: ENSConfirmRenewSheetHeight + (data?.images?.avatarUrl ? 70 : 0), mode: REGISTRATION_MODES.RENEW, @@ -58,7 +59,7 @@ export default function ENSBriefTokenInfoRow({ {/* @ts-expect-error JavaScript component */} Object.keys(imageKeyMap).includes(recordKey), [recordKey] ); - const isUrlValue = useMemo( + + const isUrlRecord = useMemo( () => - recordValue.match(/^http/) || [ENS_RECORDS.url, ENS_RECORDS.website].includes(recordKey as ENS_RECORDS), - [recordKey, recordValue] + [recordKey] ); + const isUrlValue = useMemo(() => recordValue.match(/^http/), [recordValue]); const url = useMemo(() => { - if (isUrlValue) { + if (isUrlValue || isUrlRecord) { return recordValue.match(/^http/) ? recordValue : `https://${recordValue}`; @@ -72,12 +73,15 @@ export default function useENSRecordDisplayProperties({ if (links[recordKey]) { return `${links[recordKey]}${recordValue.replace('@', '')}`; } - }, [isUrlValue, recordKey, recordValue]); + }, [isUrlRecord, isUrlValue, recordKey, recordValue]); - const displayUrl = useMemo( - () => (url ? new URL(url).hostname.replace(/^www\./, '') : undefined), - [url] - ); + const { displayUrl, displayUrlUsername } = useMemo(() => { + const urlObj = url ? new URL(url) : { hostname: '', pathname: '' }; + return { + displayUrl: urlObj?.hostname?.replace(/^www\./, ''), + displayUrlUsername: urlObj?.pathname?.replace('/', ''), + }; + }, [url]); const label = useMemo(() => { // @ts-expect-error @@ -92,9 +96,12 @@ export default function useENSRecordDisplayProperties({ }, [recordKey, type]); const value = useMemo(() => { - if (isUrlValue && displayUrl) { + if (isUrlRecord && displayUrl) { return `􀤆 ${displayUrl}`; } + if (isUrlValue && displayUrlUsername) { + return displayUrlUsername; + } if (recordKey === ENS_RECORDS.email) { return `􀍕 ${recordValue}`; } @@ -102,7 +109,15 @@ export default function useENSRecordDisplayProperties({ return formatAddressForDisplay(recordValue, 4, 4) || ''; } return recordValue; - }, [displayUrl, isUrlValue, recordKey, recordValue, type]); + }, [ + displayUrl, + displayUrlUsername, + isUrlRecord, + isUrlValue, + recordKey, + recordValue, + type, + ]); const icon = useMemo(() => icons[recordKey], [recordKey]); diff --git a/src/hooks/useENSRegistration.ts b/src/hooks/useENSRegistration.ts index 010042c7930..9958718be70 100644 --- a/src/hooks/useENSRegistration.ts +++ b/src/hooks/useENSRegistration.ts @@ -109,19 +109,27 @@ export default function useENSRegistration({ Object.entries(initialRecords), isEqual ) as [keyof Records, string][]; + const changedRecords = entriesToChange.reduce( (recordsToAdd: Partial, [key, value]) => ({ ...recordsToAdd, - [key]: value, + ...(value ? { [key]: value } : {}), }), {} ); + const recordKeysWithValue = (Object.keys( + records + ) as (keyof Records)[]).filter((key: keyof Records) => { + return Boolean(records[key]); + }); + const keysToRemove = differenceWith( Object.keys(initialRecords), - Object.keys(records), + recordKeysWithValue, isEqual ) as (keyof Records)[]; + const removedRecords = keysToRemove.reduce( (recordsToAdd: Partial, key) => ({ ...recordsToAdd, @@ -135,6 +143,7 @@ export default function useENSRegistration({ ...removedRecords, }; }, [initialRecords, records]); + useEffect(() => { dispatch(ensRedux.setChangedRecords(accountAddress, changedRecords)); }, [accountAddress, changedRecords, dispatch]); @@ -163,7 +172,9 @@ export default function useENSRegistration({ token.asset_contract.address === contractAddress && token.id === tokenId ); - if (uniqueToken?.image_thumbnail_url) { + if (uniqueToken?.image_url) { + avatarUrl = uniqueToken?.image_url; + } else if (uniqueToken?.image_thumbnail_url) { avatarUrl = uniqueToken?.image_thumbnail_url; } } else if ( diff --git a/src/hooks/useENSRegistrationActionHandler.ts b/src/hooks/useENSRegistrationActionHandler.ts index 434778b3e9a..221ff45a96f 100644 --- a/src/hooks/useENSRegistrationActionHandler.ts +++ b/src/hooks/useENSRegistrationActionHandler.ts @@ -1,8 +1,10 @@ import { useNavigation } from '@react-navigation/core'; import { differenceInSeconds } from 'date-fns'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -// @ts-expect-error -import { IS_TESTING } from 'react-native-dotenv'; +import { + // @ts-ignore + IS_TESTING, +} from 'react-native-dotenv'; import { Image } from 'react-native-image-crop-picker'; import { useDispatch } from 'react-redux'; import { useRecoilValue } from 'recoil'; diff --git a/src/hooks/useENSRegistrationForm.ts b/src/hooks/useENSRegistrationForm.ts index 7cb42755b5f..78b6d55d171 100644 --- a/src/hooks/useENSRegistrationForm.ts +++ b/src/hooks/useENSRegistrationForm.ts @@ -200,6 +200,7 @@ export default function useENSRegistrationForm({ const [isLoading, setIsLoading] = useState( mode === REGISTRATION_MODES.EDIT && isEmpty(values) ); + useEffect(() => { if (mode === REGISTRATION_MODES.EDIT) { if (profileQuery.isSuccess || !isEmpty(values)) { diff --git a/src/languages/_english.json b/src/languages/_english.json index 5d9b7ffbc7a..6040201620d 100644 --- a/src/languages/_english.json +++ b/src/languages/_english.json @@ -630,6 +630,7 @@ "bio_placeholder": "Add a bio to your profile", "website_placeholder": "Add your website", "review": "Review", + "cancel": "Cancel", "choose_nft": "Choose NFT", "upload_photo": "Upload photo", "eth": "Ethereum", diff --git a/src/screens/ENSAssignRecordsSheet.js b/src/screens/ENSAssignRecordsSheet.js index 7164309521c..30d53133e38 100644 --- a/src/screens/ENSAssignRecordsSheet.js +++ b/src/screens/ENSAssignRecordsSheet.js @@ -82,12 +82,13 @@ export default function ENSAssignRecordsSheet() { }); const [avatarUrl, setAvatarUrl] = useState(initialAvatarUrl); - const [accentColor, setAccentColor] = useRecoilState(accentColorAtom); + const { result: dominantColor } = usePersistentDominantColorFromImage( avatarUrl || initialAvatarUrl || '' ); const [prevDominantColor, setPrevDominantColor] = useState(dominantColor); + useEffect(() => { setAccentColor(dominantColor || prevDominantColor || colors.purple); if (dominantColor) { @@ -186,7 +187,7 @@ export default function ENSAssignRecordsSheet() { } export function ENSAssignRecordsBottomActions({ visible: defaultVisible }) { - const { navigate } = useNavigation(); + const { navigate, goBack } = useNavigation(); const keyboardHeight = useKeyboardHeight(); const { colors } = useTheme(); @@ -303,16 +304,25 @@ export function ENSAssignRecordsBottomActions({ visible: defaultVisible }) { {lang.t('profiles.create.skip')} ) : ( - - + + {!disabled ? ( + + ) : ( + + {lang.t(`profiles.create.cancel`)} + + )} )} diff --git a/src/screens/ENSIntroSheet.tsx b/src/screens/ENSIntroSheet.tsx index 3ad16653fdf..0e8a05fa8dd 100644 --- a/src/screens/ENSIntroSheet.tsx +++ b/src/screens/ENSIntroSheet.tsx @@ -1,7 +1,7 @@ import MaskedView from '@react-native-masked-view/masked-view'; import { useRoute } from '@react-navigation/core'; import lang from 'i18n-js'; -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import LinearGradient from 'react-native-linear-gradient'; import ActivityIndicator from '../components/ActivityIndicator'; import Button from '../components/buttons/Button'; @@ -24,20 +24,29 @@ import { useColorMode, } from '@rainbow-me/design-system'; import { REGISTRATION_MODES } from '@rainbow-me/helpers/ens'; -import { useAccountENSDomains, useENSRegistration } from '@rainbow-me/hooks'; +import { + useAccountENSDomains, + useAccountSettings, + useENSRegistration, +} from '@rainbow-me/hooks'; import Routes from '@rainbow-me/routes'; +const topPadding = android ? 29 : 19; + export default function ENSIntroSheet() { const { colors } = useTheme(); - const { colorMode } = useColorMode(); - const { params } = useRoute(); - - const topPadding = android ? 29 : 19; - + const { accountAddress } = useAccountSettings(); const { data: domains, isLoading, isSuccess } = useAccountENSDomains(); + const ownedDomains = useMemo( + () => + domains?.filter( + ({ owner }) => owner.id?.toLowerCase() === accountAddress.toLowerCase() + ), + [accountAddress, domains] + ); const { navigate } = useNavigation(); const handleNavigateToSearch = useCallback(() => { navigate(Routes.ENS_SEARCH_SHEET); @@ -54,8 +63,8 @@ export default function ENSIntroSheet() { }, 0); }; - if (domains?.length === 1) { - navigateToAssignRecords(domains[0].name); + if (ownedDomains?.length === 1) { + navigateToAssignRecords(ownedDomains[0].name); } else { navigate(Routes.SELECT_ENS_SHEET, { onSelectENS: (ensName: string) => { @@ -63,7 +72,7 @@ export default function ENSIntroSheet() { }, }); } - }, [domains, navigate, params, startRegistration]); + }, [ownedDomains, navigate, params, startRegistration]); return ( - {domains?.length === 0 ? ( + {ownedDomains?.length === 0 ? ( ) : ( diff --git a/src/screens/ProfileSheet.tsx b/src/screens/ProfileSheet.tsx index bb21a8c0b6d..edf39667760 100644 --- a/src/screens/ProfileSheet.tsx +++ b/src/screens/ProfileSheet.tsx @@ -31,7 +31,7 @@ export default function ProfileSheet() { const { height: deviceHeight } = useDimensions(); const contentHeight = deviceHeight - SheetHandleFixedToTopHeight; - const ensName = params?.address || 'moxey.eth'; + const ensName = params?.address; const { data: profile, isSuccess } = useENSProfile(ensName); const avatarUrl = profile?.images?.avatarUrl; diff --git a/src/utils/getENSNFTAvatarUrl.ts b/src/utils/getENSNFTAvatarUrl.ts index 43c57380522..eadd8b45b1a 100644 --- a/src/utils/getENSNFTAvatarUrl.ts +++ b/src/utils/getENSNFTAvatarUrl.ts @@ -17,7 +17,9 @@ export default function getENSNFTAvatarUrl( token.asset_contract.address === contractAddress && token.id === tokenId ); - if (uniqueToken?.image_thumbnail_url) { + if (uniqueToken?.image_url) { + avatarUrl = uniqueToken?.image_url; + } else if (uniqueToken?.image_thumbnail_url) { avatarUrl = uniqueToken?.image_thumbnail_url; } } else if (