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

Pending invoices #339

Merged
merged 18 commits into from
Apr 7, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 6 additions & 2 deletions assets/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"back": "Zurück",
"backToDashboard": "Zurück zu Wallet",
"balance": "Guthaben",
"balanceAfterTX": "Guthaben nach Zahlung",
"balanceAfterTX": "Mint Guthaben nach Zahlung",
"balTooLow": "Nicht genug Guthaben",
"bigQrMsg": "Die Datenmenge ist zu groß für einen QR-Code.",
"calculateFeeEst": "Gebühr wird geschätzt",
Expand Down Expand Up @@ -250,7 +250,11 @@
"swapHint": "Diese Option erfordert eine Lightning-Zahlung, bringt Gebühren mit sich und kann die unbekannte Mint dennoch zur Liste hinzufügen, wenn eine Gebühren-Rückzahlung erfolgt.",
"trustHint": "Die mit dem Token verbundene Mint wird zu Ihrer Vertrauensliste hinzugefügt.",
"noDefaultHint": "Sie müssen eine Standard-Mint einrichten, um einen automatischen Tausch durchzuführen.",
"autoSwapSuccess": "Tausch erfolgreich!"
"autoSwapSuccess": "Tausch erfolgreich!",
"paidInvoice": "{{ count }} Rechnung wurde mit einem Gesamtbetrag von {{ total }} Sats bezahlt",
"paidInvoices": "{{ count }} Rechnungen wurden mit einem Gesamtbetrag von {{ total }} Sats bezahlt",
"checkPayment": "Zahlung überprüfen",
"lnPaymentSpamHint": "Bitte warten Sie {{remainingSeconds}} Sekunden um die Mint zu entlasten."
},
"error": {
"checkSpendableErr": "Fehler beim Überprüfen, ob der Token ausgegeben werden kann",
Expand Down
8 changes: 6 additions & 2 deletions assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"back": "Back",
"backToDashboard": "Back to dashboard",
"balance": "Balance",
"balanceAfterTX": "Balance after TX",
"balanceAfterTX": "Mint balance after TX",
"balTooLow": "Balance too low",
"bigQrMsg": "The amount of data is too big for a QR code.",
"calculateFeeEst": "Calculating fee",
Expand Down Expand Up @@ -250,7 +250,11 @@
"swapHint": "This option requires a Lightning payment, involves fees, and may still add the unknown mint to the list if a fee refund occurs.",
"trustHint": "The mint associated with the token will be added to your trusted list.",
"noDefaultHint": "You need to setup a default mint to perform an auto swap.",
"autoSwapSuccess": "Swap successful!"
"autoSwapSuccess": "Swap successful!",
"paidInvoice": "{{ count }} invoice has been paid with a total amount of {{ total }} Sats",
"paidInvoices": "{{ count }} invoices have been paid with a total amount of {{ total }} Sats",
"checkPayment": "Check payment",
"lnPaymentSpamHint": "Please wait {{remainingSeconds}} seconds to avoid spamming the mint."
},
"error": {
"checkSpendableErr": "Error while checking if token is spendable",
Expand Down
8 changes: 6 additions & 2 deletions assets/translations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"back": "Volver",
"backToDashboard": "Volver al panel de control",
"balance": "Saldo",
"balanceAfterTX": "Saldo tras la transacción",
"balanceAfterTX": "Mint saldo tras la transacción",
"balTooLow": "Saldo insuficiente",
"bigQrMsg": "La cantidad de datos es demasiado grande para un código QR.",
"calculateFeeEst": "Calculando comisión",
Expand Down Expand Up @@ -250,7 +250,11 @@
"swapHint": "Esta opción requiere un pago Lightning, implica una tarifa y aún puede agregar la ceca desconocida a la lista si se produce un reembolso de tarifa.",
"trustHint": "La ceca asociada al token se añadirá a tu lista de confianza.",
"noDefaultHint": "Necesitas configurar una ceca predeterminada para realizar un intercambio automático.",
"autoSwapSuccess": "¡Intercambio exitoso!"
"autoSwapSuccess": "¡Intercambio exitoso!",
"paidInvoice": "Se ha pagado {{ count }} factura con un importe total de {{ total }} Sats",
"paidInvoices": "Se han pagado {{ count }} facturas con un importe total de {{ total }} Sats",
"checkPayment": "Comprobar pago",
"lnPaymentSpamHint": "Por favor, espere {{remainingSeconds}} segundos para aliviar la Mint."
},
"error": {
"checkSpendableErr": "Error al comprobar si el token puede ser gastado",
Expand Down
8 changes: 6 additions & 2 deletions assets/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"back": "Retour",
"backToDashboard": "Retour au tableau de bord",
"balance": "Solde",
"balanceAfterTX": "Solde après paiement",
"balanceAfterTX": "Mint solde après paiement",
"balTooLow": "Solde insuffisant",
"bigQrMsg": "La quantité de données est trop grand pour un code QR.",
"calculateFeeEst": "Calcul des frais",
Expand Down Expand Up @@ -250,7 +250,11 @@
"swapHint": "Ce choix nécessite un paiement Lightning et peut entraîner des frais associés.",
"trustHint": "La menthe associée au jeton sera ajoutée à votre liste de confiance.",
"noDefaultHint": "Vous devez configurer une mint par défaut pour effectuer un échange automatique.",
"autoSwapSuccess": "Échange réussi!"
"autoSwapSuccess": "Échange réussi!",
"paidInvoice": "{{ count }} facture a été payée pour un montant total de {{ total }} Sats",
"paidInvoices": "{{ count }} factures ont été payées pour un montant total de {{ total }} Sats",
"checkPayment": "Vérifier le paiement",
"lnPaymentSpamHint": "Veuillez attendre {{remainingSeconds}} secondes pour soulager la Mint."
},
"error": {
"checkSpendableErr": "Erreur lors de la vérification si le token est dépensable",
Expand Down
8 changes: 6 additions & 2 deletions assets/translations/hu.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"back": "Vissza",
"backToDashboard": "Vissza a kezdőképernyőre",
"balance": "Egyenleg",
"balanceAfterTX": "Utalás utáni egyenleg",
"balanceAfterTX": "Verde utalás utáni egyenleg",
"balTooLow": "Egyenleg túl alacsony",
"bigQrMsg": "Az adatmennyiség túl nagy egy QR kód számára.",
"calculateFeeEst": "Díjszámítás",
Expand Down Expand Up @@ -250,7 +250,11 @@
"swapHint": "Ez az opció egy Lightning-fizetést igényel ami költségekkel jár, és még akkor is hozzáadhatja az ismeretlen verdét a listádhoz, ha költségvisszatérítésre kerül sor.",
"trustHint": "A tokenhez kapcsolódó verde hozzá lesz adva a megbízott listához.",
"noDefaultHint": "Be kell állítanod egy alapértelmezett verdét az automatikus cseréhez.",
"autoSwapSuccess": "Csere sikeres!"
"autoSwapSuccess": "Csere sikeres!",
"paidInvoice": "{{ count }} számla kifizetésre került, összesen {{ total }} Sats összegben",
"paidInvoices": "{{ count }} számla kifizetésre került, összesen {{ total }} Sats összegben",
"checkPayment": "Fizetés ellenőrzése",
"lnPaymentSpamHint": "Kérjük, várjon {{remainingSeconds}} másodpercet a Mint tehermentesítése érdekében."
},
"error": {
"checkSpendableErr": "Hiba a token elkölthetőségének ellenőrzése közben",
Expand Down
8 changes: 6 additions & 2 deletions assets/translations/sw.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"back": "Rudi",
"backToDashboard": "Rudi kwenye dashibodi",
"balance": "Salio",
"balanceAfterTX": "Salio baada ya TX",
"balanceAfterTX": "Mint salio baada ya TX",
"balTooLow": "Salio ni dogo mno",
"bigQrMsg": "Kiasi cha data ni kikubwa sana kwa nambari ya QR.",
"calculateFeeEst": "Kuhesabu ada",
Expand Down Expand Up @@ -250,7 +250,11 @@
"swapHint": "Chaguo hili linahitaji malipo ya Lightning, linajumuisha ada, na linaweza bado kuongeza mint isiyojulikana kwenye orodha ikiwa kuna marejesho ya ada.",
"trustHint": "Minti inayohusiana na alama itaongezwa kwenye orodha yako ya kuaminika.",
"noDefaultHint": "Unahitaji kuweka kalibu ya kufanya ubadilishaji wa moja kwa moja.",
"autoSwapSuccess": "Kubadilishana kufanikiwa!"
"autoSwapSuccess": "Kubadilishana kufanikiwa!",
"paidInvoice": "{{ count }} ankara imelipwa kwa jumla ya {{ total }} Sats",
"paidInvoices": "Bilansi ya ankara {{ count }} zimelipwa kwa jumla ya {{ total }} Sats",
"checkPayment": "Angalia malipo",
"lnPaymentSpamHint": "Tafadhali subiri {{remainingSeconds}} sekunde ili kupunguza mzigo kwa Mint."
},
"error": {
"checkSpendableErr": "Kumetokea kosa wakati wa kuangalia ikiwa kijenzi kina pesa za kutumiwa",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@
"blind-signatures",
"lightning-network"
],
"version": "0.3.1",
"version": "0.4.0",
"license": "AGPL-3.0-only",
"bugs": {
"url": "https://github.com/cashubtc/eNuts/issues"
Expand Down
42 changes: 24 additions & 18 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import { NavigationContainer, NavigationContainerRef } from '@react-navigation/n
import { CustomErrorBoundary } from '@screens/ErrorScreen/ErrorBoundary'
import { ErrorDetails } from '@screens/ErrorScreen/ErrorDetails'
import * as Sentry from '@sentry/react-native'
import { BalanceProvider } from '@src/context/Balance'
import { FocusClaimProvider } from '@src/context/FocusClaim'
import { HistoryProvider } from '@src/context/History'
import { KeyboardProvider } from '@src/context/Keyboard'
import { NostrProvider } from '@src/context/Nostr'
import { PinCtx } from '@src/context/Pin'
Expand Down Expand Up @@ -48,7 +50,7 @@ interface ILockData {
l('[APP] Starting app...')

void SplashScreen.preventAutoHideAsync()
function App(_: { exp: Record<string, unknown>} ) {
function App(_: { exp: Record<string, unknown> }) {
if (!env?.SENTRY_DSN) {
return (
<SafeAreaProvider>
Expand Down Expand Up @@ -206,23 +208,27 @@ function _App() {
<MenuProvider>
<NostrProvider>
<NavContainer>
<FocusClaimProvider >
<PromptProvider>
<KeyboardProvider>
<Navigator
shouldOnboard={shouldOnboard}
pinHash={auth.pinHash}
bgAuth={bgAuth}
setBgAuth={setBgAuth}
hasSeed={hasSeed}
sawSeedUpdate={sawSeedUpdate}
/>
<StatusBar style="auto" />
<ClipboardModal />
<Toaster />
</KeyboardProvider>
</PromptProvider>
</FocusClaimProvider>
<BalanceProvider>
<FocusClaimProvider >
<PromptProvider>
<HistoryProvider>
<KeyboardProvider>
<Navigator
shouldOnboard={shouldOnboard}
pinHash={auth.pinHash}
bgAuth={bgAuth}
setBgAuth={setBgAuth}
hasSeed={hasSeed}
sawSeedUpdate={sawSeedUpdate}
/>
<StatusBar style="auto" />
<ClipboardModal />
<Toaster />
</KeyboardProvider>
</HistoryProvider>
</PromptProvider>
</FocusClaimProvider>
</BalanceProvider>
</NavContainer>
</NostrProvider>
</MenuProvider>
Expand Down
81 changes: 37 additions & 44 deletions src/components/Balance.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { CheckmarkIcon, EcashIcon, SwapCurrencyIcon, ZapIcon } from '@comps/Icons'
import { CheckmarkIcon, ClockIcon, CloseCircleIcon, EcashIcon, SwapCurrencyIcon, ZapIcon } from '@comps/Icons'
import { setPreferences } from '@db'
import { type IHistoryEntry, TTXType,txType } from '@model'
import { type TTXType, txType } from '@model'
import type { RootStackParamList } from '@model/nav'
import type { NativeStackNavigationProp } from '@react-navigation/native-stack'
import EntryTime from '@screens/History/entryTime'
import { useFocusClaimContext } from '@src/context/FocusClaim'
import { useBalanceContext } from '@src/context/Balance'
import { useHistoryContext } from '@src/context/History'
import { usePrivacyContext } from '@src/context/Privacy'
import { useThemeContext } from '@src/context/Theme'
import { NS } from '@src/i18n'
import { getLatestHistory } from '@store/latestHistoryEntries'
import { globals, highlight as hi } from '@styles'
import { getColor } from '@styles/colors'
import { formatBalance, formatInt, formatSatStr, isBool } from '@util'
import { useEffect, useState } from 'react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Text, TouchableOpacity, View } from 'react-native'
import { s, ScaledSheet } from 'react-native-size-matters'
Expand All @@ -22,23 +22,16 @@ import Logo from './Logo'
import Txt from './Txt'

interface IBalanceProps {
balance: number
nav?: NativeStackNavigationProp<RootStackParamList, 'dashboard', 'MyStack'>
}

export default function Balance({ balance, nav }: IBalanceProps) {
export default function Balance({ nav }: IBalanceProps) {
const { t } = useTranslation([NS.common])
const { pref, color, highlight } = useThemeContext()
// State to indicate token claim from clipboard after app comes to the foreground, to re-render total balance
const { claimed } = useFocusClaimContext()
const { hidden, handleLogoPress } = usePrivacyContext()
const [formatSats, setFormatSats] = useState(pref?.formatBalance)
const [history, setHistory] = useState<IHistoryEntry[]>([])

const setHistoryEntries = async () => {
const stored = (await getLatestHistory()).reverse()
setHistory(stored)
}
const { balance } = useBalanceContext()
const { latestHistory } = useHistoryContext()

const toggleBalanceFormat = () => {
setFormatSats(prev => !prev)
Expand All @@ -54,23 +47,6 @@ export default function Balance({ balance, nav }: IBalanceProps) {
return t('seedBackup')
}

useEffect(() => {
void setHistoryEntries()
}, [])

// get history after navigating to this page
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
const focusHandler = nav?.addListener('focus', async () => {
await setHistoryEntries()
})
return focusHandler
}, [nav])

useEffect(() => {
void setHistoryEntries()
}, [claimed])

return (
<View style={[
styles.board,
Expand Down Expand Up @@ -100,33 +76,41 @@ export default function Balance({ balance, nav }: IBalanceProps) {
</TouchableOpacity>
}
{/* No transactions yet */}
{!history.length &&
{!latestHistory.length &&
<View style={styles.txOverview}>
<Txt txt={t('noTX')} styles={[globals(color).pressTxt, { color: getColor(highlight, color) }]} />
</View>
}
{/* latest 3 history entries */}
{history.length > 0 && !hidden.txs &&
history.map(h => (
{latestHistory.length > 0 && !hidden.txs &&
latestHistory.map(h => (
<HistoryEntry
key={h.timestamp}
icon={h.type === txType.LIGHTNING || h.type === txType.SWAP ?
<ZapIcon width={s(28)} height={s(28)} color={getColor(highlight, color)} />
:
h.type === txType.RESTORE ?
<CheckmarkIcon color={getColor(highlight, color)} />
icon={
h.isPending && !h.isExpired ?
<ClockIcon color={getColor(highlight, color)} />
:
<EcashIcon color={getColor(highlight, color)} />
h.isExpired ?
<CloseCircleIcon width={s(21)} height={s(21)} color={getColor(highlight, color)} />
:
h.type === txType.RESTORE ?
<CheckmarkIcon color={getColor(highlight, color)} />
:
h.type === txType.LIGHTNING || h.type === txType.SWAP ?
<ZapIcon width={s(28)} height={s(28)} color={getColor(highlight, color)} />
:
<EcashIcon color={getColor(highlight, color)} />
}
isSwap={h.type === txType.SWAP}
txType={getTxTypeStr(h.type)}
timestamp={h.timestamp}
amount={h.amount}
isExpired={h.isExpired}
onPress={() => nav?.navigate('history entry details', { entry: h })}
/>
))
}
{(history.length === 3 || (history.length > 0 && hidden.txs)) &&
{(latestHistory.length === 3 || (latestHistory.length > 0 && hidden.txs)) &&
<TxtButton
txt={t('seeFullHistory')}
onPress={() => nav?.navigate('history')}
Expand All @@ -144,10 +128,11 @@ interface IHistoryEntryProps {
isSwap?: boolean
timestamp: number
amount: number
isExpired?: boolean
onPress: () => void
}

function HistoryEntry({ icon, txType, isSwap, timestamp, amount, onPress }: IHistoryEntryProps) {
function HistoryEntry({ icon, txType, isSwap, timestamp, amount, isExpired, onPress }: IHistoryEntryProps) {
const { t } = useTranslation([NS.history])
const { color, highlight } = useThemeContext()

Expand All @@ -169,7 +154,15 @@ function HistoryEntry({ icon, txType, isSwap, timestamp, amount, onPress }: IHis
</Text>
</View>
</View>
<Txt txt={getAmount()} styles={[{ color: getColor(highlight, color) }]} />
<Txt
txt={
isExpired ?
t('expired', { ns: NS.common })
:
getAmount()
}
styles={[{ color: getColor(highlight, color) }]}
/>
</TouchableOpacity>
)
}
Expand Down
6 changes: 3 additions & 3 deletions src/components/ClipboardModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { getEncodedToken } from '@cashu/cashu-ts'
import { type RootStackParamList } from '@model/nav'
import { type NavigationProp, useNavigation } from '@react-navigation/core'
import { useFocusClaimContext } from '@src/context/FocusClaim'
import { useHistoryContext } from '@src/context/History'
import { usePromptContext } from '@src/context/Prompt'
import { useThemeContext } from '@src/context/Theme'
import { NS } from '@src/i18n'
import { addToHistory } from '@store/latestHistoryEntries'
import { globals, mainColors } from '@styles'
import { copyStrToClipboard, formatInt, formatMintUrl, formatSatStr, isErr } from '@util'
import { claimToken } from '@wallet'
Expand All @@ -28,6 +28,7 @@ export default function ClipboardModal() {
const { tokenInfo, claimOpen, setClaimOpen, setClaimed, closeModal } = useFocusClaimContext()
const { loading, startLoading, stopLoading } = useLoading()
const { openPromptAutoClose } = usePromptContext()
const { addHistoryEntry } = useHistoryContext()

const handleRedeem = async () => {
startLoading()
Expand Down Expand Up @@ -56,8 +57,7 @@ export default function ClipboardModal() {
}
stopLoading()
setClaimOpen(false)
// add as history entry (receive ecash)
await addToHistory({
await addHistoryEntry({
amount: info.value,
type: 1,
value: encoded,
Expand Down