Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sort mints & simplify send/receive code #225

Merged
merged 3 commits into from
Sep 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading