### Start iOS Development Environment
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/dev.md
Commands to start the iOS development environment, including starting the Metro development server and running the application on the iOS simulator. It also includes a TMUX one-liner for a two-pane setup.
```bash
yarn start
yarn ios
```
```bash
tmux new-session -d -s mySession 'yarn start' \; split-window -h 'yarn ios' \; attach-session -d -t mySession
```
--------------------------------
### Start Android Development Environment
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/dev.md
Commands to start the Android development environment, including the Metro development server, the Android emulator, and running the application on the emulator. It also includes a TMUX one-liner for a multi-pane setup.
```bash
yarn start
make emulator
yarn android
```
```bash
tmux new-session -d -s mySession 'yarn start' \; split-window -h 'sleep 3 && yarn android' \; select-pane -t 0 \; split-window -v 'make emulator' \; attach-session -d -t mySession
```
--------------------------------
### Xcode and Simulator Setup using xcodes CLI
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/dev.md
Commands to install specific Xcode versions and iOS simulator runtimes using the 'xcodes' command-line interface. This simplifies the process of ensuring compatibility with the development environment.
```bash
xcodes install 16.4 # Or whatever version direnv tells you about
xcodes runtimes install "iOS 18.5" # Or the latest iOS out there
```
--------------------------------
### TMUX One-Liner for E2E Setup
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
A single tmux command to set up a multi-pane terminal environment for running E2E tests. It includes commands for setting up the local backend, starting the development server, and optionally launching an Android emulator.
```shell
tmux new-session \; \
send-keys 'make tilt-up' C-m \; \
split-window -h \; \
send-keys 'yarn start' C-m \; \
split-window -v \; \
send-keys 'make emulator' C-m \; \
select-pane -t 0 \; \
split-window -v \; \
select-pane -t 3
```
--------------------------------
### Build and Test Android E2E with Detox
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
Commands to build and run E2E tests on Android emulators using Detox. Requires local backend setup and the 'yarn start' process running in the background.
```shell
yarn e2e:build android.emu.debug
yarn e2e:test android.emu.debug
```
--------------------------------
### Allow Direnv to Run Setup Scripts
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/dev.md
Allows Direnv to execute its configuration scripts, which downloads project dependencies, sets up the NodeJS runtime, Android SDKs, creates an AVD, and builds Ruby. It also checks for supported Xcode versions on macOS.
```bash
direnv allow
```
--------------------------------
### Install Node and Cocoapods Dependencies
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/dev.md
Installs project dependencies using Yarn, including Node.js modules and Cocoapods for iOS development. This command should be run after the project has been cloned and Direnv has been allowed.
```bash
yarn install
```
--------------------------------
### Install Appium v2 and Drivers
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
Instructions to install Appium v2 globally using npm and install necessary drivers for Android (uiautomator2) and iOS (xcuitest) automation. Verification command is also included.
```shell
npm install -g appium@next
appium driver install uiautomator2
appium driver install xcuitest
appium --version
```
--------------------------------
### Appium Troubleshooting Command
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
A command to run Appium Doctor, a utility that checks the installed Appium components and their configurations to help diagnose and resolve installation or setup issues.
```shell
yarn appium-doctor
```
--------------------------------
### Build and Test iOS E2E with Detox
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
Commands to build and run E2E tests on iOS simulators using Detox. This process must be performed on a Mac. Requires local backend setup and the 'yarn start' process running in the background.
```shell
yarn e2e:build ios.sim.debug
yarn e2e:test ios.sim.debug
```
--------------------------------
### Clone Blink Mobile Repository
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/dev.md
Clones the Blink Mobile repository to your local machine and navigates into the project directory. This is the initial step for setting up the development environment.
```bash
git clone git@github.com:GaloyMoney/blink-mobile.git
cd blink-mobile
```
--------------------------------
### Local Appium Testing Workflow
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
Steps to perform local E2E testing with Appium. This involves running the debug version of the app, starting the Appium server, and then executing the platform-specific test commands.
```shell
# In one terminal:
yarn android or yarn ios
# In a new terminal:
yarn start:appium
# In another new terminal:
yarn test:e2e:android or yarn test:e2e:ios
```
--------------------------------
### Manually Set Android Test Device
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
Example of manually setting the environment variable for the Android test device and specifying the APK path for E2E testing.
```shell
TEST_DEVICE_ANDROID="Pixel 3 API 29" yarn test:e2e:android
TEST_APK_PATH="./android/app/build/outputs/apk/debug/app-universal-debug.apk"
```
--------------------------------
### Manually Set iOS Test Device
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
Example of manually setting the environment variable for the iOS test device for E2E testing.
```shell
TEST_DEVICE_IOS="iPhone 13" yarn test:e2e:ios
```
--------------------------------
### BrowserStack E2E Testing Setup
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
Environment variables required to configure and run E2E tests on BrowserStack. These variables include user credentials, access key, and the application ID for the app hosted on BrowserStack.
```shell
export BROWSERSTACK_USER=YOURUSER
export BROWSERSTACK_ACCESS_KEY=YOURKEY
export BROWSERSTACK_APP_ID=bs://YOURAPPID
yarn test:browserstack:android
```
--------------------------------
### Required Environment Variables for E2E Testing
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
A list of essential environment variables required for E2E testing, including tokens for authentication, MailSlurp API key, and device type specification. Suggests using direnv for managing these variables.
```shell
GALOY_TEST_TOKENS={YOUR_TOKEN}
GALOY_TOKEN_2={SECOND_WALLET_TOKEN}
MAILSLURP_API_KEY={MAILSLURP_API_API_KEY}
E2E_DEVICE={ios or android}
```
--------------------------------
### Run Specific iOS Tests
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
Command to run specific iOS E2E tests by providing the test name. This method allows running tests without clearing the application state, useful for debugging or focused testing.
```shell
yarn test-ios [test-name]
```
--------------------------------
### Appium Inspector Desired Capabilities for Android
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
JSON configuration for Appium Inspector to connect to an Android emulator. Specifies platform, app path, device name, and automation name.
```json
{
"platformName": "Android",
"appium:app": "/path/to/code/blink-mobile/android/app/build/outputs/apk/debug/app-universal-debug.apk",
"appium:deviceName": "generic_x86",
"appium:automationName": "UiAutomator2"
}
```
--------------------------------
### Appium Inspector Desired Capabilities for iOS
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
JSON configuration for Appium Inspector to connect to an iOS simulator. Specifies platform, device name, bundle ID, and automation name.
```json
{
"platformName": "iOS",
"appium:deviceName": "iPhone 13",
"appium:bundleId": "io.galoy.bitcoinbeach",
"appium:automationName": "XCUITest"
}
```
--------------------------------
### Disable State Reset for Local Development
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
Environment variable setting to disable the state reset feature between tests during local development. This is useful for maintaining application state across multiple test runs.
```shell
export NO_RESET=true
```
--------------------------------
### Appium Inspector Desired Capabilities for iOS on BrowserStack
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
JSON configuration for Appium Inspector to connect to an iOS device on BrowserStack. Includes device name, automation name, platform version, and the app identifier from BrowserStack.
```json
{
"appium:deviceName": "iPhone 13",
"appium:automationName": "XCUITest",
"appium:platformVersion": "15.1",
"appium:app": "bs://{YOUR_BROWSERSTACK_ID_FROM_CIRCLE_CI}"
}
```
--------------------------------
### TypeScript Helper for E2E Test IDs
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/docs/e2e-testing.md
A TypeScript function `testProps` used in E2E tests to assign unique identifiers, accessibility labels, and ensure accessibility for UI components. This helps in reliably locating elements during automated testing.
```typescript
// This is used for E2E tests to apply id's to a
// Usage:
//
export const testProps = (testID: string) => {
return {
testID,
accessible: true,
accessibilityLabel: testID,
}
}
```
--------------------------------
### Real-time Price Conversion Hook using TypeScript
Source: https://context7.com/blinkbitcoin/blink-mobile/llms.txt
This hook fetches real-time price data and provides a function to convert money amounts between Bitcoin (sats), USD cents, and the user's display currency. It handles the complexities of exchange rate calculations and returns the converted amount along with the display currency and USD per sat rate. Dependencies include `@app/graphql/generated` for GraphQL queries and `@app/types/amounts` for currency types.
```typescript
import { useRealtimePriceQuery, WalletCurrency } from "@app/graphql/generated"
import { DisplayCurrency, MoneyAmount } from "@app/types/amounts"
export const SATS_PER_BTC = 100000000
export const usePriceConversion = () => {
const { data } = useRealtimePriceQuery({ skip: !isAuthed })
const displayCurrency = data?.me?.defaultAccount?.realtimePrice?.denominatorCurrency || "USD"
const realtimePrice = data?.me?.defaultAccount?.realtimePrice
let displayCurrencyPerSat = NaN
let displayCurrencyPerCent = NaN
if (realtimePrice) {
displayCurrencyPerSat =
realtimePrice.btcSatPrice.base / 10 ** realtimePrice.btcSatPrice.offset
displayCurrencyPerCent =
realtimePrice.usdCentPrice.base / 10 ** realtimePrice.usdCentPrice.offset
}
const convertMoneyAmount = (
moneyAmount: MoneyAmount,
toCurrency: T,
): MoneyAmount => {
// If already in target currency, return as-is
if (moneyAmount.currency === toCurrency) {
return moneyAmount
}
// Price matrix for conversions
const priceOfCurrencyInCurrency = {
[WalletCurrency.Btc]: {
[DisplayCurrency]: displayCurrencyPerSat,
[WalletCurrency.Usd]: displayCurrencyPerSat * (1 / displayCurrencyPerCent),
[WalletCurrency.Btc]: 1,
},
[WalletCurrency.Usd]: {
[DisplayCurrency]: displayCurrencyPerCent,
[WalletCurrency.Btc]: displayCurrencyPerCent * (1 / displayCurrencyPerSat),
[WalletCurrency.Usd]: 1,
},
[DisplayCurrency]: {
[WalletCurrency.Btc]: 1 / displayCurrencyPerSat,
[WalletCurrency.Usd]: 1 / displayCurrencyPerCent,
[DisplayCurrency]: 1,
},
}
const conversionRate = priceOfCurrencyInCurrency[moneyAmount.currency][toCurrency]
const amount = Math.round(moneyAmount.amount * conversionRate)
return {
amount,
currency: toCurrency,
currencyCode: toCurrency === DisplayCurrency ? displayCurrency : toCurrency,
}
}
const usdPerSat = displayCurrencyPerSat && displayCurrencyPerCent
? (displayCurrencyPerSat / displayCurrencyPerCent / 100).toFixed(8)
: null
return {
convertMoneyAmount,
displayCurrency,
usdPerSat,
}
}
// Example usage
const PaymentComponent = () => {
const { convertMoneyAmount, usdPerSat } = usePriceConversion()
const satoshiAmount = {
amount: 100000, // 100,000 sats
currency: WalletCurrency.Btc,
currencyCode: "BTC",
}
const usdAmount = convertMoneyAmount(satoshiAmount, WalletCurrency.Usd)
console.log(`100,000 sats = ${usdAmount.amount} USD cents`)
console.log(`Current rate: ${usdPerSat} USD per sat`)
// Convert USD to BTC
const usdToConvert = {
amount: 500, // $5.00 (500 cents)
currency: WalletCurrency.Usd,
currencyCode: "USD",
}
const btcAmount = convertMoneyAmount(usdToConvert, WalletCurrency.Btc)
console.log(`$5.00 = ${btcAmount.amount} satoshis`)
return (
Exchange Rate: 1 sat = ${usdPerSat}
{satoshiAmount.amount} sats = ${(usdAmount.amount / 100).toFixed(2)}
)
}
```
--------------------------------
### Update Translations with Yarn
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/app/i18n/README.md
This command updates all translation files for the project. Ensure you have run this after making changes to the default language strings.
```bash
yarn update-translations
```
--------------------------------
### Configure Galoy Instance and Manage Auth Tokens in TypeScript
Source: https://context7.com/blinkbitcoin/blink-mobile/llms.txt
Manages the configuration for the Galoy backend instance and handles authentication tokens. It uses persistent state to store the token and instance details, providing functions to set the instance, save the token, and save both simultaneously. This hook is crucial for initializing API connections and maintaining user sessions.
```typescript
import { usePersistentStateContext } from "@app/store/persistent-state"
import { GaloyInstance, resolveGaloyInstanceOrDefault } from "@app/config"
export const useAppConfig = () => {
const { persistentState, updateState } = usePersistentStateContext()
const appConfig = {
token: persistentState.galoyAuthToken,
galoyInstance: resolveGaloyInstanceOrDefault(persistentState.galoyInstance),
}
const setGaloyInstance = (newInstance: GaloyInstance) => {
updateState((state) => {
if (state) {
return {
...state,
galoyInstance: newInstance,
}
}
return undefined
})
}
const saveToken = async (token: string) => {
updateState((state) => {
if (state) {
return {
...state,
galoyAuthToken: token,
}
}
return undefined
})
}
const saveTokenAndInstance = async ({
token,
instance,
}: {
token: string
instance: GaloyInstance
}) => {
updateState((state) => {
if (state) {
return {
...state,
galoyInstance: instance,
galoyAuthToken: token,
}
}
return undefined
})
}
return {
appConfig,
setGaloyInstance,
saveToken,
saveTokenAndInstance,
}
}
// Example usage
const LoginScreen = () => {
const { saveTokenAndInstance } = useAppConfig()
const handleLogin = async (credentials) => {
const response = await loginAPI(credentials)
if (response.success) {
await saveTokenAndInstance({
token: response.authToken,
instance: {
name: "Production",
graphqlUri: "https://api.galoy.io/graphql",
graphqlWsUri: "wss://api.galoy.io/graphql",
posUrl: "https://pay.galoy.io",
lnAddressHostname: "galoy.io",
},
})
navigation.navigate("Home")
}
}
return
}
// Switching between instances (e.g., staging/production)
const SettingsScreen = () => {
const { setGaloyInstance, appConfig } = useAppConfig()
const switchToStaging = () => {
setGaloyInstance({
name: "Staging",
graphqlUri: "https://api.staging.galoy.io/graphql",
graphqlWsUri: "wss://api.staging.galoy.io/graphql",
posUrl: "https://pay.staging.galoy.io",
lnAddressHostname: "staging.galoy.io",
})
}
return (
Current Instance: {appConfig.galoyInstance.name}
)
}
```
--------------------------------
### Create Bitcoin Payment Requests (TypeScript)
Source: https://context7.com/blinkbitcoin/blink-mobile/llms.txt
Provides functions to create Lightning invoices (both with and without a specified amount) and to generate on-chain Bitcoin addresses. It utilizes GraphQL mutations defined using Apollo Client. The functions return success status, relevant payment details (like paymentRequest or address), and potential errors. Dependencies include Apollo Client and generated GraphQL hooks.
```typescript
import { gql } from "@apollo/client"
import {
useLnInvoiceCreateMutation,
useLnNoAmountInvoiceCreateMutation,
useOnChainAddressCurrentMutation,
WalletCurrency
} from "@app/graphql/generated"
// Define GraphQL mutations
gql`
mutation lnInvoiceCreate($input: LnInvoiceCreateInput!) {
lnInvoiceCreate(input: $input) {
errors {
message
}
invoice {
createdAt
paymentHash
paymentRequest
paymentStatus
satoshis
}
}
}
mutation lnNoAmountInvoiceCreate($input: LnNoAmountInvoiceCreateInput!) {
lnNoAmountInvoiceCreate(input: $input) {
errors {
message
}
invoice {
createdAt
paymentHash
paymentRequest
paymentStatus
}
}
}
mutation onChainAddressCurrent($input: OnChainAddressCurrentInput!) {
onChainAddressCurrent(input: $input) {
errors {
message
}
address
}
}
`
export const useReceiveBitcoin = () => {
const [lnInvoiceCreate] = useLnInvoiceCreateMutation()
const [lnNoAmountInvoiceCreate] = useLnNoAmountInvoiceCreateMutation()
const [onChainAddressCurrent] = useOnChainAddressCurrentMutation()
const createLightningInvoice = async ({
walletId,
amount,
memo,
}: {
walletId: string
amount: number
memo?: string
}) => {
const result = await lnInvoiceCreate({
variables: {
input: {
walletId,
amount,
memo,
},
},
})
if (result.data?.lnInvoiceCreate.invoice) {
const invoice = result.data.lnInvoiceCreate.invoice
return {
success: true,
paymentRequest: invoice.paymentRequest,
paymentHash: invoice.paymentHash,
expiresAt: new Date(invoice.createdAt).getTime() + 24 * 60 * 60 * 1000,
}
}
return {
success: false,
errors: result.data?.lnInvoiceCreate.errors,
}
}
const createFlexibleInvoice = async (walletId: string) => {
const result = await lnNoAmountInvoiceCreate({
variables: {
input: { walletId },
},
})
return {
success: !!result.data?.lnNoAmountInvoiceCreate.invoice,
paymentRequest: result.data?.lnNoAmountInvoiceCreate.invoice?.paymentRequest,
}
}
const getOnChainAddress = async (walletId: string) => {
const result = await onChainAddressCurrent({
variables: {
input: { walletId },
},
})
return {
success: !!result.data?.onChainAddressCurrent.address,
address: result.data?.onChainAddressCurrent.address,
}
}
return { createLightningInvoice, createFlexibleInvoice, getOnChainAddress }
}
// Example usage
import { useState } from "react"
import { View, Button } from "react-native"
import QRCode from "react-native-qrcode-svg"
const ReceiveScreen = () => {
const { createLightningInvoice, getOnChainAddress } = useReceiveBitcoin()
const [invoice, setInvoice] = useState(null)
const generateInvoice = async () => {
const result = await createLightningInvoice({
walletId: "btc-wallet-id",
amount: 50000, // satoshis
memo: "Payment for services",
})
if (result.success) {
setInvoice(result.paymentRequest)
// Display QR code with invoice
}
}
const generateAddress = async () => {
const result = await getOnChainAddress("btc-wallet-id")
if (result.success) {
console.log("Bitcoin address:", result.address)
}
}
return (
{invoice && }
)
}
```
--------------------------------
### Configure Apollo GraphQL Client with WebSocket in TypeScript
Source: https://context7.com/blinkbitcoin/blink-mobile/llms.txt
This snippet configures an Apollo GraphQL client for a React Native application, including WebSocket support for subscriptions, authentication, error handling, and retry logic. It utilizes Apollo Client v3 with libraries like graphql-ws and apollo3-cache-persist for robust API communication and state management.
```typescript
import { ApolloClient, ApolloProvider, createHttpLink } from "@apollo/client"
import { setContext } from "@apollo/client/link/context"
import { onError } from "@apollo/client/link/error"
import { RetryLink } from "@apollo/client/link/retry"
import { GraphQLWsLink } from "@apollo/client/link/subscriptions"
import { createClient } from "graphql-ws"
import { createCache } from "./cache"
import { CachePersistor } from "apollo3-cache-persist"
import AsyncStorage from "@react-native-async-storage/async-storage"
import { split, HttpLink, ApolloLink } from "@apollo/client/link/core"
import { getMainDefinition } from "@apollo/client/utilities"
// Mock getAppCheckToken for example purposes
const getAppCheckToken = async () => "mock-appcheck-token"
// Mock AsyncStorageWrapper for example purposes
class AsyncStorageWrapper {
constructor(storage) {
this.storage = storage
}
getItem(key) {
return this.storage.getItem(key)
}
setItem(key, value) {
return this.storage.setItem(key, value)
}
removeItem(key) {
return this.storage.removeItem(key)
}
}
// Configure WebSocket link for subscriptions
const wsLink = new GraphQLWsLink(
createClient({
url: "wss://api.example.com/graphql",
retryAttempts: 12,
connectionParams: async () => {
const token = "your-auth-token"
const appCheckToken = await getAppCheckToken()
return {
Authorization: token ? `Bearer ${token}` : undefined,
Appcheck: appCheckToken,
}
},
shouldRetry: (errOrCloseEvent) => {
console.warn("WebSocket retry triggered", errOrCloseEvent)
return true // Always attempt reconnection
},
})
)
// Configure error handling
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path }) => {
console.warn(`[GraphQL error]: Message: ${message}, Path: ${path}`)
})
}
if (networkError) {
console.log(`[Network error]: ${networkError}`)
// Handle network errors globally
}
})
// Configure retry logic with exponential backoff
const retryLink = new RetryLink({
attempts: {
max: 5,
retryIf: (error, operation) => {
const noRetryOperations = [
"intraLedgerPaymentSend",
"lnInvoicePaymentSend",
"onChainPaymentSend"
]
return (
Boolean(error) &&
!noRetryOperations.includes(operation.operationName) &&
error.statusCode !== 401
)
},
},
})
// Setup authentication link
const authLink = setContext((request, { headers }) => ({
headers: {
authorization: "Bearer your-auth-token",
...headers,
},
}))
// HTTP link for queries and mutations
const httpLink = createHttpLink({
uri: "https://api.example.com/graphql",
})
// Split between WebSocket (subscriptions) and HTTP (queries/mutations)
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
)
},
wsLink,
ApolloLink.from([errorLink, retryLink, authLink, httpLink])
)
// Create cache with persistence
const cache = createCache()
const persistor = new CachePersistor({
cache,
storage: new AsyncStorageWrapper(AsyncStorage),
debug: __DEV__,
})
// Initialize Apollo Client
const client = new ApolloClient({
cache,
link: splitLink,
name: "iOS",
version: "1.0.1",
connectToDevTools: true,
})
// Restore persisted cache
// await persistor.restore() // Uncomment in actual app context
// Usage in app (example)
// const App = () => (
//
//
//
// )
```
--------------------------------
### Add New Language: Import FormatJS Locale Data
Source: https://github.com/blinkbitcoin/blink-mobile/blob/main/app/i18n/README.md
When adding a new language, you need to import the relative time format locale data from the @formatjs/intl-relativetimeformat package. This ensures that relative time expressions are correctly formatted for the new language.
```typescript
import "@formatjs/intl-relativetimeformat/locale-data/af"
```
--------------------------------
### Send Bitcoin Payments using TypeScript
Source: https://context7.com/blinkbitcoin/blink-mobile/llms.txt
This TypeScript code defines GraphQL mutations for sending various types of Bitcoin payments and implements a hook to manage these operations. It supports Lightning Network invoices, on-chain transactions, and intra-ledger transfers, handling success and error responses. Dependencies include @apollo/client and uuid.
```typescript
import { gql } from "@apollo/client"
import {
useIntraLedgerPaymentSendMutation,
useLnInvoicePaymentSendMutation,
useOnChainPaymentSendMutation,
PaymentSendResult
} from "@app/graphql/generated"
import { v4 as uuidv4 } from "uuid"
// Define GraphQL mutations
gql`
mutation lnInvoicePaymentSend($input: LnInvoicePaymentInput!) {
lnInvoicePaymentSend(input: $input) {
errors {
message
}
status
transaction {
settlementVia {
... on SettlementViaLn {
preImage
}
}
}
}
}
mutation onChainPaymentSend($input: OnChainPaymentSendInput!) {
onChainPaymentSend(input: $input) {
transaction {
settlementVia {
... on SettlementViaOnChain {
arrivalInMempoolEstimatedAt
}
}
}
errors {
message
}
status
}
}
mutation intraLedgerPaymentSend($input: IntraLedgerPaymentSendInput!) {
intraLedgerPaymentSend(input: $input) {
errors {
message
}
status
}
}
`
// Hook implementation
export const useSendPayment = () => {
const idempotencyKey = uuidv4()
const options = {
refetchQueries: ["HomeAuthed"],
context: { headers: { "X-Idempotency-Key": idempotencyKey } },
}
const [lnInvoicePaymentSend] = useLnInvoicePaymentSendMutation(options)
const [onChainPaymentSend] = useOnChainPaymentSendMutation(options)
const [intraLedgerPaymentSend] = useIntraLedgerPaymentSendMutation(options)
const sendLightningPayment = async ({
walletId,
paymentRequest,
memo,
}: {
walletId: string
paymentRequest: string
memo?: string
}) => {
const result = await lnInvoicePaymentSend({
variables: {
input: {
walletId,
paymentRequest,
memo,
},
},
})
if (result.data?.lnInvoicePaymentSend.status === PaymentSendResult.Success) {
console.log("Payment successful!")
return { success: true }
} else {
const errors = result.data?.lnInvoicePaymentSend.errors
console.error("Payment failed:", errors)
return { success: false, errors }
}
}
const sendOnChainPayment = async ({
walletId,
address,
amount,
memo,
}: {
walletId: string
address: string
amount: number
memo?: string
}) => {
const result = await onChainPaymentSend({
variables: {
input: {
walletId,
address,
amount,
memo,
},
},
})
return {
success: result.data?.onChainPaymentSend.status === PaymentSendResult.Success,
errors: result.data?.onChainPaymentSend.errors,
}
}
return { sendLightningPayment, sendOnChainPayment }
}
// Example usage
const PaymentScreen = () => {
const { sendLightningPayment } = useSendPayment()
const handlePayment = async () => {
const result = await sendLightningPayment({
walletId: "wallet-id-123",
paymentRequest: "lnbc1000n1...",
memo: "Payment for coffee",
})
if (result.success) {
Alert.alert("Success", "Payment sent!")
}
}
return
}
```
--------------------------------
### User Logout Functionality with Token Management (TypeScript)
Source: https://context7.com/blinkbitcoin/blink-mobile/llms.txt
Implements a user logout hook that handles secure removal of authentication tokens and resets application state. It supports logging out a specific session using a token or performing a full logout by clearing all local storage and secure storage data. Includes error handling and optional state resetting.
```typescript
import { gql } from "@apollo/client"
import { useUserLogoutMutation } from "@app/graphql/generated"
import { usePersistentStateContext } from "@app/store/persistent-state"
import AsyncStorage from "@react-native-async-storage/async-storage"
import messaging from "@react-native-firebase/messaging"
import KeyStoreWrapper from "@app/utils/storage/secureStorage"
gql`
mutation userLogout($input: UserLogoutInput!) {
userLogout(input: $input) {
success
}
}
`
const useLogout = () => {
const { resetState } = usePersistentStateContext()
const [userLogoutMutation] = useUserLogoutMutation({
fetchPolicy: "no-cache",
})
const logout = async ({
stateToDefault = true,
token,
isValidToken = true,
}: {
stateToDefault?: boolean
token?: string
isValidToken?: boolean
} = {}) => {
try {
const deviceToken = await messaging().getToken()
if (token) {
// Logout specific session
await KeyStoreWrapper.removeSessionProfileByToken(token)
if (isValidToken) {
await Promise.race([
userLogoutMutation({
context: { headers: { authorization: `Bearer ${token}` } },
variables: { input: { deviceToken } },
}),
// Timeout after 2 seconds
new Promise((_, reject) => {
setTimeout(() => reject(new Error("Logout timeout")), 2000)
}),
])
}
} else {
// Full logout - clear all data
await AsyncStorage.multiRemove(["SCHEMA_VERSION_KEY"])
await KeyStoreWrapper.removeIsBiometricsEnabled()
await KeyStoreWrapper.removePin()
await KeyStoreWrapper.removePinAttempts()
await KeyStoreWrapper.removeSessionProfiles()
}
} catch (err) {
console.error("Logout error:", err)
crashlytics().recordError(err)
} finally {
if (stateToDefault) {
resetState()
}
}
}
return { logout }
}
// Example usage
const LogoutButton = () => {
const { logout } = useLogout()
const handleLogout = async () => {
Alert.alert(
"Confirm Logout",
"Are you sure you want to logout?",
[
{ text: "Cancel", style: "cancel" },
{
text: "Logout",
onPress: async () => {
await logout()
navigation.navigate("Login")
},
},
]
)
}
return
}
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.