diff --git a/README.md b/README.md
index b988fbe9..49fc80b6 100644
--- a/README.md
+++ b/README.md
@@ -137,7 +137,7 @@ Minibits wallet is in early beta and available as of now only for Android device
Minibits is a bare React Native app written in Typescript. The project structure and code itself are intentionally verbose to support readability. Critical wallet code is reasonably documented. However, there is vast space for existing code improvements, refactoring, and bug fixing. This is an early beta software and the author does not code for a living.
-The code is derived from Ignite template, however with many libraries, notably Expo, stripped down to achieve fast startup times. Performance bottleneck on some Android devices is react-native-keychain. To overcome this, it has been patched not to warm-up on startup, caching for wallet operatoions is in place and its use to encrypt storage is opt-in.
+The code is derived from Ignite template, however with many libraries, notably Expo, stripped down to achieve fast startup times. Performance bottleneck on some Android devices is react-native-keychain. To overcome this, it has been patched not to warm-up on startup, caching for wallet operations is in place and its use to encrypt storage is opt-in.
Wallet state is managed by mobx-state-tree and persisted in fast MMKV storage. Only the basic mobx concepts are in place, whole model could be improved. All critical wallet code is in services/walletService.ts and all ecash state changes are in models/ProofsStore.ts. Wallet communication with the mints is in services/cashuMintClient.ts and uses [cashu-ts](https://github.com/cashubtc/cashu-ts) library.
diff --git a/src/i18n/en.ts b/src/i18n/en.ts
index 3f2f824d..26370082 100644
--- a/src/i18n/en.ts
+++ b/src/i18n/en.ts
@@ -7,11 +7,47 @@ const en = {
paste: "Paste",
scan: "Scan",
copy: "Copy",
+ copyFailParam: "Couldn't copy %{param}",
confirm: "Confirm",
save: "Save",
add: "Add",
+ success: "Success!",
offline: "Offline",
+ qr: "QR Code",
+ sats: "SATS",
+ notCreated: 'Not yet created',
+ resetDefault: 'Reset to default',
+ share: "Share",
},
+ paymentCommon: {
+ pay: "Pay",
+ payMe: 'Pay me',
+ paySatsAmountTo: "Pay %{amount} SATS to",
+ payFromWallet: 'Pay from wallet',
+ receiveInPerson: "Receive in person"
+ },
+ transactionCommon: {
+ from: "From %{sender}",
+ receivedFrom: "Received from %{sender}",
+ sentTo: "Sent to %{receiver}",
+ paidTo: "Paid to %{receiver}",
+ youReceived: 'You received',
+ youSent: 'You sent',
+ youPaid: 'You paid',
+ unknown: 'Unknown transaction',
+ status: {
+ completedFee: 'Completed %{fee}',
+ draft: 'Draft',
+ error: 'Error',
+ pending: 'Pending',
+ prepared: 'Prepared',
+ reverted: 'Reverted',
+ blocked: 'Blocked',
+ expired: 'Expired'
+ },
+ tapToRedeem: 'Tap to redeem',
+ redeemOnline: 'Redeem online'
+ },
welcomeScreen: {
page1: {
heading: "Welcome",
@@ -48,6 +84,10 @@ const en = {
topUpWalletSubText: "Top up your balance by paying Bitcoin lightning invoice from another wallet",
transferFromWallet: "Transfer from wallet",
transferFromWalletSubText: "Debit your balance by paying Bitcoin lightning invoice from this wallet",
+ feeBadge: {
+ final: "+ final fee %{fee} SATS",
+ upto: "+ fee up to %{fee} SATS"
+ }
},
receiveScreen: {
title: "Receive",
@@ -112,8 +152,75 @@ const en = {
contactsScreen: {
new: "New",
scan: "Scan",
- newTitle: "Add new contact",
+ newTitle: "Add new contact",
+ getInvoice: "Get invoice",
+ ownName: {
+ donationSuccess: "Thank you! Donation for %{receiver} has been successfully paid.",
+ tooShort: 'Write your wallet profile name to the text box, use min 2 characters.',
+ illegalChar: 'Do not use . or - characters at the beginning or the end of name.',
+ profileExists: 'This wallet name is already in use, choose another one.',
+ chooseOwnName: 'Choose your own name',
+ chooseOwnNameFooter: 'Use lowercase letters, numbers and .-_',
+ available: "%{name} is available!",
+ payToGetOwnName: "Pay the following lightning invoice and get your %{name} wallet name.",
+ ctaPay: "Pay from wallet",
+ insufficient: 'Your wallet balance is not enough to pay this invoice amount but you can still pay it from another wallet.',
+ betaWarning: 'Please accept this is an early beta software. Your data can still be lost due to a bug or unexpected data loss.'
+ },
+ privateContacts: {
+ selectSendPaymentRequest: 'Select contact to send your payment request to.',
+ selectSendToken: 'Select contact to send your ecash to.',
+ selectSendLnURL: 'Select contact to send Lightning payment to.',
+ saveNewFormat: "Please enter a wallet address in name@domain.xyz format",
+ profileNotFound: "Profile name %{name} could not be found. Make sure the name is correct.",
+ noLightningAddress: 'This contact does not have a Lightning address, send ecash instead.',
+ explainerText: "Private contacts",
+ explainerSubText: "Add other Minibits users as your private contacts. Every user gets sharable @minibits.cash wallet address. You can pay privately to your contacts anytime even if they are offline.",
+ switchName: "Switch your wallet name and picture?",
+ switchNameSubText: 'Get cooler wallet name or profile picture. Select from an array of random names and images or opt for your own @minibits.cash wallet name.',
+ bottomModal: "Private contacts are unique identifiers of other Minibits wallets. You can use them to send or request ecash and you can safely share your own with others.",
+ domainMinibits: "Use minibits.cash domain",
+ domainExternal: "Use another domain"
+ },
+ publicContacts: {
+ npubPasteError: 'Copy your NPUB key first, then paste',
+ relayurlPasteError: 'Copy your relay URL key first, then paste',
+ relayExists: 'Relay already exists.',
+ missingLightningAddress: 'This contact does not have a Lightning address, send ecash instead.',
+ nostrTip: 'Tip the people you follow',
+ nostrTipSubText: 'Add your NOSTR social network public key (npub) and tip or donate to your favourite people and projects directly from the minibits wallet.',
+ nostrSetPublicKey: 'Set your public key',
+ nostrSetPublicKeySubText: 'Add or change your NOSTR social network public key (npub).',
+ nostrSetRelay: 'Set relay',
+ nostrSetRelaySubText: 'Add or change your own relay if your profile and follows are not hosted on the default relays.',
+ nostrRemovePub: 'Remove your public key',
+ nostrRemovePubSubText: 'Remove your npub key and stop loading public contacts.',
+ addNpub: 'Add your npub key',
+ pasteDemoKey: 'Paste demo key',
+ setOwnRelay: 'Set your own relay'
+ },
+ randomName: {
+ selectOneOfUsernames: 'Select one of the usernames'
+ }
},
+ paymentRequestScreen: {
+ incoming: {
+ noRequests: 'There are no incoming payment requests to be paid or they have already expired.'
+ },
+ outgoing: {
+ noRequests: 'There are no outgoing payment requests to be paid or they have already expired.',
+ invoiceShared: 'Lightning invoice has been shared, waiting to be paid by receiver.',
+ sharingCancelled: 'Sharing cancelled',
+ scanAndPay: 'Scan and pay to top-up'
+ },
+ // might move this to common later if it gets used in other screens
+ listItem: {
+ expires: "Expires %{datetime}",
+ expired: "Expired %{datetime}",
+ requestPaymentSuccess: 'Request has been paid'
+ }
+ },
+
profileScreen: {
changeAvatar: "Change picture",
changeAvatarSubtext: "Select one from randomly generated pictures.",
diff --git a/src/i18n/translate.ts b/src/i18n/translate.ts
index ae6aebab..2a82e8fe 100644
--- a/src/i18n/translate.ts
+++ b/src/i18n/translate.ts
@@ -1,7 +1,6 @@
-
-import { I18nOptions } from "i18n-js"
-import { i18n } from "./i18n"
-import { TxKeyPath } from "./i18n"
+import {I18nOptions} from 'i18n-js'
+import {i18n} from './i18n'
+import {TxKeyPath} from './i18n'
/**
* Translates text.
@@ -12,21 +11,29 @@ import { TxKeyPath } from "./i18n"
*
* @example
* Translations:
- *
- * ```en.ts
+ * ```ts
+ * // en.ts
* {
- * "hello": "Hello, {{name}}!"
+ * "heading": "Welcome to the app!",
+ * "buttonText": "Donate",
+ * "greeting": "Hello, %{name}!" // parameter
* }
* ```
- *
* Usage:
- * ```ts
- * import { translate } from "i18n-js"
+ * ```tsx
+ * import { translate } from "../../i18n";
+ * // or whatever the path is, let it auto-import
*
- * translate("common.ok", { name: "world" })
- * // => "Hello world!"
+ * translate("heading") // => Welcome to the app!
+ * translate("greeting", { name: "world" }) // => Hello world!
+ * // => a Donate button
* ```
+ * You can use nested objects, access them like: `common.payment.success`
*/
-export function translate(key: TxKeyPath, options?: I18nOptions) {
- return i18n.t(key, options)
+
+export function translate(
+ key: TxKeyPath,
+ options?: Partial & {[parameter: string]: any},
+) {
+ return i18n.t(key, options as I18nOptions)
}
diff --git a/src/screens/Contacts/OwnName.tsx b/src/screens/Contacts/OwnName.tsx
index 4938f280..2088fae3 100644
--- a/src/screens/Contacts/OwnName.tsx
+++ b/src/screens/Contacts/OwnName.tsx
@@ -14,6 +14,7 @@ import { ResultModalInfo } from '../Wallet/ResultModalInfo'
import { MINIBITS_NIP05_DOMAIN } from '@env'
import { useFocusEffect } from '@react-navigation/native'
import { SendOption } from '../SendOptionsScreen'
+import { translate } from '../../i18n'
const DEFAULT_DONATION_AMOUNT = 100
@@ -101,7 +102,7 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin
stopPolling(`checkDonationPaidPoller-${donationInvoice.payment_hash}`)
setResultModalInfo({
status: TransactionStatus.COMPLETED,
- message: `Thank you! Donation for ${ownName+MINIBITS_NIP05_DOMAIN} has been successfully paid.`
+ message: translate("contactsScreen.ownName.donationSuccess", { receiver: ownName+MINIBITS_NIP05_DOMAIN })
})
toggleResultModal()
resetState()
@@ -150,12 +151,12 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin
const onOwnNameCheck = async function () {
if(!ownName || ownName.length < 2) {
- setInfo('Write your wallet profile name to the text box, use min 2 characters.')
+ setInfo(translate('contactsScreen.ownName.tooShort'))
return
}
if(!isValidName(ownName)) {
- setInfo('Do not use . or - characters at the beginning or the end of name.')
+ setInfo(translate('contactsScreen.ownName.illegalChar'))
return
}
@@ -163,7 +164,7 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin
const profileExists = await MinibitsClient.getWalletProfileByNip05(ownName + MINIBITS_NIP05_DOMAIN)
if(profileExists) {
- setInfo('This wallet name is already in use, choose another one.')
+ setInfo(translate("contactsScreen.ownName.profileExists"))
return
}
setIsChecked(true)
@@ -265,7 +266,7 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin
@@ -292,7 +293,7 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin
/>
}
- footer={'Use lowercase letters, numbers and .-_' }
+ footerTx='contactsScreen.ownName.chooseOwnNameFooter'
footerStyle={{color: hint, textAlign: 'center'}}
/>
@@ -305,13 +306,13 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin
{donationInvoice ? (
<>
{isQRcodeVisible ? (
@@ -341,12 +342,12 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin
@@ -355,14 +356,14 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin
{isQRcodeVisible ? (
@@ -405,7 +406,7 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin
preset='heading'
/>
@@ -444,18 +445,18 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin
-
+
>
)}
@@ -474,7 +475,7 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin
diff --git a/src/screens/Contacts/PrivateContacts.tsx b/src/screens/Contacts/PrivateContacts.tsx
index 8cc4bb7a..e0fa50b4 100644
--- a/src/screens/Contacts/PrivateContacts.tsx
+++ b/src/screens/Contacts/PrivateContacts.tsx
@@ -17,6 +17,7 @@ import { ReceiveOption } from '../ReceiveOptionsScreen'
import { SendOption } from '../SendOptionsScreen'
import { infoMessage, warningMessage } from '../../utils/utils'
import { IncomingDataType, IncomingParser } from '../../services/incomingParser'
+import { translate } from '../../i18n'
import { RouteProp } from '@react-navigation/native'
@@ -81,14 +82,13 @@ export const PrivateContacts = observer(function (props: {
log.trace('Start', newContactName, 'saveNewContact')
if(!newContactName) {
- setInfo(`Please enter a wallet address in name@domain.xyz format`)
+ setInfo(translate("contactsScreen.privateContacts.saveNewFormat"))
return
}
toggleNewContactModal() // close
setIsLoading(true)
-
try {
let newContact: Contact | undefined = undefined
@@ -118,7 +118,7 @@ export const PrivateContacts = observer(function (props: {
const profileRecord = await MinibitsClient.getWalletProfileByNip05(newContactName + MINIBITS_NIP05_DOMAIN)
if(!profileRecord) {
- warningMessage(`Profile name ${newContactName + MINIBITS_NIP05_DOMAIN} could not be found. Make sure the name is correct.`)
+ warningMessage(translate("contactsScreen.privateContacts.profileNotFound", { name: newContactName + MINIBITS_NIP05_DOMAIN }))
setIsLoading(false)
return
}
@@ -204,7 +204,7 @@ export const PrivateContacts = observer(function (props: {
if(paymentOption && paymentOption === SendOption.LNURL_ADDRESS) {
if(!contact.lud16) {
- setInfo('This contact does not have a Lightning address, send ecash instead.')
+ setInfo(translate("contactsScreen.privateContacts.noLightningAddress"))
return
}
@@ -248,57 +248,55 @@ export const PrivateContacts = observer(function (props: {
const iconBottom = useThemeColor('button')
return (
-
+
- {contactsStore.count > 0 ? (
+ {contactsStore.count > 0 ? (
-
- data={contactsStore.all as Contact[]}
- renderItem={({ item, index }) => {
- return(
- gotoContactDetail(item)}
- />
- )
- }}
- keyExtractor={(item) => item.pubkey}
- style={{ flexGrow: 0 }}
- />
- >
- }
- style={$card}
+ ContentComponent={
+
+ data={contactsStore.all as Contact[]}
+ renderItem={({item, index}) => {
+ return (
+ gotoContactDetail(item)}
+ />
+ )
+ }}
+ keyExtractor={item => item.pubkey}
+ style={{flexGrow: 0}}
+ />
+ }
+ style={$card}
/>
- ) : (
-
-
-
- >
- }
- style={$card}
- />
- )}
- {isLoading && }
+ ) : (
+
+
+
+ >
+ }
+ style={$card}
+ />
+ )}
+ {isLoading && }
@@ -317,51 +315,78 @@ export const PrivateContacts = observer(function (props: {
-
-
-
- setNewContactName(newContactName)}
- value={newContactName}
- autoCapitalize='none'
- keyboardType='default'
- maxLength={60}
- selectTextOnFocus={true}
- style={[$contactInput, {backgroundColor: inputBg}, (isExternalDomain) && {marginRight: spacing.small, borderTopRightRadius: spacing.small, borderBottomRightRadius: spacing.small}]}
- />
- {!isExternalDomain && (
-
-
-
- )}
-
-
-
+
+
+
+
+ setNewContactName(newContactName)
+ }
+ value={newContactName}
+ autoCapitalize="none"
+ keyboardType="default"
+ maxLength={60}
+ selectTextOnFocus={true}
+ style={[
+ $contactInput,
+ {backgroundColor: inputBg},
+ isExternalDomain && {
+ marginRight: spacing.small,
+ borderTopRightRadius: spacing.small,
+ borderBottomRightRadius: spacing.small,
+ },
+ ]}
+ />
+ {!isExternalDomain && (
+
+
-
- }
- onBackButtonPress={toggleNewContactModal}
- onBackdropPress={toggleNewContactModal}
- />
+
+ )}
+
+
+
+
+ }
+ onBackButtonPress={toggleNewContactModal}
+ onBackdropPress={toggleNewContactModal}
+ />
{info && }
{error && }
-
+
)
})
diff --git a/src/screens/Contacts/ProfileHeader.tsx b/src/screens/Contacts/ProfileHeader.tsx
index b11e7bdc..b6f86a51 100644
--- a/src/screens/Contacts/ProfileHeader.tsx
+++ b/src/screens/Contacts/ProfileHeader.tsx
@@ -6,6 +6,7 @@ import { Icon, Text } from "../../components"
import { useStores } from "../../models"
import { spacing, useThemeColor } from "../../theme"
import { getImageSource } from '../../utils/utils'
+import { translate } from "../../i18n"
export interface ProfileHeaderProps {
@@ -36,7 +37,7 @@ export const ProfileHeader = observer(function (props: ProfileHeaderProps) {
color={'white'}
/>
)}
-
+
)
})
diff --git a/src/screens/Contacts/PublicContacts.tsx b/src/screens/Contacts/PublicContacts.tsx
index 1d1f35c0..f2a7e4d3 100644
--- a/src/screens/Contacts/PublicContacts.tsx
+++ b/src/screens/Contacts/PublicContacts.tsx
@@ -17,6 +17,7 @@ import { ReceiveOption } from '../ReceiveOptionsScreen'
import { useSafeAreaInsetsStyle } from '../../utils/useSafeAreaInsetsStyle'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { IncomingDataType, IncomingParser } from '../../services/incomingParser'
+import { translate } from '../../i18n'
import { RouteProp } from '@react-navigation/native'
@@ -150,8 +151,8 @@ export const PublicContacts = observer(function (props: {
if(event.kind === 3) {
const pubkeys = event.tags
- .filter((item: [string, string]) => item[0] === "p")
- .map((item: [string, string]) => item[1])
+ .filter((item) => item[0] === "p")
+ .map((item) => item[1])
log.trace('Following pubkeys:', pubkeys.length)
setFollowingPubkeys(pubkeys)
@@ -214,7 +215,7 @@ export const PublicContacts = observer(function (props: {
const onPastePublicPubkey = async function () {
const key = await Clipboard.getString()
if (!key) {
- setInfo('Copy your NPUB key first, then paste')
+ setInfo(translate("contactsScreen.publicContacts.npubPasteError"))
return
}
setNewPublicPubkey(key)
@@ -264,7 +265,7 @@ export const PublicContacts = observer(function (props: {
const onPastePublicRelay = async function () {
const url = await Clipboard.getString()
if (!url) {
- setInfo('Copy your relay URL key first, then paste')
+ setInfo(translate("contactsScreen.publicContacts.relayurlPasteError"))
return
}
setNewPublicRelay(url)
@@ -275,7 +276,7 @@ export const PublicContacts = observer(function (props: {
try {
if(newPublicRelay) {
if(relaysStore.alreadyExists(newPublicRelay)) {
- setInfo('Relay already exists.')
+ setInfo(translate("contactsScreen.publicContacts.relayExists"))
return
}
@@ -380,7 +381,7 @@ export const PublicContacts = observer(function (props: {
if(paymentOption && paymentOption === SendOption.LNURL_ADDRESS) {
if(!contact.lud16) {
- setInfo('This contact does not have a Lightning address, send ecash instead.')
+ setInfo(translate('contactsScreen.publicContacts.missingLightningAddress'))
//reset
navigation.setParams({
paymentOption: undefined,
@@ -439,8 +440,8 @@ export const PublicContacts = observer(function (props: {
leftIcon='faComment'
leftIconInverse={true}
leftIconColor={colors.palette.iconViolet200}
- text='Tip the people you follow'
- subText={'Add your NOSTR social network public key (npub) and tip or donate to your favourite people and projects directly from the minibits wallet.'}
+ tx="contactsScreen.publicContacts.nostrTip"
+ subTx='contactsScreen.publicContacts.nostrTipSubText'
onPress={toggleNpubModal}
/>
}
@@ -522,22 +523,22 @@ export const PublicContacts = observer(function (props: {
<>
>
@@ -549,7 +550,7 @@ export const PublicContacts = observer(function (props: {
isVisible={isNpubModalVisible}
ContentComponent={
-
+
- setNewPublicPubkey(defaultPublicNpub)} text='Paste demo key'/>
-
+ setNewPublicPubkey(defaultPublicNpub)} tx="contactsScreen.publicContacts.pasteDemoKey"/>
+
}
@@ -587,7 +588,7 @@ export const PublicContacts = observer(function (props: {
isVisible={isRelayModalVisible ? true : false}
ContentComponent={
-
+
{newPublicRelay && (
-
+
)}
-
+
}
diff --git a/src/screens/Contacts/RandomName.tsx b/src/screens/Contacts/RandomName.tsx
index a809bc1d..be059375 100644
--- a/src/screens/Contacts/RandomName.tsx
+++ b/src/screens/Contacts/RandomName.tsx
@@ -1,140 +1,151 @@
import {observer} from 'mobx-react-lite'
import React, {useEffect, useState} from 'react'
-import {TextStyle, View, ViewStyle, InteractionManager, FlatList } from 'react-native'
+import {
+ TextStyle,
+ View,
+ ViewStyle,
+ InteractionManager,
+ FlatList,
+} from 'react-native'
import {spacing} from '../../theme'
-import {Button, Card, ErrorModal, InfoModal, Loading, Screen, Text} from '../../components'
+import {
+ Button,
+ Card,
+ ErrorModal,
+ InfoModal,
+ Loading,
+ Screen,
+ Text,
+} from '../../components'
import {useStores} from '../../models'
-import AppError, { Err } from '../../utils/AppError'
+import AppError, {Err} from '../../utils/AppError'
import {$sizeStyles} from '../../components/Text'
import {getRandomUsername} from '../../utils/usernames'
-import { scale } from '@gocodingnow/rn-size-matters'
+import {scale} from '@gocodingnow/rn-size-matters'
+import {translate} from '../../i18n'
+export const RandomName = observer(function (props: {
+ navigation: any
+ pubkey: string
+}) {
+ // const navigation = useNavigation()
+ const {userSettingsStore, walletProfileStore} = useStores()
+ const {navigation} = props
-export const RandomName = observer(function (props: {navigation: any, pubkey: string}) {
- // const navigation = useNavigation()
- const {userSettingsStore, walletProfileStore} = useStores()
- const {navigation} = props
+ const [randomNames, setRandomNames] = useState([])
+ const [selectedName, setSelectedName] = useState('')
+ const [info, setInfo] = useState('')
+ const [isLoading, setIsLoading] = useState(false)
+ const [error, setError] = useState()
- const [randomNames, setRandomNames] = useState([])
- const [selectedName, setSelectedName] = useState('')
- const [info, setInfo] = useState('')
- const [isLoading, setIsLoading] = useState(false)
- const [error, setError] = useState()
-
- useEffect(() => {
- const load = async () => {
- setIsLoading(true)
- InteractionManager.runAfterInteractions(async () => {
- let i = 0
- let names = []
- while(i < 8) {
- const name = getRandomUsername()
- names.push(name)
- i++
- }
-
- setRandomNames(names)
- setIsLoading(false)
- })
+ useEffect(() => {
+ const load = async () => {
+ setIsLoading(true)
+ InteractionManager.runAfterInteractions(async () => {
+ let i = 0
+ let names = []
+ while (i < 8) {
+ const name = getRandomUsername()
+ names.push(name)
+ i++
}
- load()
- return () => {}
- }, [])
- const confirmSelectedName = async function () {
- if(!selectedName) {
- setInfo('Select one of the usernames')
- return
- }
-
- try {
- setIsLoading(true)
-
- await walletProfileStore.updateName(selectedName)
-
- setIsLoading(false)
- navigation.goBack()
- return
- } catch (e: any) {
- handleError(e)
- }
- }
-
-
- const handleError = function (e: AppError): void {
+ setRandomNames(names)
setIsLoading(false)
- setError(e)
+ })
}
-//
- return (
-
-
-
- {
- return(
- setSelectedName(item)}
- text={`${item}`}
- style={{minWidth: scale(150), margin: spacing.extraSmall}}
- textStyle={$sizeStyles.xs}
- />
- )
- }}
- keyExtractor={(item) => item}
- style={{ flexGrow: 0 }}
- />
- >
- }
- style={$card}
- />
-
- confirmSelectedName()}
- text={'Confirm'}
- style={{marginTop: spacing.medium, minWidth: 120}}
- />
-
+ load()
+ return () => {}
+ }, [])
+
+ const confirmSelectedName = async function () {
+ if (!selectedName) {
+ setInfo(translate('contactsScreen.randomName.selectOneOfUsernames'))
+ return
+ }
+
+ try {
+ setIsLoading(true)
+
+ await walletProfileStore.updateName(selectedName)
+
+ setIsLoading(false)
+ navigation.goBack()
+ return
+ } catch (e: any) {
+ handleError(e)
+ }
+ }
+
+ const handleError = function (e: AppError): void {
+ setIsLoading(false)
+ setError(e)
+ }
+ //
+ return (
+
+
+ {
+ return (
+ setSelectedName(item)}
+ text={`${item}`}
+ style={{minWidth: scale(150), margin: spacing.extraSmall}}
+ textStyle={$sizeStyles.xs}
+ />
+ )
+ }}
+ keyExtractor={item => item}
+ style={{flexGrow: 0}}
+ />
+ }
+ style={$card}
+ />
+
+ confirmSelectedName()}
+ tx="common.confirm"
+ style={{marginTop: spacing.medium, minWidth: 120}}
+ />
- {error && }
- {info && }
- {isLoading && }
-
- )
- })
-
-
+
+ {error && }
+ {info && }
+ {isLoading && }
+
+ )
+})
const $screen: ViewStyle = {}
-
-const $contentContainer: TextStyle = {
- padding: spacing.extraSmall,
+const $contentContainer: TextStyle = {
+ padding: spacing.extraSmall,
}
const $buttonContainer: ViewStyle = {
- flexDirection: 'row',
- alignSelf: 'center',
+ flexDirection: 'row',
+ alignSelf: 'center',
}
const $bottomContainer: ViewStyle = {
- position: 'absolute',
- bottom: 0,
- left: 0,
- right: 0,
- flex: 1,
- justifyContent: 'flex-end',
- marginBottom: spacing.medium,
- alignSelf: 'stretch',
- // opacity: 0,
- }
+ position: 'absolute',
+ bottom: 0,
+ left: 0,
+ right: 0,
+ flex: 1,
+ justifyContent: 'flex-end',
+ marginBottom: spacing.medium,
+ alignSelf: 'stretch',
+ // opacity: 0,
+}
const $card: ViewStyle = {
- marginBottom: 0,
+ marginBottom: 0,
}
diff --git a/src/screens/PaymentRequests/IncomingRequests.tsx b/src/screens/PaymentRequests/IncomingRequests.tsx
index a622be3c..873c0174 100644
--- a/src/screens/PaymentRequests/IncomingRequests.tsx
+++ b/src/screens/PaymentRequests/IncomingRequests.tsx
@@ -72,7 +72,7 @@ export const IncomingRequests = observer(function (props: {
/>
) : (
diff --git a/src/screens/PaymentRequests/OutgoingRequests.tsx b/src/screens/PaymentRequests/OutgoingRequests.tsx
index ba770e29..2ad9a009 100644
--- a/src/screens/PaymentRequests/OutgoingRequests.tsx
+++ b/src/screens/PaymentRequests/OutgoingRequests.tsx
@@ -29,6 +29,7 @@ import { PaymentRequestListItem } from './PaymentRequestListItem'
import QRCode from 'react-native-qrcode-svg'
import { infoMessage } from '../../utils/utils'
import Clipboard from '@react-native-clipboard/clipboard'
+import { translate } from '../../i18n'
export const OutgoingRequests = observer(function (props: {
@@ -73,11 +74,11 @@ export const OutgoingRequests = observer(function (props: {
if (result.action === Share.sharedAction) {
setTimeout(
- () => infoMessage('Lightning invoice has been shared, waiting to be paid by receiver.'),
+ () => infoMessage(translate("paymentRequestScreen.outgoing.invoiceShared")),
500,
)
} else if (result.action === Share.dismissedAction) {
- infoMessage('Sharing cancelled')
+ infoMessage(translate("paymentRequestScreen.outgoing.sharingCancelled"))
}
} catch (e: any) {
handleError(e)
@@ -89,7 +90,7 @@ export const OutgoingRequests = observer(function (props: {
try {
Clipboard.setString(selectedRequest?.encodedInvoice as string)
} catch (e: any) {
- setInfo(`Could not copy: ${e.message}`)
+ setInfo(translate("common.copyFailParam", { param: e.message }))
}
}
@@ -127,7 +128,7 @@ export const OutgoingRequests = observer(function (props: {
) : (
@@ -165,7 +166,7 @@ const ShareAsQRCodeBlock = observer(function (props: {
}) {
return (
-
+
(
@@ -187,10 +188,10 @@ const ShareAsQRCodeBlock = observer(function (props: {
/>
)}
/>
-
+
diff --git a/src/screens/PaymentRequests/PaymentRequestListItem.tsx b/src/screens/PaymentRequests/PaymentRequestListItem.tsx
index 4e679d61..e982e24d 100644
--- a/src/screens/PaymentRequests/PaymentRequestListItem.tsx
+++ b/src/screens/PaymentRequests/PaymentRequestListItem.tsx
@@ -1,207 +1,303 @@
-import {differenceInSeconds} from "date-fns"
-import { formatDistance } from 'date-fns'
-import { observer } from "mobx-react-lite"
-import React from "react"
-import { Image, ImageStyle, ScrollView, StyleSheet, TextStyle, View, ViewStyle } from "react-native"
-import { Button, Card, Icon, ListItem, Screen, Text } from "../../components"
-import { Contact, ContactType } from "../../models/Contact"
-import { PaymentRequest, PaymentRequestStatus, PaymentRequestType } from "../../models/PaymentRequest"
-import { log, NostrClient } from "../../services"
-import { colors, spacing, typography, useThemeColor } from "../../theme"
-import { getImageSource } from '../../utils/utils'
-import { ContactListItem } from "../Contacts/ContactListItem"
-import { SendOption } from "../SendOptionsScreen"
-import { CurrencySign } from "../Wallet/CurrencySign"
-import { CurrencyCode, MintUnitCurrencyPairs } from "../../services/wallet/currency"
-
+import {MINIBITS_NIP05_DOMAIN, MINIBITS_SERVER_API_HOST} from '@env'
+import differenceInSeconds from 'date-fns/differenceInSeconds'
+import formatDistance from 'date-fns/formatDistance'
+import {observer} from 'mobx-react-lite'
+import React from 'react'
+import {
+ Image,
+ ImageStyle,
+ ScrollView,
+ StyleSheet,
+ TextStyle,
+ View,
+ ViewStyle,
+} from 'react-native'
+import {Button, Card, Icon, ListItem, Screen, Text} from '../../components'
+import {Contact, ContactType} from '../../models/Contact'
+import {
+ PaymentRequest,
+ PaymentRequestStatus,
+ PaymentRequestType,
+} from '../../models/PaymentRequest'
+import {log, NostrClient} from '../../services'
+import {colors, spacing, typography, useThemeColor} from '../../theme'
+import {getImageSource} from '../../utils/utils'
+import {ContactListItem} from '../Contacts/ContactListItem'
+import {SendOption} from '../SendOptionsScreen'
+import { CurrencySign } from '../Wallet/CurrencySign'
+import { CurrencyCode, MintUnitCurrencyPairs } from '../../services/wallet/currency'
+import {translate} from '../../i18n'
export interface PaymentRequestListProps {
pr: PaymentRequest
- isFirst: boolean
+ isFirst: boolean
navigation: any
onShowQRModal?: any
}
-export const PaymentRequestListItem = observer(function (props: PaymentRequestListProps) {
-
- const { pr, isFirst, navigation, onShowQRModal } = props
- const hintColor = useThemeColor('textDim')
- const secToExpiry = differenceInSeconds(pr.expiresAt as Date, new Date())
- const expiryBg = ( secToExpiry < 0 ? colors.palette.angry500 : secToExpiry < 60 ? colors.palette.orange400 : colors.palette.success300)
- const separatorColor = useThemeColor('separator')
-
- const onGotoContactDetail = function() {
- log.trace(pr)
- navigation.navigate('ContactsNavigator', {
- screen: 'ContactDetail',
- params: {contact: pr.type === PaymentRequestType.INCOMING ? pr.contactFrom : pr.contactTo}
- })
- }
+export const PaymentRequestListItem = observer(function (
+ props: PaymentRequestListProps,
+) {
+ const {pr, isFirst, navigation, onShowQRModal} = props
+ const hintColor = useThemeColor('textDim')
+ const secToExpiry = differenceInSeconds(pr.expiresAt as Date, new Date())
+ const expiryBg =
+ secToExpiry < 0
+ ? colors.palette.angry500
+ : secToExpiry < 60
+ ? colors.palette.orange400
+ : colors.palette.success300
+ const separatorColor = useThemeColor('separator')
- const onPressPaymentRequest = function() {
- if (pr.type === PaymentRequestType.INCOMING) {
- navigation.navigate('Transfer', {
- paymentRequest: pr,
- paymentOption: SendOption.PAY_PAYMENT_REQUEST
- })
- } else {
- if(onShowQRModal) {
- onShowQRModal(pr)
+ const onGotoContactDetail = function () {
+ log.trace(pr)
+ navigation.navigate('ContactsNavigator', {
+ screen: 'ContactDetail',
+ params: {
+ contact:
+ pr.type === PaymentRequestType.INCOMING
+ ? pr.contactFrom
+ : pr.contactTo,
+ },
+ })
+ }
+
+ const onPressPaymentRequest = function () {
+ if (pr.type === PaymentRequestType.INCOMING) {
+ navigation.navigate('Transfer', {
+ paymentRequest: pr,
+ paymentOption: SendOption.PAY_PAYMENT_REQUEST,
+ })
+ } else {
+ if (onShowQRModal) {
+ onShowQRModal(pr)
+ }
+ }
+ }
+
+ const dim = useThemeColor('textDim')
+
+ return (
+
+
-
+
+
+
+ }
+ ContentComponent={
+ <>
+ {pr.contactFrom && pr.contactTo && (
+
+
+
+
+
+
+
+
+
+
+
+
+ {pr.contactFrom.picture ? (
+
+
-
-
-
- }
- ContentComponent={
- <>
- {pr.contactFrom && pr.contactTo && (
-
-
-
-
-
-
-
-
-
-
-
-
- {pr.contactFrom.picture ? (
-
-
-
- ) : (
-
- )}
-
-
-
- )}
- {pr.description && (
-
-
-
- )}
- {pr.status === PaymentRequestStatus.ACTIVE ? (
- 0 ? 's' : 'd'} ${formatDistance(pr.expiresAt as Date, new Date(), {addSuffix: true})}`}
- size='xxs'
- />
- ) : (
-
- )}
- >
- }
- FooterComponent={
-
- {pr.status === PaymentRequestStatus.ACTIVE && (
-
- )}
-
- }
- style={$card}
- />
- )
+
+ ) : (
+
+ )}
+
+
+
+ )}
+ {pr.description && (
+
+
+
+ )}
+ {pr.status === PaymentRequestStatus.ACTIVE ? (
+ 0
+ ? 'paymentRequestScreen.listItem.expires'
+ : 'paymentRequestScreen.listItem.expired',
+ ) +
+ ' ' +
+ formatDistance(pr.expiresAt as Date, new Date(), {
+ addSuffix: true,
+ })
+ }
+ size="xxs"
+ />
+ ) : (
+
+ )}
+ >
+ }
+ FooterComponent={
+
+ {pr.status === PaymentRequestStatus.ACTIVE && (
+
+ )}
+
+ }
+ style={$card}
+ />
+ )
})
const $profileIcon: ImageStyle = {
- padding: spacing.medium,
+ padding: spacing.medium,
}
const $headerContainer: TextStyle = {
- // justifyContent: 'space-between',
- alignItems: 'center',
- paddingHorizontal: spacing.medium,
- // marginBottom: spacing.small,
- // height: spacing.screenHeight * 0.18,
-}
-
-const $expiry: ViewStyle = {
- paddingHorizontal: spacing.small,
- borderRadius: spacing.tiny,
- alignSelf: 'center',
- marginVertical: spacing.small,
+ // justifyContent: 'space-between',
+ alignItems: 'center',
+ paddingHorizontal: spacing.medium,
+ // marginBottom: spacing.small,
+ // height: spacing.screenHeight * 0.18,
}
+const $expiry: ViewStyle = {
+ paddingHorizontal: spacing.small,
+ borderRadius: spacing.tiny,
+ alignSelf: 'center',
+ marginVertical: spacing.small,
+}
const $buttonContainer: ViewStyle = {
- flexDirection: 'row',
- alignSelf: 'center',
+ flexDirection: 'row',
+ alignSelf: 'center',
}
-
const $iconContainer: ImageStyle = {
- padding: spacing.extraSmall,
- alignSelf: "center",
- marginRight: spacing.medium,
+ padding: spacing.extraSmall,
+ alignSelf: 'center',
+ marginRight: spacing.medium,
}
const $prText: TextStyle = {
- overflow: 'hidden',
- fontSize: 14,
+ overflow: 'hidden',
+ fontSize: 14,
}
const $card: ViewStyle = {
- marginBottom: spacing.small,
+ marginBottom: spacing.small,
}
-
diff --git a/src/screens/Transactions/TransactionListItem.tsx b/src/screens/Transactions/TransactionListItem.tsx
index 56bb60dc..7159d0db 100644
--- a/src/screens/Transactions/TransactionListItem.tsx
+++ b/src/screens/Transactions/TransactionListItem.tsx
@@ -1,89 +1,112 @@
-import { formatDistance } from 'date-fns'
-import { observer } from "mobx-react-lite"
-import React from "react"
-import { TextStyle, View, ViewStyle } from "react-native"
-import { Icon, ListItem, Text } from "../../components"
-import { Transaction, TransactionStatus, TransactionType } from "../../models/Transaction"
-import { colors, spacing, typography, useThemeColor } from "../../theme"
-import useIsInternetReachable from "../../utils/useIsInternetReachable"
+import formatDistance from 'date-fns/formatDistance'
+import {observer} from 'mobx-react-lite'
+import React from 'react'
+import {ScrollView, TextStyle, View, ViewStyle} from 'react-native'
+import {Button, Icon, ListItem, Screen, Text} from '../../components'
+import {
+ Transaction,
+ TransactionStatus,
+ TransactionType,
+} from '../../models/Transaction'
+import {colors, spacing, typography, useThemeColor} from '../../theme'
+import useIsInternetReachable from '../../utils/useIsInternetReachable'
+import {translate} from '../../i18n'
import { Currencies, MintUnit, MintUnitCurrencyPairs } from '../../services/wallet/currency'
import { CurrencyAmount } from '../Wallet/CurrencyAmount'
export interface TransactionListProps {
- transaction: Transaction,
- isFirst: boolean,
- isTimeAgoVisible: boolean,
- gotoTranDetail: any,
+ transaction: Transaction
+ isFirst: boolean
+ isTimeAgoVisible: boolean
+ gotoTranDetail: any
}
-export const TransactionListItem = observer(function (props: TransactionListProps) {
-
- const { transaction: tx, isTimeAgoVisible } = props
-
- const txReceiveColor = colors.palette.success300
- const txSendColor = useThemeColor('amount')
- const txErrorColor = useThemeColor('textDim')
- const txPendingColor = useThemeColor('textDim')
- const isInternetReachable = useIsInternetReachable()
-
- const getText = function(tx: Transaction) {
- if(tx.noteToSelf) return tx.noteToSelf
- // if(tx.memo) return tx.memo
-
- switch(tx.type) {
- case TransactionType.RECEIVE || TransactionType.RECEIVE_OFFLINE:
- if(tx.sentFrom) {
- if(!tx.memo || tx.memo.includes('Sent from Minibits')) {
- return `From ${tx.sentFrom}`
- }
- } else {
- return tx.memo ? tx.memo : 'You received'
- }
- case TransactionType.SEND:
- return tx.memo ? tx.memo : tx.sentTo ? `Sent to ${tx.sentTo}` : 'You sent'
- case TransactionType.TOPUP:
- return tx.memo ? tx.memo : tx.sentFrom ? `Received from ${tx.sentFrom}` : 'You received'
- case TransactionType.TRANSFER:
- return tx.memo ? tx.memo : tx.sentTo ? `Paid to ${tx.sentTo}` : 'You paid'
- default:
- return 'Unknown transaction'
- }
+export const TransactionListItem = observer(function (
+ props: TransactionListProps,
+) {
+ const {transaction: tx, isTimeAgoVisible} = props
+
+ const txReceiveColor = colors.palette.success300
+ const txSendColor = useThemeColor('amount')
+ const txErrorColor = useThemeColor('textDim')
+ const txPendingColor = useThemeColor('textDim')
+ const isInternetReachable = useIsInternetReachable()
+
+ const getText = function (tx: Transaction) {
+ if (tx.noteToSelf) return tx.noteToSelf
+ // if(tx.memo) return tx.memo
+
+ switch (tx.type) {
+ case TransactionType.RECEIVE || TransactionType.RECEIVE_OFFLINE:
+ if (tx.sentFrom) {
+ if (!tx.memo || tx.memo.includes('Sent from Minibits')) {
+ return translate('transactionCommon.from', {sender: tx.sentFrom})
+ }
+ } else {
+ return tx.memo ? tx.memo : translate('transactionCommon.youReceived')
+ }
+ case TransactionType.SEND:
+ return tx.memo
+ ? tx.memo
+ : tx.sentTo
+ ? translate('transactionCommon.sentTo', {receiver: tx.sentTo})
+ : translate('transactionCommon.youSent')
+ case TransactionType.TOPUP:
+ return tx.memo
+ ? tx.memo
+ : tx.sentFrom
+ ? translate('transactionCommon.receivedFrom', {sender: tx.sentFrom})
+ : translate('transactionCommon.youReceived')
+ case TransactionType.TRANSFER:
+ return tx.memo
+ ? tx.memo
+ : tx.sentTo
+ ? translate('transactionCommon.paidTo', {receiver: tx.sentTo})
+ : translate('transactionCommon.youPaid')
+ default:
+ return translate('transactionCommon.unknown')
}
-
-
- const getSubText = function(tx: Transaction) {
-
- let distance = formatDistance(tx.createdAt as Date, new Date(), {addSuffix: true})
- let timeAgo = ''
- if (isTimeAgoVisible) timeAgo = `${distance} · `
-
- switch(tx.status) {
- case TransactionStatus.COMPLETED:
- return timeAgo + `Completed ${(tx.fee && tx.fee > 0) ? ' · Fee ' + tx.fee : ''}`
- case TransactionStatus.DRAFT:
- return timeAgo + `Draft`
- case TransactionStatus.ERROR:
- return timeAgo + `Error`
- case TransactionStatus.PENDING:
- return timeAgo + `Pending`
- case TransactionStatus.PREPARED:
- return timeAgo + `Prepared`
- case TransactionStatus.PREPARED_OFFLINE:
- if(isInternetReachable) {
- return timeAgo + `Tap to redeem`
- } else {
- return timeAgo + `Redeem online`
- }
- case TransactionStatus.REVERTED:
- return timeAgo + `Reverted`
- case TransactionStatus.BLOCKED:
- return timeAgo + `Blocked`
- case TransactionStatus.EXPIRED:
- return timeAgo + `Expired`
- default:
- return timeAgo
- }
+ }
+
+ const getSubText = function (tx: Transaction) {
+ let distance = formatDistance(tx.createdAt as Date, new Date(), {
+ addSuffix: true,
+ })
+ let timeAgo = ''
+ if (isTimeAgoVisible) timeAgo = `${distance} · `
+
+ switch (tx.status) {
+ case TransactionStatus.COMPLETED:
+ return (
+ timeAgo +
+ translate('transactionCommon.status.completedFee', {
+ fee: (tx.fee && tx.fee > 0) ? ' · Fee ' + tx.fee : '',
+ })
+ )
+ case TransactionStatus.DRAFT:
+ return timeAgo + translate('transactionCommon.status.draft')
+ case TransactionStatus.ERROR:
+ return timeAgo + translate('transactionCommon.status.error')
+ case TransactionStatus.PENDING:
+ return timeAgo + translate('transactionCommon.status.pending')
+ case TransactionStatus.PREPARED:
+ return timeAgo + translate('transactionCommon.status.prepared')
+ case TransactionStatus.PREPARED_OFFLINE:
+ if (isInternetReachable) {
+ return timeAgo + translate('transactionCommon.tapToRedeem')
+ } else {
+ return timeAgo + translate('transactionCommon.redeemOnline')
+ }
+ case TransactionStatus.REVERTED:
+ return timeAgo + translate('transactionCommon.status.reverted')
+ case TransactionStatus.BLOCKED:
+ return timeAgo + translate('transactionCommon.status.blocked')
+ case TransactionStatus.EXPIRED:
+ return timeAgo + translate('transactionCommon.status.expired')
+ default:
+ return timeAgo
}
+ }
const getLeftIcon = function(tx: Transaction) {
@@ -183,29 +206,27 @@ export const TransactionListItem = observer(function (props: TransactionListProp
})
+const $item: ViewStyle = {
+ marginHorizontal: spacing.micro,
+}
-
- const $item: ViewStyle = {
- marginHorizontal: spacing.micro,
- }
-
- const $mintText: TextStyle = {
- overflow: 'hidden',
- fontSize: 14,
- }
-
- const $txContainer: ViewStyle = {
- justifyContent: 'center',
- alignSelf: 'center',
- marginRight: spacing.extraSmall
- }
-
- const $txAmount: TextStyle = {
- fontFamily: typography.primary?.medium
- }
-
- const $txIconContainer: ViewStyle = {
- padding: spacing.extraSmall,
- alignSelf: "center",
- marginRight: spacing.medium,
- }
+const $mintText: TextStyle = {
+ overflow: 'hidden',
+ fontSize: 14,
+}
+
+const $txContainer: ViewStyle = {
+ justifyContent: 'center',
+ alignSelf: 'center',
+ marginRight: spacing.extraSmall,
+}
+
+const $txAmount: TextStyle = {
+ fontFamily: typography.primary?.medium,
+}
+
+const $txIconContainer: ViewStyle = {
+ padding: spacing.extraSmall,
+ alignSelf: 'center',
+ marginRight: spacing.medium,
+}
diff --git a/src/screens/Wallet/FeeBadge.tsx b/src/screens/Wallet/FeeBadge.tsx
index 96d36965..5c666705 100644
--- a/src/screens/Wallet/FeeBadge.tsx
+++ b/src/screens/Wallet/FeeBadge.tsx
@@ -1,44 +1,51 @@
-import React from "react"
-import { TextStyle, View, ViewStyle } from "react-native"
-import { Button, Icon, IconTypes, ListItem, Screen, Text } from "../../components"
-import { colors, spacing, typography, useThemeColor } from "../../theme"
-import { CurrencyCode } from "../../services/wallet/currency"
+import React from 'react'
+import {TextStyle, View, ViewStyle} from 'react-native'
+import {Button, Icon, IconTypes, ListItem, Screen, Text} from '../../components'
+import {colors, spacing, typography, useThemeColor} from '../../theme'
+import {translate} from '../../i18n'
+import { CurrencyCode } from '../../services/wallet/currency'
-export const FeeBadge = function(props: {
- currencyCode: CurrencyCode,
- estimatedFee: number,
- finalFee: number,
- containerStyle?: ViewStyle,
- textStyle?: TextStyle,
- }
-) {
-
- const textColor = useThemeColor('header')
- const bgColor = colors.palette.primary200
-
- return (
-
-
-
- )
- }
+export const FeeBadge = function (props: {
+ currencyCode: CurrencyCode
+ estimatedFee: number
+ finalFee: number
+ containerStyle?: ViewStyle
+ textStyle?: TextStyle
+}) {
+ const textColor = useThemeColor('header')
+ const bgColor = colors.palette.primary200
+ return (
+
+
+
+ )
+}
diff --git a/src/services/nostrService.ts b/src/services/nostrService.ts
index 6837c827..b7f33675 100644
--- a/src/services/nostrService.ts
+++ b/src/services/nostrService.ts
@@ -23,6 +23,7 @@ import AppError, { Err } from '../utils/AppError'
import { MinibitsClient } from './minibitsService'
import { rootStoreInstance } from '../models'
import { WalletTask } from './walletService'
+import { WalletTask } from './walletService'
export {
NostrEvent,
@@ -81,6 +82,7 @@ const reconnectToRelays = async function () {
// recreate subscriptions if all relays down
if(relaysStore.connectedCount === 0) {
WalletTask.handleSpentFromPending().catch(e => false)
+ WalletTask.handleSpentFromPending().catch(e => false)
}
const pool = getRelayPool()
@@ -202,6 +204,7 @@ const decryptNip04 = async function(
const publish = async function (
event: NostrUnsignedEvent,
relays: string[],
+): Promise {
): Promise {
const keys: KeyPair = await getOrCreateKeyPair()
@@ -209,11 +212,11 @@ const publish = async function (
const signed = {...event} as NostrEvent
signed.created_at = Math.floor(Date.now() / 1000)
- signed.id = getEventHash(event)
- signed.sig = getSignature(event, keys.privateKey)
+ signed.id = getEventHash(signed)
+ signed.sig = getSignature(signed, keys.privateKey)
- if(!validateEvent(event)) {
- throw new AppError(Err.VALIDATION_ERROR, 'Event is invalid and could not be published', event)
+ if(!validateEvent(signed)) {
+ throw new AppError(Err.VALIDATION_ERROR, 'Event is invalid and could not be published', signed)
}
log.trace('Event to be published', signed, 'publish')