diff --git a/app/src/app/(modal)/_layout.tsx b/app/src/app/(modal)/_layout.tsx
index 177c3b669..978fcba4c 100644
--- a/app/src/app/(modal)/_layout.tsx
+++ b/app/src/app/(modal)/_layout.tsx
@@ -1,16 +1,8 @@
-import { Slot, Stack } from 'expo-router';
+import { Slot } from 'expo-router';
export default function SheetLayout() {
return (
<>
-
>
);
diff --git a/app/src/app/(nav)/[account]/(home)/_layout.tsx b/app/src/app/(nav)/[account]/(home)/_layout.tsx
index 9f506e09a..574966919 100644
--- a/app/src/app/(nav)/[account]/(home)/_layout.tsx
+++ b/app/src/app/(nav)/[account]/(home)/_layout.tsx
@@ -1,11 +1,10 @@
import { Panes } from '#/layout/Panes';
-import { Slot, Stack } from 'expo-router';
+import { Slot } from 'expo-router';
import { HomePane } from './index';
export default function HomeLayout() {
return (
-
diff --git a/app/src/app/(nav)/[account]/(home)/activity/_layout.tsx b/app/src/app/(nav)/[account]/(home)/activity/_layout.tsx
index 11921efcf..900313a0d 100644
--- a/app/src/app/(nav)/[account]/(home)/activity/_layout.tsx
+++ b/app/src/app/(nav)/[account]/(home)/activity/_layout.tsx
@@ -1,4 +1,3 @@
-import { Panes } from '#/layout/Panes';
import { Slot } from 'expo-router';
import { ActivityPane } from './index';
diff --git a/app/src/app/(nav)/[account]/(home)/index.tsx b/app/src/app/(nav)/[account]/(home)/index.tsx
index 84c145a8f..b14e6740b 100644
--- a/app/src/app/(nav)/[account]/(home)/index.tsx
+++ b/app/src/app/(nav)/[account]/(home)/index.tsx
@@ -63,17 +63,17 @@ function HomePane_() {
return (
+ }
+ style={styles.appbar}
+ noPadding
+ />
- }
- style={styles.appbar}
- noPadding
- />
diff --git a/app/src/app/(nav)/[account]/(home)/send.tsx b/app/src/app/(nav)/[account]/(home)/send.tsx
new file mode 100644
index 000000000..820cffce7
--- /dev/null
+++ b/app/src/app/(nav)/[account]/(home)/send.tsx
@@ -0,0 +1,135 @@
+import { PaneSkeleton } from '#/skeleton/PaneSkeleton';
+import { withSuspense } from '#/skeleton/withSuspense';
+import { zUAddress } from '~/lib/zod';
+import { AccountParams } from '../_layout';
+import { useLocalParams } from '~/hooks/useLocalParams';
+import { graphql } from 'relay-runtime';
+import { useState } from 'react';
+import Decimal from 'decimal.js';
+import { z } from 'zod';
+import { Pane } from '#/layout/Pane';
+import { TokenAmountInput } from '#/token/TokenAmountInput';
+import { useLazyQuery } from '~/api';
+import { send_SendScreen2Query } from '~/api/__generated__/send_SendScreen2Query.graphql';
+import { useSelectedToken } from '~/hooks/useSelectToken';
+import { asChain } from 'lib';
+import { Scrollable } from '#/Scrollable';
+import { Appbar } from '#/Appbar/Appbar';
+import { View } from 'react-native';
+import { createStyles, useStyles } from '@theme/styles';
+import { SendMode, SendModeChips } from '#/send/SendModeChips';
+import { ItemList } from '#/layout/ItemList';
+import { SendAccount } from '#/send/SendAccount';
+import { SendTo } from '#/send/SendTo';
+import { match } from 'ts-pattern';
+import { TransferMode } from '#/send/TransferMode';
+import { TransferFromMode } from '#/send/TransferFromMode';
+import { Text } from 'react-native-paper';
+
+const Query = graphql`
+ query send_SendScreen2Query($account: UAddress!, $token: UAddress!) {
+ token(address: $token) @required(action: THROW) {
+ id
+ address
+ decimals
+ balance(input: { account: $account })
+ price {
+ usd
+ }
+ ...TokenAmountInput_token
+ ...SendAccount_token @arguments(account: $account)
+ ...TransferMode_token
+ ...TransferFromMode_token
+ }
+
+ account(address: $account) @required(action: THROW) {
+ address
+ ...useProposeTransaction_account
+ ...SendAccount_account
+ ...TransferMode_account
+ ...TransferFromMode_account
+ }
+ }
+`;
+
+export const SendScreenParams = AccountParams.extend({
+ to: zUAddress().optional(),
+});
+export type SendScreenParams = z.infer;
+
+function SendScreen() {
+ const params = useLocalParams(SendScreenParams);
+ const { styles } = useStyles(stylesheet);
+ const chain = asChain(params.account);
+
+ const { token, account } = useLazyQuery(Query, {
+ account: params.account,
+ token: useSelectedToken(chain),
+ });
+
+ const [amount, setAmount] = useState(new Decimal(0));
+ const [mode, setMode] = useState('transfer');
+ const [to, setTo] = useState(params.to);
+
+ const warning = amount.gt(token.balance) && 'Insufficient balance';
+
+ return (
+
+
+
+
+
+
+ {warning}
+
+
+
+
+
+
+
+
+
+
+
+
+ {match(mode)
+ .with('transfer', () => (
+
+ ))
+ .with('transferFrom', () => (
+
+ ))
+ .exhaustive()}
+
+
+ );
+}
+
+const stylesheet = createStyles(({ colors, padding, negativeMargin }) => ({
+ container: {
+ gap: 8,
+ paddingHorizontal: padding,
+ },
+ inputContainer: {
+ marginVertical: 16,
+ },
+ sendModeChipsContainer: {
+ marginHorizontal: negativeMargin,
+ },
+ item: {
+ backgroundColor: colors.surface,
+ },
+ warning: {
+ color: colors.warning,
+ },
+}));
+
+export default withSuspense(SendScreen, );
+
+export { ErrorBoundary } from '#/ErrorBoundary';
diff --git a/app/src/app/(nav)/[account]/_layout.tsx b/app/src/app/(nav)/[account]/_layout.tsx
index 039388dcf..cc8e0a18a 100644
--- a/app/src/app/(nav)/[account]/_layout.tsx
+++ b/app/src/app/(nav)/[account]/_layout.tsx
@@ -1,5 +1,5 @@
import { useEffect, useLayoutEffect } from 'react';
-import { Redirect, Stack, useLocalSearchParams, useRouter } from 'expo-router';
+import { Redirect, Slot, useLocalSearchParams, useRouter } from 'expo-router';
import {
ErrorBoundary as BaseErrorBoundary,
ErrorBoundaryProps,
@@ -9,7 +9,6 @@ import NotFound from '~/app/+not-found';
import { useLocalParams } from '~/hooks/useLocalParams';
import { useSelectedAccount, useSetSelectedAccont } from '~/hooks/useSelectedAccount';
import { zUAddress } from '~/lib/zod';
-import { AppbarHeader } from '#/Appbar/AppbarHeader';
import { withSuspense } from '#/skeleton/withSuspense';
import { Splash } from '#/Splash';
import { graphql } from 'relay-runtime';
@@ -52,12 +51,7 @@ export function AccountLayout() {
// Redirect to the home page if account isn't found
if (!found) return ;
- return (
- <>
-
- }} />
- >
- );
+ return ;
}
export default withSuspense(AccountLayout, );
diff --git a/app/src/app/(nav)/[account]/send.tsx b/app/src/app/(nav)/[account]/send.tsx
deleted file mode 100644
index fc3bc0c05..000000000
--- a/app/src/app/(nav)/[account]/send.tsx
+++ /dev/null
@@ -1,152 +0,0 @@
-import { useRouter } from 'expo-router';
-import { useProposeTransaction } from '~/hooks/mutations/useProposeTransaction';
-import { FIAT_DECIMALS, asAddress, asChain, asFp, asUAddress } from 'lib';
-import { useEffect, useState } from 'react';
-import { StyleSheet, View } from 'react-native';
-import { Divider } from 'react-native-paper';
-import { useAddressLabel } from '#/address/AddressLabel';
-import { NumericInput } from '#/fields/NumericInput';
-import { TokenItem } from '#/token/TokenItem';
-import { InputsView, InputType } from '../../../components/InputsView';
-import { Button } from '#/Button';
-import { useInvalidateRecentToken, useSelectToken, useSelectedToken } from '~/hooks/useSelectToken';
-import { createTransferOp } from '~/lib/transfer';
-import { AppbarOptions } from '#/Appbar/AppbarOptions';
-import { z } from 'zod';
-import { zAddress, zUAddress } from '~/lib/zod';
-import { useLocalParams } from '~/hooks/useLocalParams';
-import { Actions } from '#/layout/Actions';
-import { withSuspense } from '#/skeleton/withSuspense';
-import { ScreenSkeleton } from '#/skeleton/ScreenSkeleton';
-import { ScrollableScreenSurface } from '#/layout/ScrollableScreenSurface';
-import Decimal from 'decimal.js';
-import { ampli } from '~/lib/ampli';
-import { graphql } from 'relay-runtime';
-import { useLazyQuery } from '~/api';
-import { send_SendScreenQuery } from '~/api/__generated__/send_SendScreenQuery.graphql';
-
-const Query = graphql`
- query send_SendScreenQuery($account: UAddress!, $token: UAddress!) {
- token(address: $token) {
- id
- address
- decimals
- balance(input: { account: $account })
- price {
- id
- usd
- }
- ...InputsView_token @arguments(account: $account)
- ...TokenItem_token
- }
-
- account(address: $account) @required(action: THROW) {
- ...useProposeTransaction_account
- }
- }
-`;
-
-const SendScreenParams = z.object({
- account: zUAddress(),
- to: zAddress(),
-});
-export type SendScreenParams = z.infer;
-
-function SendScreen() {
- const { account: accountAddress, to } = useLocalParams(SendScreenParams);
- const chain = asChain(accountAddress);
- const router = useRouter();
- const propose = useProposeTransaction();
- const toLabel = useAddressLabel(asUAddress(to, chain));
- const invalidateRecent = useInvalidateRecentToken(chain);
- const selectToken = useSelectToken();
- const selectedToken = useSelectedToken(chain);
-
- const { token, account } = useLazyQuery(Query, {
- account: accountAddress,
- token: selectedToken,
- });
-
- const [input, setInput] = useState('');
- const [type, setType] = useState(InputType.Token);
-
- useEffect(() => {
- if (!token) invalidateRecent(selectedToken);
- }, [chain, invalidateRecent, selectedToken, token]);
-
- if (!token) return null;
-
- const inputAmount = input || '0';
- const amount =
- type === InputType.Token
- ? new Decimal(inputAmount)
- : new Decimal(inputAmount).div(token.price?.usd ?? 0);
-
- return (
- <>
-
-
-
-
-
-
-
- selectToken({ account: accountAddress })}
- />
-
-
-
-
-
-
-
-
- >
- );
-}
-
-const styles = StyleSheet.create({
- spacer: {
- flex: 1,
- },
- action: {
- marginHorizontal: 16,
- marginBottom: 16,
- alignSelf: 'stretch',
- },
-});
-
-export default withSuspense(SendScreen, ScreenSkeleton);
-
-export { ErrorBoundary } from '#/ErrorBoundary';
diff --git a/app/src/app/(nav)/[account]/settings/_layout.tsx b/app/src/app/(nav)/[account]/settings/_layout.tsx
index 1da3890c3..d44ab9cd9 100644
--- a/app/src/app/(nav)/[account]/settings/_layout.tsx
+++ b/app/src/app/(nav)/[account]/settings/_layout.tsx
@@ -1,16 +1,13 @@
import { Panes } from '#/layout/Panes';
-import { Slot, Stack } from 'expo-router';
+import { Slot } from 'expo-router';
import _ from 'lodash';
import { AccountSettingsPane } from './index';
export default function AccountSettingsLayout() {
return (
- <>
-
-
-
-
-
- >
+
+
+
+
);
}
diff --git a/app/src/app/(nav)/[account]/settings/details.tsx b/app/src/app/(nav)/[account]/settings/details.tsx
index e3113f482..2d1b8654b 100644
--- a/app/src/app/(nav)/[account]/settings/details.tsx
+++ b/app/src/app/(nav)/[account]/settings/details.tsx
@@ -4,7 +4,6 @@ import { AccountSettingsParams } from './index';
import { useForm } from 'react-hook-form';
import { createStyles } from '@theme/styles';
import { Appbar } from '#/Appbar/Appbar';
-import { Surface } from '#/layout/Surface';
import { View } from 'react-native';
import { AccountNameFormField } from '#/fields/AccountNameFormField';
import { Actions } from '#/layout/Actions';
diff --git a/app/src/app/(nav)/[account]/swap.tsx b/app/src/app/(nav)/[account]/swap.tsx
index 1805aee9c..7509ea96a 100644
--- a/app/src/app/(nav)/[account]/swap.tsx
+++ b/app/src/app/(nav)/[account]/swap.tsx
@@ -8,7 +8,6 @@ import { View } from 'react-native';
import { NumericInput } from '#/fields/NumericInput';
import { DateTime } from 'luxon';
import { Button } from '#/Button';
-import { AppbarOptions } from '#/Appbar/AppbarOptions';
import { useLocalParams } from '~/hooks/useLocalParams';
import { withSuspense } from '#/skeleton/withSuspense';
import { ScrollableScreenSurface } from '#/layout/ScrollableScreenSurface';
@@ -31,6 +30,7 @@ import { ScreenSkeleton } from '#/skeleton/ScreenSkeleton';
import { graphql } from 'relay-runtime';
import { useLazyQuery } from '~/api';
import { swap_SwapScreenQuery } from '~/api/__generated__/swap_SwapScreenQuery.graphql';
+import { Appbar } from '#/Appbar/Appbar';
const DownArrow = materialCommunityIcon('arrow-down-thin');
const ICON_BUTTON_SIZE = 24;
@@ -112,7 +112,7 @@ function SwapScreen() {
return (
<>
-
+
diff --git a/app/src/app/(nav)/[account]/tokens.tsx b/app/src/app/(nav)/[account]/tokens.tsx
index b1a5e3510..781af6de8 100644
--- a/app/src/app/(nav)/[account]/tokens.tsx
+++ b/app/src/app/(nav)/[account]/tokens.tsx
@@ -10,12 +10,12 @@ import { zUAddress } from '~/lib/zod';
import { useLocalParams } from '~/hooks/useLocalParams';
import { withSuspense } from '#/skeleton/withSuspense';
import { ScreenSkeleton } from '#/skeleton/ScreenSkeleton';
-import { SearchbarOptions } from '#/Appbar/SearchbarOptions';
import { ScreenSurface } from '#/layout/ScreenSurface';
import { MenuOrSearchIcon } from '#/Appbar/MenuOrSearchIcon';
import { graphql } from 'relay-runtime';
import { tokens_TokensScreenQuery } from '~/api/__generated__/tokens_TokensScreenQuery.graphql';
import { useLazyQuery } from '~/api';
+import { Searchbar } from '#/Appbar/Searchbar';
const Query = graphql`
query tokens_TokensScreenQuery($account: UAddress!, $chain: Chain, $query: String) {
@@ -44,7 +44,7 @@ function TokensScreen() {
return (
<>
- router.push(`/token/add`)} />}
diff --git a/app/src/app/(nav)/_layout.tsx b/app/src/app/(nav)/_layout.tsx
index 672e485f4..46b410efb 100644
--- a/app/src/app/(nav)/_layout.tsx
+++ b/app/src/app/(nav)/_layout.tsx
@@ -17,16 +17,15 @@ import {
import { DrawerItem as Item } from '#/drawer/DrawerItem';
import { useSelectedAccount } from '~/hooks/useSelectedAccount';
import { CONFIG } from '~/util/config';
-import { useSend } from '~/hooks/useSend';
import { DrawerSurface } from '#/drawer/DrawerSurface';
-import { Link, Stack } from 'expo-router';
-import { AppbarHeader } from '#/Appbar/AppbarHeader';
+import { Link, Slot, useRouter } from 'expo-router';
import { DrawerLogo } from '#/drawer/DrawerLogo';
import { createStyles, useStyles } from '@theme/styles';
import { PressableOpacity } from '#/PressableOpacity';
import { Fab } from '#/Fab';
import { RailSurface } from '#/drawer/RailSurface';
import { RailItem } from '#/drawer/RailItem';
+import { SendScreenParams } from './[account]/(home)/send';
const Section = PaperDrawer.Section;
@@ -37,7 +36,7 @@ export const unstable_settings = {
export default function DrawerLayout() {
return (
- }} />
+
);
}
@@ -45,7 +44,7 @@ export default function DrawerLayout() {
function RailContent() {
const { styles } = useStyles(railStylesheet);
const account = useSelectedAccount();
- const send = useSend();
+ const router = useRouter();
return (
}
style={styles.fabContainer}
- loading={false}
- onPress={() => send({ account })}
animated={false}
+ onPress={() =>
+ router.push({
+ pathname: `/(nav)/[account]/send`,
+ params: { account } satisfies SendScreenParams,
+ })
+ }
/>
)
}
@@ -111,7 +114,6 @@ const railStylesheet = createStyles(({ colors }) => ({
function DrawerContent() {
const account = useSelectedAccount();
- const send = useSend();
return (
@@ -137,7 +139,6 @@ function DrawerContent() {
href={{ pathname: `/(nav)/[account]/send`, params: { account } }}
icon={OutboundIcon}
label="Send"
- onPress={() => send({ account })}
/>
)}
diff --git a/app/src/app/(nav)/accounts/create.tsx b/app/src/app/(nav)/accounts/create.tsx
index a0f4a9b2a..a8e46b39c 100644
--- a/app/src/app/(nav)/accounts/create.tsx
+++ b/app/src/app/(nav)/accounts/create.tsx
@@ -1,4 +1,4 @@
-import { AppbarOptions } from '#/Appbar/AppbarOptions';
+import { Appbar } from '#/Appbar/Appbar';
import { withSuspense } from '#/skeleton/withSuspense';
import { ScreenSkeleton } from '#/skeleton/ScreenSkeleton';
import { ScrollableScreenSurface } from '#/layout/ScrollableScreenSurface';
@@ -7,7 +7,7 @@ import { CreateAccount } from '#/CreateAccount';
function CreateAccountScreen() {
return (
<>
-
+
diff --git a/app/src/app/(nav)/contacts/_layout.tsx b/app/src/app/(nav)/contacts/_layout.tsx
index 81ef56872..d41ea6a6f 100644
--- a/app/src/app/(nav)/contacts/_layout.tsx
+++ b/app/src/app/(nav)/contacts/_layout.tsx
@@ -1,19 +1,16 @@
import { Panes } from '#/layout/Panes';
-import { Slot, Stack } from 'expo-router';
+import { Slot } from 'expo-router';
import { ContactsPane } from './index';
import { Pane } from '#/layout/Pane';
export default function ContactsLayout() {
return (
- <>
-
-
-
-
-
+
+
+
+
-
-
- >
+
+
);
}
diff --git a/app/src/app/(nav)/ledger/link.tsx b/app/src/app/(nav)/ledger/link.tsx
index d4039b584..a7b2d0663 100644
--- a/app/src/app/(nav)/ledger/link.tsx
+++ b/app/src/app/(nav)/ledger/link.tsx
@@ -6,7 +6,7 @@ import { Actions } from '#/layout/Actions';
import { Button } from '#/Button';
import { match } from 'ts-pattern';
import { LedgerItem } from '#/link/ledger/LedgerItem';
-import { AppbarOptions } from '#/Appbar/AppbarOptions';
+import { Appbar } from '#/Appbar/Appbar';
import { useObservable } from '~/hooks/useObservable';
import { bleDevices } from '~/lib/ble/manager';
import { ok } from 'neverthrow';
@@ -36,7 +36,7 @@ function LinkLedgerScreen() {
return (
<>
-
+
diff --git a/app/src/app/(nav)/message/[id].tsx b/app/src/app/(nav)/message/[id].tsx
index 5bc8674de..deb9e2e45 100644
--- a/app/src/app/(nav)/message/[id].tsx
+++ b/app/src/app/(nav)/message/[id].tsx
@@ -3,7 +3,7 @@ import { useLocalParams } from '~/hooks/useLocalParams';
import { zUuid } from '~/lib/zod';
import { AppbarMore } from '#/Appbar/AppbarMore';
import { Divider, Menu } from 'react-native-paper';
-import { AppbarOptions } from '#/Appbar/AppbarOptions';
+import { Appbar } from '#/Appbar/Appbar';
import { ScrollableScreenSurface } from '#/layout/ScrollableScreenSurface';
import { MessageStatus } from '#/message/MessageStatus';
import { StyleSheet, View } from 'react-native';
@@ -58,7 +58,7 @@ export default function MessageScreen() {
return (
- }
mode="large"
{...(remove && {
diff --git a/app/src/app/(nav)/sessions/index.tsx b/app/src/app/(nav)/sessions/index.tsx
index fb82c6aa6..83941e832 100644
--- a/app/src/app/(nav)/sessions/index.tsx
+++ b/app/src/app/(nav)/sessions/index.tsx
@@ -4,7 +4,7 @@ import { StyleSheet, FlatList } from 'react-native';
import { useWalletConnect } from '~/lib/wc';
import { PairingItem } from '#/walletconnect/PairingItem';
import { Divider, Text } from 'react-native-paper';
-import { AppbarOptions } from '#/Appbar/AppbarOptions';
+import { Appbar } from '#/Appbar/Appbar';
import { withSuspense } from '#/skeleton/withSuspense';
import { ScreenSkeleton } from '#/skeleton/ScreenSkeleton';
import { ScreenSurface } from '#/layout/ScreenSurface';
@@ -24,7 +24,7 @@ function SessionsScreen() {
return (
<>
-
+
-
+
diff --git a/app/src/app/(nav)/settings/notifications.tsx b/app/src/app/(nav)/settings/notifications.tsx
index ad22222f2..bc3dab28f 100644
--- a/app/src/app/(nav)/settings/notifications.tsx
+++ b/app/src/app/(nav)/settings/notifications.tsx
@@ -1,4 +1,4 @@
-import { AppbarOptions } from '#/Appbar/AppbarOptions';
+import { Appbar } from '#/Appbar/Appbar';
import { ScrollableScreenSurface } from '#/layout/ScrollableScreenSurface';
import { NotificationSettings } from '#/NotificationSettings';
import { ScreenSkeleton } from '#/skeleton/ScreenSkeleton';
@@ -8,7 +8,7 @@ import { createStyles } from '@theme/styles';
export function NotificationSettingsScreen() {
return (
<>
-
+
diff --git a/app/src/app/(nav)/token/[address].tsx b/app/src/app/(nav)/token/[address].tsx
index 36aed88d5..5ca1404c0 100644
--- a/app/src/app/(nav)/token/[address].tsx
+++ b/app/src/app/(nav)/token/[address].tsx
@@ -7,7 +7,7 @@ import { FormTextField } from '#/fields/FormTextField';
import { Actions } from '#/layout/Actions';
import { AppbarMore } from '#/Appbar/AppbarMore';
import { Menu } from 'react-native-paper';
-import { AppbarOptions } from '#/Appbar/AppbarOptions';
+import { Appbar } from '#/Appbar/Appbar';
import { withSuspense } from '#/skeleton/withSuspense';
import { ScreenSkeleton } from '#/skeleton/ScreenSkeleton';
import { z } from 'zod';
@@ -92,7 +92,7 @@ function TokenScreen_() {
return (
- (
diff --git a/app/src/app/(nav)/token/add.tsx b/app/src/app/(nav)/token/add.tsx
index 9da42075a..259772183 100644
--- a/app/src/app/(nav)/token/add.tsx
+++ b/app/src/app/(nav)/token/add.tsx
@@ -1,4 +1,4 @@
-import { AppbarOptions } from '#/Appbar/AppbarOptions';
+import { Appbar } from '#/Appbar/Appbar';
import { FormSelectChip } from '#/fields/FormSelectChip';
import { FormSubmitButton } from '#/fields/FormSubmitButton';
import { FormTextField } from '#/fields/FormTextField';
@@ -31,7 +31,7 @@ export default function AddTokenScreen() {
return (
<>
-
+
diff --git a/app/src/app/(nav)/transaction/[id].tsx b/app/src/app/(nav)/transaction/[id].tsx
index 85a597b85..869dabf18 100644
--- a/app/src/app/(nav)/transaction/[id].tsx
+++ b/app/src/app/(nav)/transaction/[id].tsx
@@ -1,7 +1,7 @@
import { AppbarMore } from '#/Appbar/AppbarMore';
import { z } from 'zod';
import { useLocalParams } from '~/hooks/useLocalParams';
-import { AppbarOptions } from '#/Appbar/AppbarOptions';
+import { Appbar } from '#/Appbar/Appbar';
import { ScrollableScreenSurface } from '#/layout/ScrollableScreenSurface';
import { zUuid } from '~/lib/zod';
import { TransactionStatus } from '#/transaction/TransactionStatus';
@@ -92,7 +92,7 @@ function TransactionScreen() {
return (
<>
- }
{...(remove && {
trailing: (props) => (
diff --git a/app/src/app/(sheet)/_layout.tsx b/app/src/app/(sheet)/_layout.tsx
index c884e22ad..978fcba4c 100644
--- a/app/src/app/(sheet)/_layout.tsx
+++ b/app/src/app/(sheet)/_layout.tsx
@@ -1,16 +1,8 @@
-import { Slot, Stack } from 'expo-router';
+import { Slot } from 'expo-router';
export default function SheetLayout() {
return (
<>
-
>
);
diff --git a/app/src/app/_layout.tsx b/app/src/app/_layout.tsx
index 352e2bf6e..ede6e2c47 100644
--- a/app/src/app/_layout.tsx
+++ b/app/src/app/_layout.tsx
@@ -15,7 +15,6 @@ import { NotificationsProvider } from '#/provider/NotificationsProvider';
import { SnackbarProvider } from '#/provider/SnackbarProvider';
import { UpdateProvider } from '#/provider/UpdateProvider';
import { ThemeProvider } from '~/util/theme/ThemeProvider';
-import { AppbarHeader } from '#/Appbar/AppbarHeader';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { Portal as RnpPortal } from 'react-native-paper';
import { TQueryProvider } from '#/provider/TQueryProvider';
@@ -33,13 +32,23 @@ export const unstable_settings = {
function Layout() {
return (
- }}>
-
-
-
-
-
-
+
+
+
);
}
diff --git a/app/src/app/index.tsx b/app/src/app/index.tsx
index 392be0a34..995e7966c 100644
--- a/app/src/app/index.tsx
+++ b/app/src/app/index.tsx
@@ -1,4 +1,4 @@
-import { ScrollView, View } from 'react-native';
+import { View } from 'react-native';
import { createStyles, useStyles } from '@theme/styles';
import { LandingHeader } from '#/landing/LandingHeader';
import { PrimarySection } from '#/landing/PrimarySection';
@@ -13,6 +13,7 @@ import { ScreenSkeleton } from '#/skeleton/ScreenSkeleton';
import { graphql } from 'relay-runtime';
import { useLazyQuery } from '~/api';
import { app_LandingScreenQuery } from '~/api/__generated__/app_LandingScreenQuery.graphql';
+import { Scrollable } from '#/Scrollable';
// Must use Query.accounts to avoid potential redirect loop with AccountLayout
const Query = graphql`
@@ -38,7 +39,7 @@ function LandingScreen() {
return ;
return (
-
+
-
+
);
}
diff --git a/app/src/app/scan.tsx b/app/src/app/scan.tsx
index a7e2c730f..c65935418 100644
--- a/app/src/app/scan.tsx
+++ b/app/src/app/scan.tsx
@@ -13,7 +13,7 @@ import { useFocusEffect, useRouter } from 'expo-router';
import { ScanOverlay } from '#/ScanOverlay';
import { Subject } from 'rxjs';
import { useGetEvent } from '~/hooks/useGetEvent';
-import { AppbarOptions } from '#/Appbar/AppbarOptions';
+import { Appbar } from '#/Appbar/Appbar';
import { z } from 'zod';
import { zUAddress } from '~/lib/zod';
import { useLocalParams } from '~/hooks/useLocalParams';
@@ -92,8 +92,7 @@ export default function ScanScreen() {
) : (
-
-
+
Please grant camera permissions in order to scan a QR code
diff --git a/app/src/components/Chip.tsx b/app/src/components/Chip.tsx
index 16515aff5..ec7cd4c69 100644
--- a/app/src/components/Chip.tsx
+++ b/app/src/components/Chip.tsx
@@ -1,19 +1,35 @@
-import { ComponentPropsWithoutRef } from 'react';
+import { IconProps } from '@theme/icons';
+import { createStyles, useStyles } from '@theme/styles';
+import { ComponentPropsWithoutRef, FC } from 'react';
import { Chip as Base } from 'react-native-paper';
import { useWithLoading } from '~/hooks/useWithLoading';
type BaseProps = ComponentPropsWithoutRef;
-export interface ChipProps extends BaseProps {}
+export interface ChipProps extends Omit {
+ icon?: FC;
+}
+
+export function Chip({ icon: Icon, selected, ...props }: ChipProps) {
+ const { styles } = useStyles(stylesheet);
-export function Chip(props: ChipProps) {
const [loading, onPress] = useWithLoading(props.onPress);
return (
})}
onPress={onPress}
{...(loading && { disabled: true })}
/>
);
}
+
+const stylesheet = createStyles(({ colors }) => ({
+ unselectedIcon: {
+ color: colors.onSurface,
+ },
+}));
diff --git a/app/src/components/fields/DecimalInput.tsx b/app/src/components/fields/DecimalInput.tsx
new file mode 100644
index 000000000..b17f93f40
--- /dev/null
+++ b/app/src/components/fields/DecimalInput.tsx
@@ -0,0 +1,47 @@
+import Decimal from 'decimal.js';
+import { ComponentType, Dispatch, SetStateAction, startTransition, useState } from 'react';
+import { TextInput as NativeTextInput, TextInputProps } from 'react-native';
+import { z } from 'zod';
+
+const decimal = z
+ .string()
+ .regex(/^\d*(\.\d*)?$/)
+ .transform((v, ctx) => {
+ try {
+ if (v === '') return new Decimal(0);
+ return new Decimal(v);
+ } catch {
+ ctx.addIssue({ code: 'custom', message: 'Must be a valid decimal' });
+ return z.NEVER;
+ }
+ });
+
+export interface DecimalInputProps extends Omit {
+ value: Decimal;
+ onChange: Dispatch>;
+ as?: ComponentType;
+}
+
+export function DecimalInput({
+ as: TextInput = NativeTextInput,
+ value,
+ onChange,
+ ...props
+}: DecimalInputProps) {
+ const [input, setInput] = useState(() => (value.isZero() ? '' : value.toString()));
+
+ const handleChangeText = (input: string) => {
+ const parsed = decimal.safeParse(input);
+ if (parsed.data) {
+ startTransition(() => onChange(parsed.data));
+ } else {
+ // If the input is not a valid decimal, don't update the value
+ }
+
+ if (parsed.success || input === '.') setInput(input);
+ };
+
+ return (
+
+ );
+}
diff --git a/app/src/components/home/QuickActions.tsx b/app/src/components/home/QuickActions.tsx
index 93cca90c8..40ffce92c 100644
--- a/app/src/components/home/QuickActions.tsx
+++ b/app/src/components/home/QuickActions.tsx
@@ -2,10 +2,10 @@ import { OutboundIcon, SwapIcon, ScanIcon, ReceiveIcon } from '@theme/icons';
import { ScrollView, View } from 'react-native';
import { UAddress } from 'lib';
import { Link } from 'expo-router';
-import { useSend } from '~/hooks/useSend';
import { CORNER } from '@theme/paper';
import { createStyles, useStyles } from '@theme/styles';
import { Button } from '#/Button';
+import { SendScreenParams } from '~/app/(nav)/[account]/(home)/send';
export interface QuickActionsProps {
account: UAddress;
@@ -13,7 +13,6 @@ export interface QuickActionsProps {
export function QuickActions({ account }: QuickActionsProps) {
const { styles } = useStyles(stylesheet);
- const send = useSend();
return (
@@ -22,15 +21,17 @@ export function QuickActions({ account }: QuickActionsProps) {
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.content}
>
-
+
+