diff --git a/__tests__/todos.ts b/__tests__/todos.ts index 43a300995..f423e5f97 100644 --- a/__tests__/todos.ts +++ b/__tests__/todos.ts @@ -32,7 +32,7 @@ describe('Todos selector', () => { let s: RootState; beforeAll(async () => { - let res = await createNewWallet(); + const res = await createNewWallet(); if (res.isErr()) { throw res.error; } @@ -67,7 +67,7 @@ describe('Todos selector', () => { it('should not return backupSeedPhraseTodo if backup is verified', () => { const state = cloneDeep(s); - state.settings.backupVerified = true; + state.user.backupVerified = true; expect(todosFullSelector(state)).not.toEqual( expect.arrayContaining([backupSeedPhraseTodo]), diff --git a/e2e/slashtags.e2e.js b/e2e/slashtags.e2e.js index 68e5226c5..515e5fdd2 100644 --- a/e2e/slashtags.e2e.js +++ b/e2e/slashtags.e2e.js @@ -144,6 +144,7 @@ d('Profile and Contacts', () => { await expect(element(by.text('WEBSITE'))).toExist(); await expect(element(by.text(satoshi.website))).toExist(); await element(by.id('NavigationBack')).tap(); + await element(by.id('NavigationBack')).tap(); if (device.getPlatform() === 'ios') { // FIXME: this bottom sheet should not appear diff --git a/src/assets/tos.tsx b/src/assets/tos.tsx index 2c0ca92c0..f40eccf0d 100644 --- a/src/assets/tos.tsx +++ b/src/assets/tos.tsx @@ -71,7 +71,7 @@ const BUI = ({ children }: Props): ReactElement => { const TOS = (): ReactElement => (

- Effective date: June 7, 2024 + Effective date: February 4, 2025

@@ -113,10 +113,9 @@ const TOS = (): ReactElement => (

- FUNDS TRANSFERRED USING CAVEMAN JUST-IN-TIME CHANNELS BY THIRD PARTIES - MAY NOT BE DELIVERED TO YOU BY THE THIRD PARTY. YOU ARE REFERRED TO - CLAUSE 9.3 OF THE TERMS AS TO SYNONYM’S LIABILITY FOR THIRD PARTY - SERVICES. + FUNDS TRANSFERRED USING JUST-IN-TIME CHANNELS BY THIRD PARTIES MAY NOT + BE DELIVERED TO YOU BY THE THIRD PARTY. YOU ARE REFERRED TO CLAUSE 9.3 + OF THE TERMS AS TO SYNONYM’S LIABILITY FOR THIRD PARTY SERVICES.

diff --git a/src/components/widgets/CalculatorWidget.tsx b/src/components/widgets/CalculatorWidget.tsx index 79e445852..af29d2823 100644 --- a/src/components/widgets/CalculatorWidget.tsx +++ b/src/components/widgets/CalculatorWidget.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement, useState } from 'react'; +import React, { ReactElement, useEffect, useState } from 'react'; import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import { useCurrency } from '../../hooks/displayValues'; @@ -9,6 +9,8 @@ import { fiatToBitcoinUnit } from '../../utils/conversion'; import { getDisplayValues } from '../../utils/displayValues'; import BaseWidget from './BaseWidget'; +const MAX_BITCOIN = 2_100_000_000_000_000; // Maximum bitcoin amount in sats + const CalculatorWidget = ({ isEditing = false, style, @@ -30,16 +32,58 @@ const CalculatorWidget = ({ return dv.fiatValue.toString(); }); + // biome-ignore lint/correctness/useExhaustiveDependencies: update fiat amount when currency changes + useEffect(() => { + updateFiatAmount(bitcoinAmount); + }, [fiatTicker]); + const updateFiatAmount = (bitcoin: string) => { - const amount = Number(bitcoin); - const dv = getDisplayValues({ satoshis: amount, shouldRoundUpFiat: true }); + // Remove leading zeros for positive numbers + const sanitizedBitcoin = bitcoin.replace(/^0+(?=\d)/, ''); + const amount = Number(sanitizedBitcoin); + // Cap the amount at maximum bitcoin + const cappedAmount = Math.min(amount, MAX_BITCOIN); + const dv = getDisplayValues({ + satoshis: cappedAmount, + shouldRoundUpFiat: true, + }); setFiatAmount(dv.fiatValue.toString()); + // Update bitcoin amount if it was capped + if (cappedAmount !== amount) { + setBitcoinAmount(cappedAmount.toString()); + } }; const updateBitcoinAmount = (fiat: string) => { - const amount = Number(fiat.replace(',', '.')); + // Remove leading zeros and handle decimal separator + const sanitizedFiat = fiat.replace(/^0+(?=\d)/, ''); + // Only convert to number if it's not just a decimal point + const amount = sanitizedFiat === '.' ? 0 : Number(sanitizedFiat); const sats = fiatToBitcoinUnit({ amount }); - setBitcoinAmount(sats.toString()); + // Cap the amount at maximum bitcoin + const cappedSats = Math.min(sats, MAX_BITCOIN); + setBitcoinAmount(cappedSats.toString()); + // Update fiat amount if bitcoin was capped + if (cappedSats !== sats) { + const dv = getDisplayValues({ + satoshis: cappedSats, + shouldRoundUpFiat: true, + }); + setFiatAmount(dv.fiatValue.toString()); + } + }; + + const formatNumberWithSeparators = (value: string): string => { + const endsWithDecimal = value.endsWith('.'); + const cleanNumber = value.replace(/[^\d.]/g, ''); + const [integer, decimal] = cleanNumber.split('.'); + const formattedInteger = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ' '); + + if (decimal !== undefined) { + return `${formattedInteger}.${decimal}`; + } + + return endsWithDecimal ? `${formattedInteger}.` : formattedInteger; }; return ( @@ -56,13 +100,16 @@ const CalculatorWidget = ({ { - setBitcoinAmount(text); - updateFiatAmount(text); + // Remove any spaces before processing + const rawText = text.replace(/\s/g, ''); + const sanitizedText = rawText.replace(/^0+(?=\d)/, ''); + setBitcoinAmount(sanitizedText); + updateFiatAmount(sanitizedText); }} /> @@ -75,13 +122,36 @@ const CalculatorWidget = ({ { - setFiatAmount(text); - updateBitcoinAmount(text); + // Process the input text + const processedText = text + .replace(',', '.') // Convert comma to dot + .replace(/\s/g, ''); // Remove spaces + + // Split and clean the number parts + const [integer, decimal] = processedText.split('.'); + const cleanInteger = integer.replace(/^0+(?=\d)/, '') || '0'; + + // Construct the final number + const finalText = + decimal !== undefined + ? `${cleanInteger}.${decimal.slice(0, 2)}` + : cleanInteger; + + // Update state if valid + if ( + finalText === '' || + finalText === '.' || + finalText === '0.' || + /^\d*\.?\d{0,2}$/.test(finalText) + ) { + setFiatAmount(finalText); + updateBitcoinAmount(finalText); + } }} /> diff --git a/src/components/widgets/WeatherWidget.tsx b/src/components/widgets/WeatherWidget.tsx index 9a63f0eb6..bd164395d 100644 --- a/src/components/widgets/WeatherWidget.tsx +++ b/src/components/widgets/WeatherWidget.tsx @@ -87,7 +87,7 @@ const WeatherWidget = ({ - {nextBlockFee} bitcoin/vB + {nextBlockFee} ₿/vByte )} diff --git a/src/screens/Activity/ActivityListShort.tsx b/src/screens/Activity/ActivityListShort.tsx index 9f232da29..03c9244f5 100644 --- a/src/screens/Activity/ActivityListShort.tsx +++ b/src/screens/Activity/ActivityListShort.tsx @@ -13,7 +13,7 @@ import Button from '../../components/buttons/Button'; import { useAppSelector } from '../../hooks/redux'; import type { RootNavigationProp } from '../../navigation/types'; import { activityItemsSelector } from '../../store/reselect/activity'; -import { EActivityType, IActivityItem } from '../../store/types/activity'; +import { IActivityItem } from '../../store/types/activity'; import { showBottomSheet } from '../../store/utils/ui'; import { Caption13Up } from '../../styles/text'; import { groupActivityItems } from '../../utils/activity'; @@ -31,13 +31,6 @@ const ActivityListShort = (): ReactElement => { return groupActivityItems(sliced); }, [items]); - const droppedItems = useMemo(() => { - const dropped = items.filter((item) => { - return item.activityType === EActivityType.onchain && !item.exists; - }); - return dropped; - }, [items]); - const renderItem = useCallback( ({ item, @@ -76,13 +69,6 @@ const ActivityListShort = (): ReactElement => { {t('activity')} - - {droppedItems.length !== 0 && ( - - {' '} - ({droppedItems.length} {t('activity_removed')}) - - )} {groupedItems.length === 0 ? ( diff --git a/src/screens/Contacts/Contact.tsx b/src/screens/Contacts/Contact.tsx index 79f74c2e0..9070e5cbc 100644 --- a/src/screens/Contacts/Contact.tsx +++ b/src/screens/Contacts/Contact.tsx @@ -135,10 +135,7 @@ const Contact = ({ return ( - navigation.navigate('Contacts')} - /> + -