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

Avoid foreground prompts for self-created token #201

Merged
merged 2 commits into from
Sep 13, 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
29 changes: 3 additions & 26 deletions src/components/Backup.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { l } from '@log'
import { useThemeContext } from '@src/context/Theme'
import { NS } from '@src/i18n'
import { globals } from '@styles'
import { formatMintUrl } from '@util'
import { formatMintUrl, share } from '@util'
import { useTranslation } from 'react-i18next'
import { Share, StyleSheet, Text } from 'react-native'
import { StyleSheet, Text } from 'react-native'

import ActionButtons from './ActionButtons'
import useCopy from './hooks/Copy'
Expand All @@ -19,28 +18,6 @@ export default function BackupSuccess({ token, mint }: IBackupSuccessProps) {
const { color } = useThemeContext()
const { copied, copy } = useCopy()

const handleShare = async () => {
try {
const res = await Share.share({
message: token, // `cashu://${route.params.token}`
url: `cashu://${token}`
})
if (res.action === Share.sharedAction) {
if (res.activityType) {
// shared with activity type of result.activityType
l('shared with activity type of result.activityType')
} else {
// shared
l('shared')
}
} else if (res.action === Share.dismissedAction) {
// dismissed
l('sharing dismissed')
}
} catch (e) {
l(e)
}
}
return (
<>
<Text style={[globals(color).navTxt, styles.subTxt]}>
Expand All @@ -57,7 +34,7 @@ export default function BackupSuccess({ token, mint }: IBackupSuccessProps) {
<ActionButtons
absolutePos
topBtnTxt={t('share')}
topBtnAction={() => void handleShare()}
topBtnAction={() => void share(token, `cashu://${token}`)}
bottomBtnTxt={copied ? t('copied') : t('copyToken')}
bottomBtnAction={() => void copy(token)}
/>
Expand Down
4 changes: 4 additions & 0 deletions src/context/FocusClaim.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { getMintsUrls } from '@db'
import { l } from '@log'
import type { ITokenInfo } from '@model'
import { NS } from '@src/i18n'
import { store } from '@store'
import { STORE_KEYS } from '@store/consts'
import { addToHistory } from '@store/latestHistoryEntries'
import { formatInt, formatMintUrl, getStrFromClipboard, hasTrustedMint, isCashuToken, isErr, sleep } from '@util'
import { claimToken, isTokenSpendable } from '@wallet'
Expand All @@ -29,8 +31,10 @@ const useFocusClaim = () => {
// in an empty string returned. Find a better way than the following function to handle it.
let isSpent = false
const fn = async () => {
const selfCreated = await store.get(STORE_KEYS.createdToken)
const clipboard = await getStrFromClipboard()
if (!clipboard?.length || !isCashuToken(clipboard)) { return false }
if (selfCreated === clipboard) { return false }
const info = getTokenInfo(clipboard)
if (!info) { return false }
// check if mint is a trusted one
Expand Down
28 changes: 3 additions & 25 deletions src/screens/Payment/Receive/Invoice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ 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 { formatMintUrl, formatSeconds, isErr, openUrl } from '@util'
import { formatMintUrl, formatSeconds, isErr, openUrl, share } from '@util'
import { requestToken } from '@wallet'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Share, StyleSheet, Text, TouchableOpacity, View } from 'react-native'
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'

export default function InvoiceScreen({ navigation, route }: TMintInvoicePageProps) {
Expand Down Expand Up @@ -62,28 +62,6 @@ export default function InvoiceScreen({ navigation, route }: TMintInvoicePagePro
}
}

const handleShare = async () => {
try {
const res = await Share.share({
message: paymentRequest,
})
if (res.action === Share.sharedAction) {
if (res.activityType) {
// shared with activity type of result.activityType
l('shared with activity type of result.activityType')
} else {
// shared
l('shared')
}
} else if (res.action === Share.dismissedAction) {
// dismissed
l('sharing dismissed')
}
} catch (e) {
l(e)
}
}

// countdown
useEffect(() => {
const timeLeft = Math.ceil((expiryTime - Date.now()) / 1000)
Expand Down Expand Up @@ -157,7 +135,7 @@ export default function InvoiceScreen({ navigation, route }: TMintInvoicePagePro
<TxtButton
txt={t('shareInvoice')}
icon={<ShareIcon width={24} height={24} color={hi[highlight]} />}
onPress={() => void handleShare()}
onPress={() => void share(paymentRequest)}
/>
</View>
:
Expand Down
39 changes: 10 additions & 29 deletions src/screens/Payment/Send/EncodedToken.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ import useCopy from '@comps/hooks/Copy'
import { CopyIcon, ShareIcon } from '@comps/Icons'
import QR from '@comps/QR'
import Txt from '@comps/Txt'
import { l } from '@log'
import type { TEncodedTokenPageProps } from '@model/nav'
import TopNav from '@nav/TopNav'
import { isIOS } from '@src/consts'
import { useThemeContext } from '@src/context/Theme'
import { NS } from '@src/i18n'
import { store } from '@store'
import { STORE_KEYS } from '@store/consts'
import { globals, highlight as hi, mainColors } from '@styles'
import { vib } from '@util'
import { share, vib } from '@util'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Share, StyleSheet, View } from 'react-native'
import { StyleSheet, View } from 'react-native'

/**
* The page that shows the created Cashu token that can be scanned, copied or shared
Expand All @@ -24,31 +25,11 @@ export default function EncodedTokenPage({ navigation, route }: TEncodedTokenPag
const { copied, copy } = useCopy()
const [error, setError] = useState({ msg: '', open: false })

useEffect(() => vib(400), [])

// share token
const handleShare = async () => {
try {
const res = await Share.share({
message: route.params.token, // `cashu://${route.params.token}`
url: `cashu://${route.params.token}`
})
if (res.action === Share.sharedAction) {
if (res.activityType) {
// shared with activity type of result.activityType
l('shared with activity type of result.activityType')
} else {
// shared
l('shared')
}
} else if (res.action === Share.dismissedAction) {
// dismissed
l('sharing dismissed')
}
} catch (e) {
l(e)
}
}
useEffect(() => {
// we can save the created token here to avoid foreground prompts of self-created tokens
void store.set(STORE_KEYS.createdToken, route.params.token)
vib(400)
}, [route.params.token])

return (
<View style={[globals(color).container, styles.container, { paddingBottom: isIOS ? 50 : 20 }]}>
Expand Down Expand Up @@ -79,7 +60,7 @@ export default function EncodedTokenPage({ navigation, route }: TEncodedTokenPag
<ActionButtons
topBtnTxt={t('share')}
topIcon={<ShareIcon width={20} height={20} color={mainColors.WHITE} />}
topBtnAction={() => void handleShare()}
topBtnAction={() => void share(route.params.token, `cashu://${route.params.token}`)}
bottomBtnTxt={copied ? t('copied') + '!' : t('copyToken')}
bottomIcon={<CopyIcon color={hi[highlight]} />}
bottomBtnAction={() => void copy(route.params.token)}
Expand Down
3 changes: 2 additions & 1 deletion src/storage/store/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export const STORE_KEYS = {
defaultMint: 'MINT_STORE=|:|=default_mint',
hiddenBal: 'privacy_balance',
hiddenTxs: 'privacy_txs',
latestHistory: 'history_latest'
latestHistory: 'history_latest',
createdToken: 'createdToken',
}

export const SECURESTORE_KEY = 'auth_pin'
Expand Down
22 changes: 21 additions & 1 deletion src/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { ILnUrl, IProofSelection } from '@model'
import axios from 'axios'
import type { Buffer } from 'buffer/'
import * as Clipboard from 'expo-clipboard'
import { Linking, Vibration } from 'react-native'
import { Linking, Share, Vibration } from 'react-native'

import { getLanguageCode } from './localization'
import { isArr, isBuf, isNum, isStr } from './typeguards'
Expand Down Expand Up @@ -276,4 +276,24 @@ export async function copyStrToClipboard(str: string) {
export async function getStrFromClipboard() {
const s = await Clipboard.getStringAsync()
return !s || s === 'null' ? null : s
}

export async function share(message: string, url?: string) {
try {
const res = await Share.share({ message, url })
if (res.action === Share.sharedAction) {
if (res.activityType) {
// shared with activity type of result.activityType
l('shared with activity type of result.activityType')
} else {
// shared
l('shared')
}
} else if (res.action === Share.dismissedAction) {
// dismissed
l('sharing dismissed')
}
} catch (e) {
l('[quick-share error] ', e)
}
}
Loading