Improve address display (#1010)
* adding social providers * add translation * improving invoice info container
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 14 KiB |
@@ -1,6 +1,8 @@
|
||||
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { useMemo } from 'react';
|
||||
import { Image } from 'expo-image';
|
||||
import { ThemeText } from '../../../../../functions/CustomElements';
|
||||
import { CENTER } from '../../../../../constants';
|
||||
import { CENTER, COLORS, FONT, ICONS, SIZES } from '../../../../../constants';
|
||||
import GetThemeColors from '../../../../../hooks/themeColors';
|
||||
import formatSparkPaymentAddress from '../functions/formatSparkPaymentAddress';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
@@ -8,6 +10,25 @@ import { InputTypes } from 'bitcoin-address-parser';
|
||||
import ContactProfileImage from '../../contacts/internalComponents/profileImage';
|
||||
import normalizeLNURLAddress from '../../../../../functions/lnurl/normalizeLNURLAddress';
|
||||
import ProfileImageRow from '../../contacts/internalComponents/profileImageRow';
|
||||
import ThemeIcon from '../../../../../functions/CustomElements/themeIcon';
|
||||
import { HIDDEN_OPACITY } from '../../../../../constants/theme';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// LNURL/lightning-address domain → ICONS key for the provider brand logo.
|
||||
const LNURL_PROVIDER_ICONS = {
|
||||
'aqua.net': 'aqua',
|
||||
'blink.sv': 'blink',
|
||||
'breez.tips': 'breez',
|
||||
'cake.cash': 'cake',
|
||||
'coinos.io': 'coinos',
|
||||
'mannabitcoin.com': 'mannabitcoin',
|
||||
'cluborange.org': 'cluborange',
|
||||
'strike.me': 'strike',
|
||||
'tether.me': 'tether',
|
||||
'walletofsatoshi.com': 'walletofsatoshi',
|
||||
'zeuspay.com': 'zeuspay',
|
||||
};
|
||||
|
||||
export default function InvoiceInfo({
|
||||
paymentInfo,
|
||||
fromPage,
|
||||
@@ -23,11 +44,131 @@ export default function InvoiceInfo({
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
const { backgroundOffset, backgroundColor } = GetThemeColors();
|
||||
const navigate = useNavigation();
|
||||
const splitContacts = splitRecipients?.map(({ contact }) => contact);
|
||||
|
||||
const isLNURLPay = paymentInfo?.type === InputTypes.LNURL_PAY;
|
||||
const paymentType = formmateedSparkPaymentInfo.paymentType;
|
||||
|
||||
// The bitcoin/spark/lrc20 branch renders the full address on screen, so
|
||||
// tapping to "reveal the full address" adds nothing. Only single-row label
|
||||
// variants (branta/contact/LNURL/lightning) stay clickable + get a chevron.
|
||||
const showsFullAddress =
|
||||
!isUsingBranta &&
|
||||
!isSplitPayment &&
|
||||
fromPage !== 'contacts' &&
|
||||
!isLNURLPay &&
|
||||
paymentType !== 'lightning';
|
||||
const isClickable = !isSplitPayment && !showsFullAddress;
|
||||
|
||||
// LNURL: resolve the human-readable "user@host", match the host to a provider
|
||||
// logo, and drop "@host" when we have a logo (the logo conveys the provider).
|
||||
const normalizedLNURL = isLNURLPay
|
||||
? normalizeLNURLAddress(paymentInfo?.data?.address) ??
|
||||
paymentInfo?.data?.address ??
|
||||
''
|
||||
: '';
|
||||
const lnurlDomain = normalizedLNURL.includes('@')
|
||||
? normalizedLNURL.split('@')[1]?.toLowerCase()
|
||||
: '';
|
||||
const providerIconKey = LNURL_PROVIDER_ICONS[lnurlDomain];
|
||||
const lnurlDisplayText = providerIconKey
|
||||
? normalizedLNURL.split('@')[0]
|
||||
: normalizedLNURL;
|
||||
|
||||
// On-chain / spark addresses: 4-char groups with alternating weight for
|
||||
// easy visual validation (mirrors depositQRView).
|
||||
const addressSegments = useMemo(() => {
|
||||
const addr = formmateedSparkPaymentInfo.address || '';
|
||||
return (addr.match(/.{1,4}/g) || []).map((group, i, all) => (
|
||||
<Text
|
||||
key={i}
|
||||
style={{
|
||||
fontFamily: i % 2 === 0 ? FONT.Title_SemiBold : FONT.Title_Regular,
|
||||
}}
|
||||
>
|
||||
{group}
|
||||
{i < all.length - 1 ? ' ' : ''}
|
||||
</Text>
|
||||
));
|
||||
}, [formmateedSparkPaymentInfo.address]);
|
||||
|
||||
let paymentContent;
|
||||
if (isLNURLPay) {
|
||||
paymentContent = (
|
||||
<View style={styles.contactRow}>
|
||||
<View
|
||||
style={[
|
||||
styles.profileImage,
|
||||
providerIconKey
|
||||
? styles.providerLogoCircle
|
||||
: { backgroundColor: backgroundColor },
|
||||
]}
|
||||
>
|
||||
{providerIconKey ? (
|
||||
<Image
|
||||
style={styles.providerLogo}
|
||||
source={ICONS[providerIconKey]}
|
||||
contentFit="contain"
|
||||
/>
|
||||
) : (
|
||||
<ContactProfileImage
|
||||
updated={undefined}
|
||||
uri={undefined}
|
||||
darkModeType={darkModeType}
|
||||
theme={theme}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
<ThemeText
|
||||
styles={styles.addressText}
|
||||
CustomNumberOfLines={1}
|
||||
content={lnurlDisplayText}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
} else if (paymentType === 'lightning') {
|
||||
paymentContent = (
|
||||
<View style={styles.contactRow}>
|
||||
<View
|
||||
style={[styles.profileImage, { backgroundColor: backgroundColor }]}
|
||||
>
|
||||
<Image
|
||||
style={[
|
||||
styles.lightningIcon,
|
||||
{
|
||||
tintColor:
|
||||
theme && darkModeType ? COLORS.darkModeText : COLORS.primary,
|
||||
},
|
||||
]}
|
||||
source={ICONS.lightningReceiveIcon}
|
||||
contentFit="contain"
|
||||
/>
|
||||
</View>
|
||||
<ThemeText
|
||||
styles={styles.addressText}
|
||||
CustomNumberOfLines={1}
|
||||
content={t('wallet.sendPages.sendPaymentScreen.lightningPayment')}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
// bitcoin / spark / lrc20
|
||||
paymentContent = (
|
||||
<ThemeText
|
||||
styles={styles.segmentText}
|
||||
content={addressSegments}
|
||||
CustomNumberOfLines={4}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const Container = isClickable ? TouchableOpacity : View;
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
<Container
|
||||
onPress={() => {
|
||||
navigate.navigate('ErrorScreen', {
|
||||
errorMessage: formmateedSparkPaymentInfo.address,
|
||||
@@ -35,11 +176,12 @@ export default function InvoiceInfo({
|
||||
}}
|
||||
style={[
|
||||
styles.invoiceContainer,
|
||||
isClickable && styles.clickableContainer,
|
||||
{
|
||||
backgroundColor: backgroundOffset,
|
||||
},
|
||||
]}
|
||||
disabled={isSplitPayment}
|
||||
disabled={!isClickable}
|
||||
>
|
||||
{isUsingBranta ? (
|
||||
<View style={styles.contactRow}>
|
||||
@@ -94,18 +236,12 @@ export default function InvoiceInfo({
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<ThemeText
|
||||
styles={{ includeFontPadding: false }}
|
||||
CustomNumberOfLines={2}
|
||||
content={
|
||||
paymentInfo?.type === InputTypes.LNURL_PAY
|
||||
? normalizeLNURLAddress(paymentInfo.data.address) ??
|
||||
paymentInfo.data.address
|
||||
: formmateedSparkPaymentInfo.address
|
||||
}
|
||||
/>
|
||||
paymentContent
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
{isClickable && (
|
||||
<ThemeIcon iconName="ChevronRight" size={20} styles={styles.chevron} />
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -114,15 +250,23 @@ const styles = StyleSheet.create({
|
||||
width: '80%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 8,
|
||||
borderRadius: 8,
|
||||
padding: 12,
|
||||
borderRadius: 16,
|
||||
...CENTER,
|
||||
marginTop: 30,
|
||||
},
|
||||
clickableContainer: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
contactRow: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
chevron: {
|
||||
opacity: 0.8,
|
||||
marginLeft: 8,
|
||||
},
|
||||
|
||||
profileImage: {
|
||||
width: 40,
|
||||
@@ -133,8 +277,28 @@ const styles = StyleSheet.create({
|
||||
overflow: 'hidden',
|
||||
marginRight: 10,
|
||||
},
|
||||
providerLogoCircle: {
|
||||
backgroundColor: COLORS.white,
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: COLORS.gray,
|
||||
},
|
||||
providerLogo: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
lightningIcon: {
|
||||
width: 24,
|
||||
height: 24,
|
||||
},
|
||||
addressText: {
|
||||
includeFontPadding: false,
|
||||
flexShrink: 1,
|
||||
},
|
||||
segmentText: {
|
||||
fontSize: SIZES.small,
|
||||
lineHeight: 24,
|
||||
includeFontPadding: false,
|
||||
textAlign: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -64,6 +64,7 @@ import {
|
||||
resolveUsdFiatStats,
|
||||
} from '../../../../functions/displayCurrency';
|
||||
import CurrencySwitchButton from '../../../../functions/CustomElements/currencySwitchButton';
|
||||
import ThemeIcon from '../../../../functions/CustomElements/themeIcon';
|
||||
import { Image } from 'expo-image';
|
||||
|
||||
const QUOTE_TTL_MS = 115_000;
|
||||
@@ -747,35 +748,42 @@ export default function StablecoinSendScreen() {
|
||||
}
|
||||
style={[styles.destinationBox, { backgroundColor: rowBg }]}
|
||||
>
|
||||
<View style={styles.confirmIconWrapper}>
|
||||
<View
|
||||
style={[
|
||||
styles.confirmChainCircle,
|
||||
{ backgroundColor: backgroundOffset },
|
||||
]}
|
||||
>
|
||||
<Image
|
||||
style={styles.confirmChainIcon}
|
||||
source={ICONS[`chain_${chainLabel?.toLowerCase()}`]}
|
||||
contentFit="contain"
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
styles.confirmCurrencyBadge,
|
||||
{ borderColor: backgroundColor },
|
||||
]}
|
||||
>
|
||||
<Image
|
||||
style={styles.confirmCurrencyIcon}
|
||||
source={ICONS[`${asset?.toLowerCase()}Logo`]}
|
||||
contentFit="contain"
|
||||
/>
|
||||
<View style={styles.destinationContent}>
|
||||
<View style={styles.confirmIconWrapper}>
|
||||
<View
|
||||
style={[
|
||||
styles.confirmChainCircle,
|
||||
{ backgroundColor: backgroundOffset },
|
||||
]}
|
||||
>
|
||||
<Image
|
||||
style={styles.confirmChainIcon}
|
||||
source={ICONS[`chain_${chainLabel?.toLowerCase()}`]}
|
||||
contentFit="contain"
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
styles.confirmCurrencyBadge,
|
||||
{ borderColor: backgroundColor },
|
||||
]}
|
||||
>
|
||||
<Image
|
||||
style={styles.confirmCurrencyIcon}
|
||||
source={ICONS[`${asset?.toLowerCase()}Logo`]}
|
||||
contentFit="contain"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<ThemeText
|
||||
styles={styles.quoteValue}
|
||||
content={`${truncateAddress(address)}`}
|
||||
/>
|
||||
</View>
|
||||
<ThemeText
|
||||
styles={styles.quoteValue}
|
||||
content={`${truncateAddress(address)}`}
|
||||
<ThemeIcon
|
||||
iconName="ChevronRight"
|
||||
size={20}
|
||||
styles={styles.destinationChevron}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
@@ -918,12 +926,21 @@ const styles = StyleSheet.create({
|
||||
width: '80%',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 8,
|
||||
borderRadius: 8,
|
||||
justifyContent: 'space-between',
|
||||
padding: 12,
|
||||
borderRadius: 16,
|
||||
...CENTER,
|
||||
marginTop: 30,
|
||||
},
|
||||
destinationContent: {
|
||||
flexShrink: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
destinationChevron: {
|
||||
opacity: 0.8,
|
||||
marginLeft: 8,
|
||||
},
|
||||
receiveAmount: {
|
||||
opacity: HIDDEN_OPACITY,
|
||||
...CENTER,
|
||||
|
||||
@@ -39,6 +39,17 @@ import ios_maps_dark from '../assets/apple_maps_dark.png';
|
||||
import android_maps_light from '../assets/google_maps_light.png';
|
||||
import android_maps_dark from '../assets/google_maps_dark.png';
|
||||
import bankIcon from '../assets/icons/bank.png';
|
||||
import aqua from '../assets/social_logos/aqua.png';
|
||||
import blink from '../assets/social_logos/blink.png';
|
||||
import breez from '../assets/social_logos/breez.png';
|
||||
import cake from '../assets/social_logos/cake.png';
|
||||
import coinos from '../assets/social_logos/coinos.png';
|
||||
import mannabitcoin from '../assets/social_logos/manna.png';
|
||||
import cluborange from '../assets/social_logos/orangePillApp.png';
|
||||
import strike from '../assets/social_logos/strike.png';
|
||||
import tether from '../assets/social_logos/tether.png';
|
||||
import walletofsatoshi from '../assets/social_logos/walletofsatoshi.png';
|
||||
import zeuspay from '../assets/social_logos/zeus.png';
|
||||
|
||||
export default {
|
||||
logoIcon,
|
||||
@@ -100,4 +111,17 @@ export default {
|
||||
android_maps_dark,
|
||||
|
||||
bankIcon,
|
||||
|
||||
// LNURL Providers
|
||||
aqua,
|
||||
blink,
|
||||
breez,
|
||||
cake,
|
||||
coinos,
|
||||
mannabitcoin,
|
||||
cluborange,
|
||||
strike,
|
||||
tether,
|
||||
walletofsatoshi,
|
||||
zeuspay,
|
||||
};
|
||||
|
||||
@@ -1228,7 +1228,8 @@
|
||||
"swapRatesChangedBody": "Der Wechselkurs für diesen Swap hat sich während Ihrer Prüfung geändert. Bitte geben Sie den Betrag erneut ein, um fortzufahren.",
|
||||
"swapRatesChangedButton": "Betrag erneut eingeben",
|
||||
"feeEstimateError": "Gebühr konnte nicht geschätzt werden, bitte versuchen Sie es erneut",
|
||||
"brantaVerification": "Diese Zahlung ist von Branta verifiziert."
|
||||
"brantaVerification": "Diese Zahlung ist von Branta verifiziert.",
|
||||
"lightningPayment": "Lightning-Zahlung"
|
||||
},
|
||||
"selectPaymentMethod": {
|
||||
"header": "Wie möchten Sie diese Zahlung senden (BTC oder USD)?"
|
||||
|
||||
@@ -1228,7 +1228,8 @@
|
||||
"swapRatesChangedBody": "The exchange rate for this swap changed while you were reviewing. Please re-enter your amount to continue.",
|
||||
"swapRatesChangedButton": "Re-enter Amount",
|
||||
"feeEstimateError": "Unable to estimate fee, please try again",
|
||||
"brantaVerification": "This payment has been verified by Branta"
|
||||
"brantaVerification": "This payment has been verified by Branta",
|
||||
"lightningPayment": "Lightning payment"
|
||||
},
|
||||
"selectPaymentMethod": {
|
||||
"header": "How do you want to fund your payment?"
|
||||
|
||||
@@ -1228,7 +1228,8 @@
|
||||
"swapRatesChangedBody": "El tipo de cambio para este intercambio cambió mientras lo estabas revisando. Vuelve a ingresar el monto para continuar.",
|
||||
"swapRatesChangedButton": "Volver a ingresar el monto",
|
||||
"feeEstimateError": "No se pudo estimar la comisión, inténtalo de nuevo",
|
||||
"brantaVerification": "Este pago ha sido verificado por Branta"
|
||||
"brantaVerification": "Este pago ha sido verificado por Branta",
|
||||
"lightningPayment": "Pago Lightning"
|
||||
},
|
||||
"selectPaymentMethod": {
|
||||
"header": "Selecciona cómo deseas financiar tu pago."
|
||||
|
||||
@@ -1228,7 +1228,8 @@
|
||||
"swapRatesChangedBody": "Le taux de change pour cet échange a changé pendant votre vérification. Veuillez saisir à nouveau le montant pour continuer.",
|
||||
"swapRatesChangedButton": "Ressaisir le montant",
|
||||
"feeEstimateError": "Impossible d’estimer les frais, veuillez réessayer",
|
||||
"brantaVerification": "Ce paiement a été vérifié par Branta"
|
||||
"brantaVerification": "Ce paiement a été vérifié par Branta",
|
||||
"lightningPayment": "Paiement Lightning"
|
||||
},
|
||||
"selectPaymentMethod": {
|
||||
"header": "Veuillez sélectionner comment vous souhaitez financer votre paiement."
|
||||
|
||||
@@ -1228,7 +1228,8 @@
|
||||
"swapRatesChangedBody": "Il tasso di cambio per questo scambio è cambiato durante la revisione. Inserisci nuovamente l’importo per continuare.",
|
||||
"swapRatesChangedButton": "Reinserisci importo",
|
||||
"feeEstimateError": "Impossibile stimare la commissione, riprova",
|
||||
"brantaVerification": "Questo pagamento è stato verificato da Branta"
|
||||
"brantaVerification": "Questo pagamento è stato verificato da Branta",
|
||||
"lightningPayment": "Pagamento Lightning"
|
||||
},
|
||||
"selectPaymentMethod": {
|
||||
"header": "Seleziona come desideri finanziare il pagamento."
|
||||
|
||||
@@ -1227,7 +1227,8 @@
|
||||
"swapRatesChangedBody": "A cotação para esta troca mudou enquanto você estava revisando. Insira o valor novamente para continuar.",
|
||||
"swapRatesChangedButton": "Inserir valor novamente",
|
||||
"feeEstimateError": "Não foi possível estimar a taxa, tente novamente",
|
||||
"brantaVerification": "Este pagamento foi verificado pela Branta"
|
||||
"brantaVerification": "Este pagamento foi verificado pela Branta",
|
||||
"lightningPayment": "Pagamento Lightning"
|
||||
},
|
||||
"selectPaymentMethod": {
|
||||
"header": "Escolha o saldo para realizar esse pagamento"
|
||||
|
||||
@@ -1229,7 +1229,8 @@
|
||||
"swapRatesChangedBody": "Курс обмена для этой операции изменился во время проверки. Пожалуйста, введите сумму заново, чтобы продолжить.",
|
||||
"swapRatesChangedButton": "Ввести сумму снова",
|
||||
"feeEstimateError": "Не удалось оценить комиссию, попробуйте снова",
|
||||
"brantaVerification": "Этот платёж был подтверждён Branta"
|
||||
"brantaVerification": "Этот платёж был подтверждён Branta",
|
||||
"lightningPayment": "Платёж Lightning"
|
||||
},
|
||||
"selectPaymentMethod": {
|
||||
"header": "Выберите источник средств."
|
||||
|
||||
@@ -1228,7 +1228,8 @@
|
||||
"swapRatesChangedBody": "Växelkursen för denna växling ändrades medan du granskade den. Ange beloppet igen för att fortsätta.",
|
||||
"swapRatesChangedButton": "Ange belopp igen",
|
||||
"feeEstimateError": "Kunde inte uppskatta avgiften, försök igen",
|
||||
"brantaVerification": "Denna betalning har verifierats av Branta"
|
||||
"brantaVerification": "Denna betalning har verifierats av Branta",
|
||||
"lightningPayment": "Lightning-betalning"
|
||||
},
|
||||
"selectPaymentMethod": {
|
||||
"header": "Välj hur du vill finansiera din betalning."
|
||||
|
||||