diff --git a/app/assets/social_logos/aqua.png b/app/assets/social_logos/aqua.png new file mode 100644 index 00000000..b70f2f0a Binary files /dev/null and b/app/assets/social_logos/aqua.png differ diff --git a/app/assets/social_logos/blink.png b/app/assets/social_logos/blink.png new file mode 100644 index 00000000..86ebb2dc Binary files /dev/null and b/app/assets/social_logos/blink.png differ diff --git a/app/assets/social_logos/breez.png b/app/assets/social_logos/breez.png new file mode 100644 index 00000000..01b7f779 Binary files /dev/null and b/app/assets/social_logos/breez.png differ diff --git a/app/assets/social_logos/cake.png b/app/assets/social_logos/cake.png new file mode 100644 index 00000000..70844cdf Binary files /dev/null and b/app/assets/social_logos/cake.png differ diff --git a/app/assets/social_logos/coinos.png b/app/assets/social_logos/coinos.png new file mode 100644 index 00000000..4e742261 Binary files /dev/null and b/app/assets/social_logos/coinos.png differ diff --git a/app/assets/social_logos/manna.png b/app/assets/social_logos/manna.png new file mode 100644 index 00000000..e817e733 Binary files /dev/null and b/app/assets/social_logos/manna.png differ diff --git a/app/assets/social_logos/orangePillApp.png b/app/assets/social_logos/orangePillApp.png new file mode 100644 index 00000000..53c082c5 Binary files /dev/null and b/app/assets/social_logos/orangePillApp.png differ diff --git a/app/assets/social_logos/strike.png b/app/assets/social_logos/strike.png new file mode 100644 index 00000000..4b540b40 Binary files /dev/null and b/app/assets/social_logos/strike.png differ diff --git a/app/assets/social_logos/tether.png b/app/assets/social_logos/tether.png new file mode 100644 index 00000000..2f6d840d Binary files /dev/null and b/app/assets/social_logos/tether.png differ diff --git a/app/assets/social_logos/walletofsatoshi.png b/app/assets/social_logos/walletofsatoshi.png new file mode 100644 index 00000000..3dd2dfeb Binary files /dev/null and b/app/assets/social_logos/walletofsatoshi.png differ diff --git a/app/assets/social_logos/zeus.png b/app/assets/social_logos/zeus.png new file mode 100644 index 00000000..1811613e Binary files /dev/null and b/app/assets/social_logos/zeus.png differ diff --git a/app/components/admin/homeComponents/sendBitcoin/components/invoiceInfo.js b/app/components/admin/homeComponents/sendBitcoin/components/invoiceInfo.js index 5bb626eb..32f52ab0 100644 --- a/app/components/admin/homeComponents/sendBitcoin/components/invoiceInfo.js +++ b/app/components/admin/homeComponents/sendBitcoin/components/invoiceInfo.js @@ -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) => ( + + {group} + {i < all.length - 1 ? ' ' : ''} + + )); + }, [formmateedSparkPaymentInfo.address]); + + let paymentContent; + if (isLNURLPay) { + paymentContent = ( + + + {providerIconKey ? ( + + ) : ( + + )} + + + + ); + } else if (paymentType === 'lightning') { + paymentContent = ( + + + + + + + ); + } else { + // bitcoin / spark / lrc20 + paymentContent = ( + + ); + } + + const Container = isClickable ? TouchableOpacity : View; + return ( - { 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 ? ( @@ -94,18 +236,12 @@ export default function InvoiceInfo({ /> ) : ( - + paymentContent )} - + {isClickable && ( + + )} + ); } @@ -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%', + }, }); diff --git a/app/components/admin/homeComponents/sendBitcoin/stablecoinSendScreen.js b/app/components/admin/homeComponents/sendBitcoin/stablecoinSendScreen.js index 7b398df8..f7a86cc9 100644 --- a/app/components/admin/homeComponents/sendBitcoin/stablecoinSendScreen.js +++ b/app/components/admin/homeComponents/sendBitcoin/stablecoinSendScreen.js @@ -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 }]} > - - - - - - + + + + + + + + + - )} @@ -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, diff --git a/app/constants/icons.js b/app/constants/icons.js index 9f777442..dd7797af 100644 --- a/app/constants/icons.js +++ b/app/constants/icons.js @@ -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, }; diff --git a/locales/de-DE/translation.json b/locales/de-DE/translation.json index a90a3a60..db75b338 100644 --- a/locales/de-DE/translation.json +++ b/locales/de-DE/translation.json @@ -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)?" diff --git a/locales/en/translation.json b/locales/en/translation.json index 1302a9e3..66d5bed9 100644 --- a/locales/en/translation.json +++ b/locales/en/translation.json @@ -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?" diff --git a/locales/es/translation.json b/locales/es/translation.json index 323134ce..85c2a1c6 100644 --- a/locales/es/translation.json +++ b/locales/es/translation.json @@ -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." diff --git a/locales/fr/translation.json b/locales/fr/translation.json index 8a60eee2..69f9941c 100644 --- a/locales/fr/translation.json +++ b/locales/fr/translation.json @@ -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." diff --git a/locales/it/translation.json b/locales/it/translation.json index 986f3e86..29bc5a79 100644 --- a/locales/it/translation.json +++ b/locales/it/translation.json @@ -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." diff --git a/locales/pt-BR/translation.json b/locales/pt-BR/translation.json index d1d5227f..d6c1db21 100644 --- a/locales/pt-BR/translation.json +++ b/locales/pt-BR/translation.json @@ -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" diff --git a/locales/ru/translation.json b/locales/ru/translation.json index 3bf8cc00..d99ed5ff 100644 --- a/locales/ru/translation.json +++ b/locales/ru/translation.json @@ -1229,7 +1229,8 @@ "swapRatesChangedBody": "Курс обмена для этой операции изменился во время проверки. Пожалуйста, введите сумму заново, чтобы продолжить.", "swapRatesChangedButton": "Ввести сумму снова", "feeEstimateError": "Не удалось оценить комиссию, попробуйте снова", - "brantaVerification": "Этот платёж был подтверждён Branta" + "brantaVerification": "Этот платёж был подтверждён Branta", + "lightningPayment": "Платёж Lightning" }, "selectPaymentMethod": { "header": "Выберите источник средств." diff --git a/locales/sv/translation.json b/locales/sv/translation.json index 967c0f93..1876a122 100644 --- a/locales/sv/translation.json +++ b/locales/sv/translation.json @@ -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."