Skip to content

Commit

Permalink
Refactor Cashu UI (#163)
Browse files Browse the repository at this point in the history
* add no mint banner - cashu

* refactor cashu screen styles

* refactor cashu balance component styles

* refactor cashu tabs component styles

* refactor mints tab styles and add cashu context-provider

* fix texts style

* refactor cashu invoices and history styles
  • Loading branch information
lindsaymoralesb authored Oct 6, 2024
1 parent 8072f61 commit f535af0
Show file tree
Hide file tree
Showing 22 changed files with 762 additions and 473 deletions.
9 changes: 6 additions & 3 deletions apps/mobile/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {View} from 'react-native';
import {useTips} from '../hooks';
import {useDialog, useToast} from '../hooks/modals';
import {Router} from './Router';
import { CashuProvider } from '../providers/CashuProvider';

SplashScreen.preventAutoHideAsync();

Expand Down Expand Up @@ -92,8 +93,10 @@ export default function App() {
if (!appIsReady) return null;

return (
<View style={{flex: 1, flexDirection: 'row'}} onLayout={onLayoutRootView}>
<Router />
</View>
<CashuProvider>
<View style={{flex: 1, flexDirection: 'row'}} onLayout={onLayoutRootView}>
<Router />
</View>
</CashuProvider>
);
}
6 changes: 5 additions & 1 deletion apps/mobile/src/assets/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -826,4 +826,8 @@ export const PortfolioWallet: React.FC<SvgProps> = (props) => (
</Svg>
);


export const ScanQrIcon: React.FC<SvgProps> = (props) => (
<Svg fill="currentColor" viewBox="0 0 24 24" {...props}>
<Path d="M16.1666667,6 C16.0746192,6 16,6.07461921 16,6.16666667 L16,7.83333333 C16,7.92538079 16.0746192,8 16.1666667,8 L17.8333333,8 C17.9253808,8 18,7.92538079 18,7.83333333 L18,6.16666667 C18,6.07461921 17.9253808,6 17.8333333,6 L16.1666667,6 Z M16,18 L16,17.5 C16,17.2238576 16.2238576,17 16.5,17 C16.7761424,17 17,17.2238576 17,17.5 L17,18 L18,18 L18,17.5 C18,17.2238576 18.2238576,17 18.5,17 C18.7761424,17 19,17.2238576 19,17.5 L19,18.5 C19,18.7761424 18.7761424,19 18.5,19 L14.5,19 C14.2238576,19 14,18.7761424 14,18.5 L14,17.5 C14,17.2238576 14.2238576,17 14.5,17 C14.7761424,17 15,17.2238576 15,17.5 L15,18 L16,18 L16,18 Z M13,11 L13.5,11 C13.7761424,11 14,11.2238576 14,11.5 C14,11.7761424 13.7761424,12 13.5,12 L11.5,12 C11.2238576,12 11,11.7761424 11,11.5 C11,11.2238576 11.2238576,11 11.5,11 L12,11 L12,10 L10.5,10 C10.2238576,10 10,9.77614237 10,9.5 C10,9.22385763 10.2238576,9 10.5,9 L13.5,9 C13.7761424,9 14,9.22385763 14,9.5 C14,9.77614237 13.7761424,10 13.5,10 L13,10 L13,11 Z M18,12 L17.5,12 C17.2238576,12 17,11.7761424 17,11.5 C17,11.2238576 17.2238576,11 17.5,11 L18,11 L18,10.5 C18,10.2238576 18.2238576,10 18.5,10 C18.7761424,10 19,10.2238576 19,10.5 L19,12.5 C19,12.7761424 18.7761424,13 18.5,13 C18.2238576,13 18,12.7761424 18,12.5 L18,12 Z M13,14 L12.5,14 C12.2238576,14 12,13.7761424 12,13.5 C12,13.2238576 12.2238576,13 12.5,13 L13.5,13 C13.7761424,13 14,13.2238576 14,13.5 L14,15.5 C14,15.7761424 13.7761424,16 13.5,16 L10.5,16 C10.2238576,16 10,15.7761424 10,15.5 C10,15.2238576 10.2238576,15 10.5,15 L13,15 L13,14 L13,14 Z M16.1666667,5 L17.8333333,5 C18.4776655,5 19,5.52233446 19,6.16666667 L19,7.83333333 C19,8.47766554 18.4776655,9 17.8333333,9 L16.1666667,9 C15.5223345,9 15,8.47766554 15,7.83333333 L15,6.16666667 C15,5.52233446 15.5223345,5 16.1666667,5 Z M6.16666667,5 L7.83333333,5 C8.47766554,5 9,5.52233446 9,6.16666667 L9,7.83333333 C9,8.47766554 8.47766554,9 7.83333333,9 L6.16666667,9 C5.52233446,9 5,8.47766554 5,7.83333333 L5,6.16666667 C5,5.52233446 5.52233446,5 6.16666667,5 Z M6.16666667,6 C6.07461921,6 6,6.07461921 6,6.16666667 L6,7.83333333 C6,7.92538079 6.07461921,8 6.16666667,8 L7.83333333,8 C7.92538079,8 8,7.92538079 8,7.83333333 L8,6.16666667 C8,6.07461921 7.92538079,6 7.83333333,6 L6.16666667,6 Z M6.16666667,15 L7.83333333,15 C8.47766554,15 9,15.5223345 9,16.1666667 L9,17.8333333 C9,18.4776655 8.47766554,19 7.83333333,19 L6.16666667,19 C5.52233446,19 5,18.4776655 5,17.8333333 L5,16.1666667 C5,15.5223345 5.52233446,15 6.16666667,15 Z M6.16666667,16 C6.07461921,16 6,16.0746192 6,16.1666667 L6,17.8333333 C6,17.9253808 6.07461921,18 6.16666667,18 L7.83333333,18 C7.92538079,18 8,17.9253808 8,17.8333333 L8,16.1666667 C8,16.0746192 7.92538079,16 7.83333333,16 L6.16666667,16 Z M13,6 L10.5,6 C10.2238576,6 10,5.77614237 10,5.5 C10,5.22385763 10.2238576,5 10.5,5 L13.5,5 C13.7761424,5 14,5.22385763 14,5.5 L14,7.5 C14,7.77614237 13.7761424,8 13.5,8 C13.2238576,8 13,7.77614237 13,7.5 L13,6 Z M10.5,8 C10.2238576,8 10,7.77614237 10,7.5 C10,7.22385763 10.2238576,7 10.5,7 L11.5,7 C11.7761424,7 12,7.22385763 12,7.5 C12,7.77614237 11.7761424,8 11.5,8 L10.5,8 Z M5.5,14 C5.22385763,14 5,13.7761424 5,13.5 C5,13.2238576 5.22385763,13 5.5,13 L7.5,13 C7.77614237,13 8,13.2238576 8,13.5 C8,13.7761424 7.77614237,14 7.5,14 L5.5,14 Z M9.5,14 C9.22385763,14 9,13.7761424 9,13.5 C9,13.2238576 9.22385763,13 9.5,13 L10.5,13 C10.7761424,13 11,13.2238576 11,13.5 C11,13.7761424 10.7761424,14 10.5,14 L9.5,14 Z M11,18 L11,18.5 C11,18.7761424 10.7761424,19 10.5,19 C10.2238576,19 10,18.7761424 10,18.5 L10,17.5 C10,17.2238576 10.2238576,17 10.5,17 L12.5,17 C12.7761424,17 13,17.2238576 13,17.5 C13,17.7761424 12.7761424,18 12.5,18 L11,18 Z M9,11 L9.5,11 C9.77614237,11 10,11.2238576 10,11.5 C10,11.7761424 9.77614237,12 9.5,12 L8.5,12 C8.22385763,12 8,11.7761424 8,11.5 L8,11 L7.5,11 C7.22385763,11 7,10.7761424 7,10.5 C7,10.2238576 7.22385763,10 7.5,10 L8.5,10 C8.77614237,10 9,10.2238576 9,10.5 L9,11 Z M5,10.5 C5,10.2238576 5.22385763,10 5.5,10 C5.77614237,10 6,10.2238576 6,10.5 L6,11.5 C6,11.7761424 5.77614237,12 5.5,12 C5.22385763,12 5,11.7761424 5,11.5 L5,10.5 Z M15,10.5 C15,10.2238576 15.2238576,10 15.5,10 C15.7761424,10 16,10.2238576 16,10.5 L16,12.5 C16,12.7761424 15.7761424,13 15.5,13 C15.2238576,13 15,12.7761424 15,12.5 L15,10.5 Z M17,15 L17,14.5 C17,14.2238576 17.2238576,14 17.5,14 L18.5,14 C18.7761424,14 19,14.2238576 19,14.5 C19,14.7761424 18.7761424,15 18.5,15 L18,15 L18,15.5 C18,15.7761424 17.7761424,16 17.5,16 L15.5,16 C15.2238576,16 15,15.7761424 15,15.5 L15,14.5 C15,14.2238576 15.2238576,14 15.5,14 C15.7761424,14 16,14.2238576 16,14.5 L16,15 L17,15 Z M3,6.5 C3,6.77614237 2.77614237,7 2.5,7 C2.22385763,7 2,6.77614237 2,6.5 L2,4.5 C2,3.11928813 3.11928813,2 4.5,2 L6.5,2 C6.77614237,2 7,2.22385763 7,2.5 C7,2.77614237 6.77614237,3 6.5,3 L4.5,3 C3.67157288,3 3,3.67157288 3,4.5 L3,6.5 Z M17.5,3 C17.2238576,3 17,2.77614237 17,2.5 C17,2.22385763 17.2238576,2 17.5,2 L19.5,2 C20.8807119,2 22,3.11928813 22,4.5 L22,6.5 C22,6.77614237 21.7761424,7 21.5,7 C21.2238576,7 21,6.77614237 21,6.5 L21,4.5 C21,3.67157288 20.3284271,3 19.5,3 L17.5,3 Z M6.5,21 C6.77614237,21 7,21.2238576 7,21.5 C7,21.7761424 6.77614237,22 6.5,22 L4.5,22 C3.11928813,22 2,20.8807119 2,19.5 L2,17.5 C2,17.2238576 2.22385763,17 2.5,17 C2.77614237,17 3,17.2238576 3,17.5 L3,19.5 C3,20.3284271 3.67157288,21 4.5,21 L6.5,21 Z M21,17.5 C21,17.2238576 21.2238576,17 21.5,17 C21.7761424,17 22,17.2238576 22,17.5 L22,19.5 C22,20.8807119 20.8807119,22 19.5,22 L17.5,22 C17.2238576,22 17,21.7761424 17,21.5 C17,21.2238576 17.2238576,21 17.5,21 L19.5,21 C20.3284271,21 21,20.3284271 21,19.5 L21,17.5 Z"/>
</Svg>
);
13 changes: 10 additions & 3 deletions apps/mobile/src/components/TabSelector/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import {ScrollView, StyleSheet, View} from 'react-native';
import {ScrollView, StyleProp, StyleSheet, View, ViewStyle} from 'react-native';

import {Button} from '../Button';

Expand All @@ -8,12 +8,19 @@ interface ITabSelector {
activeTab: string | any;
buttons?: {tab: any | string; title?: string; screen?: string}[];
addScreenNavigation?: boolean;
containerStyle?: StyleProp<ViewStyle>;
tabStyle?: StyleProp<ViewStyle>;
activeTabStyle?: StyleProp<ViewStyle>;
}

const TabSelector: React.FC<ITabSelector> = ({
activeTab,
handleActiveTab,
buttons,
addScreenNavigation = true,
containerStyle,
tabStyle,
activeTabStyle
}) => {
const handlePress = (tab: string | any, screen?: string) => {
if (addScreenNavigation) {
Expand All @@ -28,13 +35,13 @@ const TabSelector: React.FC<ITabSelector> = ({
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.container}
contentContainerStyle={containerStyle ?? styles.container}
>
{buttons?.map((b, i) => {
return (
<Button
key={i}
style={[styles.tab, activeTab === b?.tab ? styles.active : null]}
style={[tabStyle ?? styles.tab, activeTab === b?.tab ? activeTabStyle ?? styles.active : null]}
onPress={() => handlePress(b?.tab, b?.screen)}
>
{b?.title}
Expand Down
65 changes: 10 additions & 55 deletions apps/mobile/src/modules/Cashu/BalanceCashu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { canUseBiometricAuthentication } from 'expo-secure-store';
import { retrieveAndDecryptCashuMnemonic, retrievePassword, storeCashuMnemonic } from '../../utils/storage';
import { SelectedTab, TABS_CASHU } from '../../types/tab';
import { useCashuBalance, useGetCashuWalletsInfo } from 'afk_nostr_sdk/src/hooks/cashu';
import { useCashuContext } from '../../providers/CashuProvider';


export const BalanceCashu = () => {
Expand All @@ -29,20 +30,17 @@ export const BalanceCashu = () => {
requestMintQuote,
generateMnemonic,
derivedSeedFromMnenomicAndSaved,
mintUrl,
setMintUrl,

activeMintIndex,
mintUrls
} = useCashuContext()!;

} = useCashu()
const { ndkCashuWallet, ndkWallet } = useNostrContext()

const {balance, setBalance, getProofsWalletAndBalance} = useCashuBalance()
const [mint, setMint] = useState<CashuMint | undefined>(mintUrl ? new CashuMint(mintUrl) : undefined)

const { isSeedCashuStorage, setIsSeedCashuStorage } = useCashuStore()
const styles = useStyles(stylesheet);
const [quote, setQuote] = useState<MintQuoteResponse | undefined>()
const [mintsUrls, setMintUrls] = useState<string[]>(["https://mint.minibits.cash/Bitcoin"])
const [hasSeedCashu, setHasSeedCashu] = useState(false);

const [isLoading, setIsLoading] = useState(false);
Expand Down Expand Up @@ -97,56 +95,13 @@ export const BalanceCashu = () => {
getProofsWalletAndBalance()
}, []);




return (
// <SafeAreaView style={styles.safeArea}>
<View
// contentContainerStyle={styles.scrollView}
>

<View style={styles.container}>

<View>

<Text
style={styles.text}
>Your balance</Text>

<Text
style={styles.text}
>{balance}</Text>
</View>


<View>
<Text
style={styles.text}>Connect to</Text>

</View>
<View style={styles.content}>
<TextInput
placeholder="Mint URL"
value={mintUrl}
onChangeText={setMintUrl}
style={styles.input}
/>

</View>

{/*
<View>
<Text>You have {lenWallet} ecash wallets</Text>
<Button
onPress={() => {
// handleGenerateWallet()
}}
>Generate wallet</Button>
</View>
*/}
</View>
<View style={styles.balanceContainer}>
<Text style={styles.balanceTitle}>Your balance</Text>
<Text style={styles.balance}>{balance}</Text>
<Text style={styles.activeMintText}>
Connected to: <b>{mintUrls?.[activeMintIndex]?.alias}</b>
</Text>
</View>
// </SafeAreaView>
);
};
11 changes: 5 additions & 6 deletions apps/mobile/src/modules/Cashu/GenerateInvoiceCashu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export const GenerateInvoiceCashu = () => {
derivedSeedFromMnenomicAndSaved,
getMintInfo, mint,
mintTokens,
mintUrl,
setMintUrl
activeMintIndex,
mintUrls

} = useCashu()

Expand All @@ -49,7 +49,6 @@ export const GenerateInvoiceCashu = () => {

const [quote, setQuote] = useState<MintQuoteResponse | undefined>()
const [infoMint, setMintInfo] = useState<GetInfoResponse | undefined>()
const [mintsUrls, setMintUrls] = useState<string[]>(["https://mint.minibits.cash/Bitcoin"])
const [isInvoiceModalVisible, setIsInvoiceModalVisible] = useState(false);
const [isZapModalVisible, setIsZapModalVisible] = useState(false);
const [hasSeedCashu, setHasSeedCashu] = useState(false);
Expand All @@ -76,7 +75,8 @@ export const GenerateInvoiceCashu = () => {

useEffect(() => {
(async () => {
if (!mintUrl) return;
if (activeMintIndex < 0) return;
const mintUrl = mintUrls?.[activeMintIndex]?.url;
const info = await getMintInfo(mintUrl)
setMintInfo(info)
})();
Expand Down Expand Up @@ -111,10 +111,9 @@ export const GenerateInvoiceCashu = () => {


const generateInvoice = async () => {
const mintUrl = mintUrls?.[activeMintIndex]?.url;
if (!mintUrl || !invoiceAmount) return;
try {


const cashuMint = await connectCashMint(mintUrl)
const wallet = await connectCashWallet(cashuMint?.mint)

Expand Down
135 changes: 65 additions & 70 deletions apps/mobile/src/modules/Cashu/HistoryTxCashu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@ import { useStyles, useTheme } from '../../hooks';
import { useDialog, useToast } from '../../hooks/modals';
import stylesheet from './styles';
import { CashuMint, getEncodedToken, MintQuoteResponse, MintQuoteState, Proof } from '@cashu/cashu-ts';
import { CopyIconStack } from '../../assets/icons';
import { CopyIconStack, InfoIcon } from '../../assets/icons';
import { canUseBiometricAuthentication } from 'expo-secure-store';
import { retrieveAndDecryptCashuMnemonic, retrievePassword, storeCashuMnemonic } from '../../utils/storage';
import { SelectedTab, TABS_CASHU } from '../../types/tab';
import { getInvoices, storeInvoices } from '../../utils/storage_cashu';
import { TypeToast } from '../../context/Toast/ToastContext';
import { useCashuContext } from '../../providers/CashuProvider';


export const HistoryTxCashu = () => {

const styles = useStyles(stylesheet);
const { theme } = useTheme();

const { wallet, connectCashMint,
connectCashWallet,
requestMintQuote,
Expand All @@ -35,23 +39,18 @@ export const HistoryTxCashu = () => {
checkMintQuote,
checkProofSpent,
receiveP2PK, mintTokens,
mint,
mintUrl
mint
} = useCashuContext()!;

} = useCashu()
const { ndkCashuWallet, ndkWallet } = useNostrContext()
const [txInvoices, setTxInvoices] = useState<ICashuInvoice[]>([])

// const [mintUrl, setMintUrl] = useState<string | undefined>("https://mint.minibits.cash/Bitcoin")
// const [mint, setMint] = useState<CashuMint | undefined>(mintUrl ? new CashuMint(mintUrl) : undefined)

const { isSeedCashuStorage, setIsSeedCashuStorage } = useCashuStore()
const [invoices, setInvoices] = useState<ICashuInvoice[] | undefined>([])

useEffect(() => {

const handleGetInvoices = async () => {
const invoicesLocal = await getInvoices()
const invoicesLocal = await getInvoices()
let invoicesIn: ICashuInvoice[] = []

if (invoicesLocal) {
Expand Down Expand Up @@ -99,12 +98,7 @@ export const HistoryTxCashu = () => {

}, []);


const styles = useStyles(stylesheet);


const [quote, setQuote] = useState<MintQuoteResponse | undefined>()
const [mintsUrls, setMintUrls] = useState<string[]>(["https://mint.minibits.cash/Bitcoin"])
const [isInvoiceModalVisible, setIsInvoiceModalVisible] = useState(false);
const [isZapModalVisible, setIsZapModalVisible] = useState(false);
const [hasSeedCashu, setHasSeedCashu] = useState(false);
Expand All @@ -115,7 +109,6 @@ export const HistoryTxCashu = () => {
const [connectionStatus, setConnectionStatus] = useState('disconnected');
const [connectionData, setConnectionData] = useState<any>(null);

const { theme } = useTheme();
const [newSeed, setNewSeed] = useState<string | undefined>()

const { showDialog, hideDialog } = useDialog()
Expand Down Expand Up @@ -273,60 +266,62 @@ export const HistoryTxCashu = () => {
};

return (
// <SafeAreaView style={styles.safeArea}>
<ScrollView contentContainerStyle={styles.scrollView}>
<View style={styles.container}>

<FlatList
ItemSeparatorComponent={() => <Divider></Divider>}
data={txInvoices?.flat().reverse()}
contentContainerStyle={styles.flatListContent}

keyExtractor={(item, i) => item?.bolt11 ?? i?.toString()}
renderItem={({ item }) => {
const date = item?.date && new Date(item?.date)?.toISOString()
return (<View style={styles.card}>
<View>

<Input
value={item?.bolt11}
editable={false}
right={
<TouchableOpacity
onPress={() => handleCopy(item?.bolt11)}
style={{
marginRight: 10,
}}
>
<CopyIconStack color={theme.colors.primary} />
</TouchableOpacity>
}
/>
<Text style={styles.text}>Amount: {item?.amount}</Text>
<Text style={styles.text}>Mint: {item?.mint}</Text>
<Text style={styles.text}>Status: {item?.state}</Text>
{date &&
<Text
style={styles.text}>Date: {date}</Text>}

</View>


<View>
<Button
onPress={() => handleVerify(item?.quote)}
>Verify</Button>

</View>

</View>)
}}
/>



</View>
</ScrollView>
// </SafeAreaView >
<View style={styles.tabContentContainer}>
<Text style={styles.tabTitle}>Cashu History</Text>
{
txInvoices?.length > 0 ? (
<FlatList
ItemSeparatorComponent={() => <Divider></Divider>}
data={txInvoices?.flat().reverse()}
contentContainerStyle={styles.flatListContent}

keyExtractor={(item, i) => item?.bolt11 ?? i?.toString()}
renderItem={({ item }) => {
const date = item?.date && new Date(item?.date)?.toISOString()
return (<View style={styles.card}>
<View>

<Input
value={item?.bolt11}
editable={false}
right={
<TouchableOpacity
onPress={() => handleCopy(item?.bolt11)}
style={{
marginRight: 10,
}}
>
<CopyIconStack color={theme.colors.primary} />
</TouchableOpacity>
}
/>
<Text style={styles.text}>Amount: {item?.amount}</Text>
<Text style={styles.text}>Mint: {item?.mint}</Text>
<Text style={styles.text}>Status: {item?.state}</Text>
{date &&
<Text
style={styles.text}>Date: {date}</Text>}

</View>


<View>
<Button
onPress={() => handleVerify(item?.quote)}
>Verify</Button>

</View>

</View>)
}}
/>
) : (
<View style={styles.noDataContainer}>
<InfoIcon width={30} height={30} color={theme.colors.primary} />
<Text style={styles.noDataText}>No history data found.</Text>
</View>
)
}
</View>
);
};
Loading

0 comments on commit f535af0

Please sign in to comment.