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 })
+ }}
+ />
+ }
+
+
)
}
+interface IIsSpentContainerProps {
+ isSpent: boolean,
+ handleCheckSpendable: () => void
+ children: React.ReactNode
+}
+
+function IsSpentContainer({ isSpent, handleCheckSpendable, children }: IIsSpentContainerProps) {
+ return isSpent ?
+
+ {children}
+
+ :
+ void handleCheckSpendable()}
+ >
+ {children}
+
+}
+
const styles = StyleSheet.create({
container: {
flex: 1,