Skip to content

Commit

Permalink
Sort mints & simplify send/receive code (#225)
Browse files Browse the repository at this point in the history
* sort mints #185

* update

* simplify send/receive function
  • Loading branch information
KKA11010 authored Sep 16, 2023
1 parent b63e8a7 commit b93bacf
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 106 deletions.
7 changes: 4 additions & 3 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useThemeContext } from '@src/context/Theme'
import { globals, highlight as hi, mainColors } from '@styles'
import { getColor } from '@styles/colors'
import { SafeAreaView, type StyleProp, StyleSheet, Text, type TextStyle, TouchableOpacity } from 'react-native'

import Loading from './Loading'
Expand All @@ -17,7 +18,7 @@ interface IButtonProps {
}

export default function Button({ txt, onPress, border, outlined, filled, disabled, loading, icon }: IButtonProps) {
const { highlight } = useThemeContext()
const { color, highlight } = useThemeContext()
return (
<SafeAreaView style={styles.safeArea}>
<TouchableOpacity
Expand All @@ -36,12 +37,13 @@ export default function Button({ txt, onPress, border, outlined, filled, disable
>
<Text style={[
styles.btnTxt,
{ color: getColor(highlight, color) },
filled || outlined ? { color: hi[highlight] } : {},
loading || icon ? { marginRight: 10 } : {}
]}>
{txt}
</Text>
{loading && <Loading color={mainColors.WHITE} />}
{loading && <Loading color={getColor(highlight, color)} />}
{!loading ? icon : null}
</TouchableOpacity>
</SafeAreaView>
Expand Down Expand Up @@ -122,7 +124,6 @@ const styles = StyleSheet.create({
borderRadius: 50,
},
btnTxt: {
color: mainColors.WHITE,
textAlign: 'center',
fontSize: 16,
fontWeight: '500'
Expand Down
59 changes: 31 additions & 28 deletions src/screens/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,54 +180,57 @@ export default function Dashboard({ navigation, route }: TDashboardPageProps) {
await handleTokenSubmit(clipboard)
}

// Options modal buttons -> mint (RECEIVE OPTION) - melt/send ecash (SEND OPTION)
const handleOptsBtnPress = async ({ isMelt, isSendEcash, isMinting }: { isMelt?: boolean, isSendEcash?: boolean, isMinting?: boolean }) => {
// mint new token
const handleMintBtnPress = async () => {
const { mintsBals, mints } = await getMintsForPayment()
closeOptsModal()
// user has only 1 mint so he can skip selectMint screen
if (mints.length === 1 && mintsBals.length === 1) {
navigation.navigate('selectAmount', {
mint: mints[0],
balance: mintsBals[0].amount
})
return
}
// user has more than 1 mint so he has to choose the one he wants to communicate to
navigation.navigate('selectMint', {
mints,
mintsWithBal: mintsBals,
})
}

// send token or melt ecash
const handleSendBtnPress = async ({ isMelt, isSendEcash }: { isMelt?: boolean, isSendEcash?: boolean }) => {
const { mintsBals, mints } = await getMintsForPayment()
closeOptsModal()
// user has only 1 mint with balance, he can skip the mint selection only for melting (he can mint new token with a mint that has no balance)
const nonEmptyMints = mintsBals.filter(m => m.amount > 0)
if ((isMelt || isSendEcash || isMinting) && nonEmptyMints.length === 1) {
// (RECEIVE OPTION) user wants to mint new token with his single mint so he can skip selectMint screen
if (isMinting) {
navigation.navigate('selectAmount', {
mint: mints.find(m => m.mintUrl === nonEmptyMints[0].mintUrl) || { mintUrl: 'N/A', customName: 'N/A' },
balance: nonEmptyMints[0].amount
})
return
}
// (SEND OPTION) user has no nostr contacts so he can directly navigate to amount selection
// user has only 1 mint with balance, he can skip the mint selection
if (nonEmptyMints.length === 1) {
// user has no nostr contacts so he can directly navigate to amount selection
if (!nutPub.length && isSendEcash) {
navigation.navigate('selectAmount', {
mint: mints.find(m => m.mintUrl === nonEmptyMints[0].mintUrl) || { mintUrl: 'N/A', customName: 'N/A' },
balance: nonEmptyMints[0].amount,
isSendEcash,
balance: nonEmptyMints[0].amount
})
return
}
// (SEND OPTION) otherwise he can select his contacts as target, get remaining mints for a possible multimint swap
// otherwise he can select his contacts as target, get remaining mints for a possible multimint swap
const remainingMints = mints.filter(m => m.mintUrl !== _testmintUrl)
navigation.navigate('selectTarget', {
mint: mints.find(m => m.mintUrl === nonEmptyMints[0].mintUrl) || { mintUrl: 'N/A', customName: 'N/A' },
balance: nonEmptyMints[0].amount,
isMelt,
isSendEcash,
balance: nonEmptyMints[0].amount,
remainingMints
})
return
}
// user has only 1 mint and no balance in it
if (mints.length === 1 && !nonEmptyMints.length) {
navigation.navigate('selectAmount', {
mint: mints[0],
balance: mintsBals[0].amount
})
return
}
// user has more than 1 mint so he has to choose the one he wants to communicate to
navigation.navigate('selectMint', {
mints,
mintsWithBal: mintsBals,
allMintsEmpty: (isMelt || isSendEcash) && !nonEmptyMints.length,
allMintsEmpty: !nonEmptyMints.length,
isMelt,
isSendEcash
})
Expand Down Expand Up @@ -365,9 +368,9 @@ export default function Dashboard({ navigation, route }: TDashboardPageProps) {
<OptsModal
visible={modal.sendOpts}
button1Txt={t('sendEcash')}
onPressFirstBtn={() => void handleOptsBtnPress({ isSendEcash: true })}
onPressFirstBtn={() => void handleSendBtnPress({ isSendEcash: true })}
button2Txt={t('payLNInvoice', { ns: NS.wallet })}
onPressSecondBtn={() => void handleOptsBtnPress({ isMelt: true })}
onPressSecondBtn={() => void handleSendBtnPress({ isMelt: true })}
onPressCancel={closeOptsModal}
isSend
/>
Expand All @@ -377,7 +380,7 @@ export default function Dashboard({ navigation, route }: TDashboardPageProps) {
button1Txt={loading ? t('claiming', { ns: NS.wallet }) : t('pasteToken', { ns: NS.wallet })}
onPressFirstBtn={() => void handleClaimBtnPress()}
button2Txt={t('createLnInvoice', { ns: NS.wallet })}
onPressSecondBtn={() => void handleOptsBtnPress({ isMinting: true })}
onPressSecondBtn={() => void handleMintBtnPress()}
handleNostrReceive={() => {
closeOptsModal()
navigation.navigate('nostrReceive')
Expand Down
107 changes: 60 additions & 47 deletions src/screens/Mints/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { useThemeContext } from '@src/context/Theme'
import { NS } from '@src/i18n'
import { getCustomMintNames, getDefaultMint } from '@store/mintStore'
import { globals, highlight as hi, mainColors } from '@styles'
import { getColor } from '@styles/colors'
import { formatInt, formatMintUrl, getStrFromClipboard, isErr, isUrl } from '@util'
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
Expand Down Expand Up @@ -181,50 +182,62 @@ export default function Mints({ navigation, route }: TMintsPageProps) {
<View style={[styles.topSection, { marginBottom: 75 + insets.bottom }]}>
{/* Mints list where test mint is always visible */}
<ScrollView style={[globals(color).wrapContainer]}>
{usertMints.reverse().map((m, i) => (
<View key={m.mintUrl}>
<TouchableOpacity
style={styles.mintUrlWrap}
onPress={() => {
const remainingMints = usertMints.filter(mint => mint.mintUrl !== m.mintUrl && mint.mintUrl !== _testmintUrl)
handleMintEntry(m, m.amount, remainingMints)
}}
>
<View style={styles.mintNameWrap}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
{defaultMint === m.mintUrl &&
<MintBoardIcon width={18} height={18} color={hi[highlight]} />
{usertMints
.sort((a, b) => {
if (a.mintUrl === defaultMint) { return -1 }
if (b.mintUrl === defaultMint) { return 1 }
return 0
})
.map((m, i) => (
<View key={m.mintUrl}>
<TouchableOpacity
style={styles.mintUrlWrap}
onPress={() => {
const remainingMints = usertMints.filter(mint => mint.mintUrl !== m.mintUrl && mint.mintUrl !== _testmintUrl)
handleMintEntry(m, m.amount, remainingMints)
}}
>
<View style={styles.mintNameWrap}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
{defaultMint === m.mintUrl &&
<MintBoardIcon width={18} height={18} color={hi[highlight]} />
}
<Txt
txt={m.customName || formatMintUrl(m.mintUrl)}
styles={[{ marginLeft: defaultMint === m.mintUrl ? 10 : 0, fontWeight: '500' }]}
/>
</View>
{isTrustedMint(m.mintUrl) &&
<View style={styles.mintBal}>
{m.amount > 0 && <ZapIcon color={hi[highlight]} />}
<Text
style={{
color: m.amount > 0 ? color.TEXT : color.TEXT_SECONDARY,
marginLeft: m.amount > 0 ? 5 : 0,
marginBottom: 5
}}
>
{m.amount > 0 ?
formatInt(m.amount, 'compact', 'en') + ' Satoshi'
:
t('emptyMint')
}
</Text>
</View>
}
<Txt
txt={m.customName || formatMintUrl(m.mintUrl)}
styles={[{ marginLeft: defaultMint === m.mintUrl ? 10 : 0, fontWeight: '500' }]}
/>
</View>
{isTrustedMint(m.mintUrl) &&
<View style={styles.mintBal}>
<ZapIcon color={m.amount > 0 ? hi[highlight] : color.TEXT_SECONDARY} />
<Text style={[styles.mintAmount, { color: m.amount > 0 ? color.TEXT : color.TEXT_SECONDARY, marginBottom: 5 }]}>
{m.amount > 0 ?
formatInt(m.amount, 'compact', 'en') + ' Satoshi'
:
t('emptyMint')
}
</Text>
</View>
}
</View>
{/* Add mint icon or show balance */}
<View>
{isTrustedMint(m.mintUrl) ?
<ChevronRightIcon color={color.TEXT} />
:
<PlusIcon color={color.TEXT} />
}
</View>
</TouchableOpacity>
{i < usertMints.length - 1 && <Separator />}
</View>
))}
{/* Add mint icon or show balance */}
<View>
{isTrustedMint(m.mintUrl) ?
<ChevronRightIcon color={color.TEXT} />
:
<PlusIcon color={color.TEXT} />
}
</View>
</TouchableOpacity>
{i < usertMints.length - 1 && <Separator />}
</View>
))}
</ScrollView>
</View>
:
Expand Down Expand Up @@ -291,7 +304,10 @@ export default function Mints({ navigation, route }: TMintsPageProps) {
})
}}
bottomBtnTxt={t('willDoLater')}
bottomBtnAction={() => setTopUpModal(false)}
bottomBtnAction={() => {
setTopUpModal(false)
navigation.navigate('dashboard')
}}
/>
</MyModal>
<QuestionModal
Expand All @@ -308,7 +324,7 @@ export default function Mints({ navigation, route }: TMintsPageProps) {
{usertMints.length > 0 &&
<View style={[styles.newMint, { marginBottom: insets.bottom }]}>
<IconBtn
icon={<PlusIcon width={28} height={28} color={mainColors.WHITE} />}
icon={<PlusIcon width={28} height={28} color={getColor(highlight, color)} />}
onPress={() => {
closePrompt()
setNewMintModal(true)
Expand Down Expand Up @@ -350,9 +366,6 @@ const styles = StyleSheet.create({
alignItems: 'center',
marginTop: 10,
},
mintAmount: {
marginLeft: 5,
},
cancel: {
alignItems: 'center',
marginTop: 15,
Expand Down
3 changes: 2 additions & 1 deletion src/screens/Payment/Receive/Invoice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { NS } from '@src/i18n'
import { getBalance } from '@src/storage/db'
import { addToHistory } from '@store/latestHistoryEntries'
import { globals, highlight as hi, mainColors } from '@styles'
import { getColor } from '@styles/colors'
import { formatMintUrl, formatSeconds, isErr, openUrl, share } from '@util'
import { requestToken } from '@wallet'
import { useEffect, useState } from 'react'
Expand Down Expand Up @@ -141,7 +142,7 @@ export default function InvoiceScreen({ navigation, route }: TMintInvoicePagePro
void openUrl(`lightning:${paymentRequest}`)?.catch(e =>
openPromptAutoClose({ msg: isErr(e) ? e.message : t('deepLinkErr') }))
}}
icon={<WalletIcon color={mainColors.WHITE} />}
icon={<WalletIcon color={getColor(highlight, color)} />}
/>
<TxtButton
txt={t('shareInvoice')}
Expand Down
65 changes: 39 additions & 26 deletions src/screens/Payment/SelectMint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,31 +137,37 @@ export default function SelectMintScreen({ navigation, route }: TSelectMintPageP
{userMints.length && !allMintsEmpty ?
<ScrollView>
<View style={globals(color).wrapContainer}>
{userMints.reverse().map((m, i) => (
<View key={m.mintUrl}>
<TouchableOpacity
style={styles.mintUrlWrap}
onPress={() => void handlePressMint(m)}
>
<View style={styles.mintNameWrap}>
{defaultMint === m.mintUrl &&
<MintBoardIcon width={18} height={18} color={hi[highlight]} />
}
<Txt
txt={m.customName || formatMintUrl(m.mintUrl)}
styles={[{ marginLeft: defaultMint === m.mintUrl ? 10 : 0 }]}
/>
</View>
<View style={styles.mintBal}>
<Text style={[styles.mintAmount, { color: color.TEXT, paddingBottom: 3 }]}>
{formatInt(m.amount, 'compact', 'en')}
</Text>
<ZapIcon color={hi[highlight]} />
</View>
</TouchableOpacity>
{i < userMints.length - 1 && <Separator />}
</View>
))}
{userMints
.sort((a, b) => {
if (a.mintUrl === defaultMint) { return -1 }
if (b.mintUrl === defaultMint) { return 1 }
return 0
})
.map((m, i) => (
<View key={m.mintUrl}>
<TouchableOpacity
style={styles.mintUrlWrap}
onPress={() => void handlePressMint(m)}
>
<View style={styles.mintNameWrap}>
{defaultMint === m.mintUrl &&
<MintBoardIcon width={18} height={18} color={hi[highlight]} />
}
<Txt
txt={m.customName || formatMintUrl(m.mintUrl)}
styles={[{ marginLeft: defaultMint === m.mintUrl ? 10 : 0 }]}
/>
</View>
<View style={styles.mintBal}>
<Text style={[styles.mintAmount, { color: color.TEXT, paddingBottom: 3 }]}>
{formatInt(m.amount, 'compact', 'en')}
</Text>
<ZapIcon color={m.amount > 0 ? hi[highlight] : color.TEXT} />
</View>
</TouchableOpacity>
{i < userMints.length - 1 && <Separator />}
</View>
))}
</View>
</ScrollView>
:
Expand All @@ -172,7 +178,14 @@ export default function SelectMintScreen({ navigation, route }: TSelectMintPageP
<Button
txt={t(allMintsEmpty ? 'mintNewTokens' : 'addNewMint', { ns: NS.mints })}
onPress={() => {
if (allMintsEmpty) {
if (allMintsEmpty && userMints.length === 1) {
navigation.navigate('selectAmount', {
mint: userMints[0],
balance: userMints[0].amount,
})
return
}
if (allMintsEmpty && userMints.length > 1) {
navigation.navigate('selectMint', {
mints,
mintsWithBal
Expand Down
3 changes: 2 additions & 1 deletion src/screens/Payment/Send/EncodedToken.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { NS } from '@src/i18n'
import { store } from '@store'
import { STORE_KEYS } from '@store/consts'
import { globals, highlight as hi, mainColors } from '@styles'
import { getColor } from '@styles/colors'
import { share, vib } from '@util'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
Expand Down Expand Up @@ -66,7 +67,7 @@ export default function EncodedTokenPage({ navigation, route }: TEncodedTokenPag
{/* Action buttons */}
<ActionButtons
topBtnTxt={t('share')}
topIcon={<ShareIcon width={20} height={20} color={mainColors.WHITE} />}
topIcon={<ShareIcon width={20} height={20} color={getColor(highlight, color)} />}
topBtnAction={() => void share(route.params.token, `cashu://${route.params.token}`)}
bottomBtnTxt={copied ? t('copied') + '!' : t('copyToken')}
bottomIcon={<CopyIcon color={hi[highlight]} />}
Expand Down

0 comments on commit b93bacf

Please sign in to comment.