diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 00000000..51178c40 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,21 @@ +changelog: + exclude: + labels: + - ignore-for-release + - bot + categories: + - title: Pull requests + labels: + - release + - title: Breaking Changes 🛠 + labels: + - breaking-change + - title: New Features 🎉 + labels: + - enhancement + - title: Tests + labels: + - "test" + - title: Documentation + labels: + - "documentation" \ No newline at end of file diff --git a/package.json b/package.json index e295d2d2..f1d642ad 100644 --- a/package.json +++ b/package.json @@ -144,11 +144,11 @@ "blind-signatures", "lightning-network" ], - "version": "0.0.1-alpha", + "version": "0.0.2-alpha", "license": "AGPL-3.0-only", "bugs": { "url": "https://github.com/cashubtc/eNuts/issues" }, "main": "src/AppEntry.ts", "private": true -} \ No newline at end of file +} diff --git a/src/components/nav/CustomDrawer.tsx b/src/components/nav/CustomDrawer.tsx index 3260e90a..be0d0e05 100644 --- a/src/components/nav/CustomDrawer.tsx +++ b/src/components/nav/CustomDrawer.tsx @@ -6,6 +6,8 @@ import { skipRoute } from '@util' import { useContext } from 'react' import { Image, StyleSheet, Text, View } from 'react-native' import { TouchableOpacity } from 'react-native-gesture-handler' + +import { version } from '../../../package.json' // import { useState } from 'react' // import { interpolateNode } from 'react-native-reanimated' @@ -91,7 +93,7 @@ export default function CustomDrawer(props: DrawerContentComponentProps) { {/* Footer */} - v0.0.1-alpha + v{version} diff --git a/src/components/pages/Dashboard.tsx b/src/components/pages/Dashboard.tsx index 5d785188..d4819aec 100644 --- a/src/components/pages/Dashboard.tsx +++ b/src/components/pages/Dashboard.tsx @@ -97,6 +97,7 @@ export default function Dashboard({ navigation, route }: TDashboardPageProps) { if (!hasTrustedMint(userMints, tokenInfo.mints)) { // ask user for permission if token mint is not in his mint list setTrustModal(true) + stopLoading() return } await receiveToken(url) @@ -214,14 +215,16 @@ export default function Dashboard({ navigation, route }: TDashboardPageProps) { {modal.receiveOpts && { if (token.length) { return } void (async () => { + startLoading() const clipboard = await Clipboard.getStringAsync() if (!isCashuToken(clipboard)) { openPromptAutoClose(false, 'Your clipboard contains an invalid cashu token!') setModal({ ...modal, receiveOpts: false }) + stopLoading() return } setToken(clipboard) diff --git a/src/components/pages/History/Details.tsx b/src/components/pages/History/Details.tsx index ad1f26ca..5fc64001 100644 --- a/src/components/pages/History/Details.tsx +++ b/src/components/pages/History/Details.tsx @@ -1,13 +1,18 @@ import { getDecodedToken } from '@cashu/cashu-ts' -import { CheckmarkIcon, CopyIcon } from '@comps/Icons' +import Button from '@comps/Button' +import useLoading from '@comps/hooks/Loading' +import { BackupIcon, CheckCircleIcon, CheckmarkIcon, CopyIcon, QRIcon } from '@comps/Icons' +import MyModal from '@comps/modal' +import QR from '@comps/QR' import Txt from '@comps/Txt' import type { THistoryEntryPageProps } from '@model/nav' import TopNav from '@nav/TopNav' import { ThemeContext } from '@src/context/Theme' import { mainColors } from '@styles' import { formatInt, formatMintUrl, getLnInvoiceInfo } from '@util' +import { isTokenSpendable } from '@wallet' import * as Clipboard from 'expo-clipboard' -import { useContext, useState } from 'react' +import { useContext, useEffect, useState } from 'react' import { StyleSheet, Text, View } from 'react-native' import { TouchableOpacity } from 'react-native-gesture-handler' @@ -20,6 +25,9 @@ const initialCopyState = { export default function DetailsPage({ route }: THistoryEntryPageProps) { const { color } = useContext(ThemeContext) const [copy, setCopy] = useState(initialCopyState) + const [isSpent, setIsSpent] = useState(false) + const { loading, startLoading, stopLoading } = useLoading() + const [qr, setQr] = useState({ open: false, error: false }) const entry = route.params.entry const isPayment = entry.amount < 0 const isLn = entry.type === 2 @@ -48,6 +56,22 @@ export default function DetailsPage({ route }: THistoryEntryPageProps) { clearTimeout(t) }, 3000) } + const handleCheckSpendable = async () => { + if (isSpent || loading) { return } + startLoading() + setIsSpent(!(await isTokenSpendable(entry.value))) + stopLoading() + } + const handleQR = () => { + setQr({ ...qr, open: true }) + } + // initial check is token spent + useEffect(() => { + if (isLn) { return } + void (async () => { + setIsSpent(!(await isTokenSpendable(entry.value))) + })() + }, [isLn, entry.value]) return ( @@ -105,13 +129,32 @@ export default function DetailsPage({ route }: THistoryEntryPageProps) { {copy.value ? : - + } } + {/* check is token spendable */} + {isPayment && !isLn && + void handleCheckSpendable()} + > + + {isSpent ? + + : + loading ? + + : + + } + + } + + {/* Lightning related */} {isLn && <> {/* LN payment hash */} @@ -171,12 +214,60 @@ export default function DetailsPage({ route }: THistoryEntryPageProps) { + } + {/* QR code */} + + + + + + {qr.error ? + + : + { + setQr({ open: true, error: true }) + }} + /> + } + +