Skip to content

Commit

Permalink
Reusable (#66)
Browse files Browse the repository at this point in the history
* create initial modal component

* add reusable Separator component
  • Loading branch information
KKA11010 authored Jun 17, 2023
1 parent beea76d commit 6ad04a9
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 95 deletions.
39 changes: 39 additions & 0 deletions src/components/InitialModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import MyModal from '@modal'
import { ThemeContext } from '@src/context/Theme'
import { globals } from '@styles'
import { useContext } from 'react'
import { StyleSheet, Text, TouchableOpacity } from 'react-native'

import Button from './Button'

interface IInitialModalProps {
visible: boolean
onConfirm: () => void
onCancel: () => void
}

export default function InitialModal({ visible, onConfirm, onCancel }: IInitialModalProps) {
const { color, highlight } = useContext(ThemeContext)
return (
<MyModal type='bottom' animation='slide' visible={visible}>
<Text style={globals(color, highlight).modalHeader}>
Get started
</Text>
<Text style={globals(color, highlight).modalTxt}>
You should add a mint that you trust before sending or receiving tokens.
</Text>
<Button txt='Add a mint now' onPress={onConfirm} />
<TouchableOpacity onPress={onCancel}>
<Text style={[globals(color, highlight).pressTxt, styles.cancel]}>
Will do later
</Text>
</TouchableOpacity>
</MyModal>
)
}

const styles = StyleSheet.create({
cancel: {
marginTop: 25,
},
})
14 changes: 14 additions & 0 deletions src/components/Separator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ThemeContext } from '@src/context/Theme'
import { useContext } from 'react'
import { type StyleProp, StyleSheet, type TextStyle, View } from 'react-native'

export default function Separator({ style }: { style?: StyleProp<TextStyle>[] }) {
const { color } = useContext(ThemeContext)
return <View style={[styles.separator, { borderColor: color.BORDER }, ...(style || [])]} />
}

const styles = StyleSheet.create({
separator: {
borderBottomWidth: 1
}
})
3 changes: 2 additions & 1 deletion src/components/pages/Addressbook/Book.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Button from '@comps/Button'
import usePrompt from '@comps/hooks/Prompt'
import { PlusIcon, UserIcon } from '@comps/Icons'
import Separator from '@comps/Separator'
import Txt from '@comps/Txt'
import { addContact, getContacts } from '@db'
import MyModal from '@modal'
Expand Down Expand Up @@ -159,7 +160,7 @@ export default function AddressBook({ nav, isModal, closeModal, setInput }: IAdd
<Txt txt={c.name} />
</TouchableOpacity>
</View>
{i < contacts.length - 1 && <View style={[styles.separator, { borderBottomColor: color.BORDER }]} />}
{i < contacts.length - 1 && <Separator style={[{ marginLeft: 60 }]} />}
</View>
))}
</View>
Expand Down
111 changes: 47 additions & 64 deletions src/components/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
import ActionButtons from '@comps/ActionButtons'
import Balance from '@comps/Balance'
import Button from '@comps/Button'
import useLoading from '@comps/hooks/Loading'
import usePrompt from '@comps/hooks/Prompt'
import useCashuToken from '@comps/hooks/Token'
import InitialModal from '@comps/InitialModal'
import Toaster from '@comps/Toaster'
import { addMint, getBalance, getMintsUrls, hasMints } from '@db'
import { l } from '@log'
import MyModal from '@modal'
import OptsModal from '@modal/OptsModal'
import TrustMintModal from '@modal/TrustMint'
import { TDashboardPageProps } from '@model/nav'
Expand All @@ -18,19 +17,18 @@ import { FocusClaimCtx } from '@src/context/FocusClaim'
import { useInitialURL } from '@src/context/Linking'
import { ThemeContext } from '@src/context/Theme'
import { addToHistory } from '@store/HistoryStore'
import { globals, highlight as hi } from '@styles'
import { hasTrustedMint, isCashuToken } from '@util'
import { claimToken } from '@wallet'
import { getTokenInfo } from '@wallet/proofs'
import * as Clipboard from 'expo-clipboard'
import { useContext, useEffect, useState } from 'react'
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'
import { StyleSheet, View } from 'react-native'

export default function Dashboard({ navigation, route }: TDashboardPageProps) {
// The URL content that redirects to this app after clicking on it (cashu:)
const { url } = useInitialURL()
// Theme
const { color, highlight } = useContext(ThemeContext)
const { color } = useContext(ThemeContext)
// State to indicate token claim from clipboard after app comes to the foreground, to re-render total balance
const { claimed } = useContext(FocusClaimCtx)
// Total Balance state (all mints)
Expand Down Expand Up @@ -189,67 +187,52 @@ export default function Dashboard({ navigation, route }: TDashboardPageProps) {
/>
}
{/* Initial mint modal prompt */}
{modal.mint &&
<MyModal type='bottom' animation='slide' visible={modal.mint}>
<Text style={globals(color, highlight).modalHeader}>
Get started
</Text>
<Text style={globals(color, highlight).modalTxt}>
You should add a mint that you trust before sending or receiving tokens.
</Text>
<Button txt='Add a mint now' onPress={handleMintModal} />
<TouchableOpacity onPress={() => setModal({ ...modal, mint: false })}>
<Text style={[styles.cancel, { color: hi[highlight] }]}>
Will do later
</Text>
</TouchableOpacity>
</MyModal>
}
<InitialModal
visible={modal.mint}
onConfirm={handleMintModal}
onCancel={() => setModal({ ...modal, mint: false })}
/>
{/* Receive options */}
{modal.receiveOpts &&
<OptsModal
visible={modal.receiveOpts}
button1Txt={loading ? 'claiming...' : 'Paste & redeem Ecash'}
onPressFirstBtn={() => {
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)
await handleTokenSubmit(clipboard)
})()
}}
button2Txt='Create Lightning invoice'
onPressSecondBtn={() => {
navigation.navigate('lightning', { receive: true })
setModal({ ...modal, receiveOpts: false })
}}
onPressCancel={() => setModal({ ...modal, receiveOpts: false })}
/>
}
<OptsModal
visible={modal.receiveOpts}
button1Txt={loading ? 'claiming...' : 'Paste & redeem Ecash'}
onPressFirstBtn={() => {
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)
await handleTokenSubmit(clipboard)
})()
}}
button2Txt='Create Lightning invoice'
onPressSecondBtn={() => {
navigation.navigate('lightning', { receive: true })
setModal({ ...modal, receiveOpts: false })
}}
onPressCancel={() => setModal({ ...modal, receiveOpts: false })}
/>
{/* Send options */}
{modal.sendOpts &&
<OptsModal
visible={modal.sendOpts}
button1Txt='Send Ecash'
onPressFirstBtn={() => {
navigation.navigate('send')
setModal({ ...modal, sendOpts: false })
}}
button2Txt='Pay Lightning invoice'
onPressSecondBtn={() => {
navigation.navigate('lightning', { send: true })
setModal({ ...modal, sendOpts: false })
}}
onPressCancel={() => setModal({ ...modal, sendOpts: false })}
/>
}
<OptsModal
visible={modal.sendOpts}
button1Txt='Send Ecash'
onPressFirstBtn={() => {
navigation.navigate('send')
setModal({ ...modal, sendOpts: false })
}}
button2Txt='Pay Lightning invoice'
onPressSecondBtn={() => {
navigation.navigate('lightning', { send: true })
setModal({ ...modal, sendOpts: false })
}}
onPressCancel={() => setModal({ ...modal, sendOpts: false })}
/>
{/* Prompt toaster */}
{prompt.open && <Toaster success={prompt.success} txt={prompt.msg} />}
</View>
Expand Down
17 changes: 9 additions & 8 deletions src/components/pages/History/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ 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 Separator from '@comps/Separator'
import Txt from '@comps/Txt'
import type { THistoryEntryPageProps } from '@model/nav'
import TopNav from '@nav/TopNav'
Expand Down Expand Up @@ -90,7 +91,7 @@ export default function DetailsPage({ route }: THistoryEntryPageProps) {
<Txt txt='Settle Time' />
<Txt txt={new Date(entry.timestamp * 1000).toLocaleString()} />
</View>
<View style={[styles.separator, { borderColor: color.BORDER }]} />
<Separator />
{/* Memo */}
<View style={styles.entryInfo}>
<Txt txt='Memo' />
Expand All @@ -99,14 +100,14 @@ export default function DetailsPage({ route }: THistoryEntryPageProps) {
styles={[styles.infoValue]}
/>
</View>
<View style={[styles.separator, { borderColor: color.BORDER }]} />
<Separator />
{/* Mints */}
{/* TODO update style to fit multiple mints */}
<View style={styles.entryInfo}>
<Txt txt={isLn ? 'Mint' : 'Mints'} />
<Txt txt={entry.mints.map(m => formatMintUrl(m)).join(', ')} />
</View>
<View style={[styles.separator, { borderColor: color.BORDER }]} />
<Separator />
{/* cashu token or ln invoice */}
<TouchableOpacity
style={styles.entryInfo}
Expand All @@ -132,7 +133,7 @@ export default function DetailsPage({ route }: THistoryEntryPageProps) {
}
</View>
</TouchableOpacity>
<View style={[styles.separator, { borderColor: color.BORDER }]} />
<Separator />
{/* check is token spendable */}
{isPayment && !isLn &&
<IsSpentContainer
Expand All @@ -152,7 +153,7 @@ export default function DetailsPage({ route }: THistoryEntryPageProps) {
}
</IsSpentContainer>
}
<View style={[styles.separator, { borderColor: color.BORDER }]} />
<Separator />
{/* Lightning related */}
{isLn &&
<>
Expand Down Expand Up @@ -181,7 +182,7 @@ export default function DetailsPage({ route }: THistoryEntryPageProps) {
}
</View>
</TouchableOpacity>
<View style={[styles.separator, { borderColor: color.BORDER }]} />
<Separator />
{/* LN payment preImage */}
<TouchableOpacity
style={styles.entryInfo}
Expand All @@ -207,13 +208,13 @@ export default function DetailsPage({ route }: THistoryEntryPageProps) {
}
</View>
</TouchableOpacity>
<View style={[styles.separator, { borderColor: color.BORDER }]} />
<Separator />
{/* LN payment fees */}
<View style={styles.entryInfo}>
<Txt txt='Fee' />
<Txt txt={entry.fee ? `${entry.fee} Satoshi` : 'Not available'} />
</View>
<View style={[styles.separator, { borderColor: color.BORDER }]} />
<Separator />
</>
}
{/* QR code */}
Expand Down
3 changes: 2 additions & 1 deletion src/components/pages/History/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Separator from '@comps/Separator'
import Txt from '@comps/Txt'
import type { IHistoryEntry } from '@model'
import type { THistoryPageProps } from '@model/nav'
Expand Down Expand Up @@ -69,7 +70,7 @@ export default function HistoryPage({ navigation, route }: THistoryPageProps) {
/>
)}
estimatedItemSize={300}
ItemSeparatorComponent={() => <View style={[styles.separator, { borderColor: color.BORDER }]} />}
ItemSeparatorComponent={() => <Separator />}
/>
</View>
</>
Expand Down
5 changes: 3 additions & 2 deletions src/components/pages/Mints/Info.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { GetInfoResponse } from '@cashu/cashu-ts'
import { ExclamationIcon, MintBoardIcon } from '@comps/Icons'
import Separator from '@comps/Separator'
import Txt from '@comps/Txt'
import { l } from '@log'
import type { TMintInfoPageProps } from '@model/nav'
Expand Down Expand Up @@ -82,12 +83,12 @@ export default function MintInfoPage({ route }: TMintInfoPageProps) {
}
</View>
))}
<View style={[styles.separator, { borderBottomColor: color.BORDER }]} />
<Separator style={[{ marginVertical: 20 }]} />
<Text style={[styles.description, { color: color.TEXT }]}>
Supported NUTs
</Text>
{info.nuts.map((n, i) => <Txt key={i} txt={n} />)}
<View style={[styles.separator, { borderBottomColor: color.BORDER }]} />
<Separator style={[{ marginVertical: 20 }]} />
<Text style={[styles.description, { color: color.TEXT }]}>
Public key
</Text>
Expand Down
41 changes: 25 additions & 16 deletions src/components/pages/Settings/Display.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Separator from '@comps/Separator'
import Txt from '@comps/Txt'
import type { TDisplaySettingsPageProps } from '@model/nav'
import TopNav from '@nav/TopNav'
import { ThemeContext } from '@src/context/Theme'
Expand Down Expand Up @@ -31,8 +33,13 @@ export default function DisplaySettings({ navigation }: TDisplaySettingsPageProp
<Text style={[styles.subHeader, { marginBottom: 20, color: color.TEXT }]}>
Theme
</Text>
{themeColors.map(t => (
<ThemeSelection key={t} name={t} selected={t === highlight} />
{themeColors.map((t, i) => (
<ThemeSelection
key={t}
name={t}
selected={t === highlight}
hasSeparator={i !== themeColors.length - 1}
/>
))}
<View style={[styles.separator, { marginTop: 10, borderBottomColor: color.BORDER }]} />
</View>
Expand All @@ -42,24 +49,26 @@ export default function DisplaySettings({ navigation }: TDisplaySettingsPageProp
interface IThemeSelectionProps {
name: string
selected: boolean
hasSeparator?: boolean
}

function ThemeSelection({ name, selected }: IThemeSelectionProps) {
function ThemeSelection({ name, selected, hasSeparator }: IThemeSelectionProps) {
const { color, highlight, setHighlight } = useContext(ThemeContext)
return (
<TouchableOpacity style={styles.settingsRow}
onPress={() => setHighlight(name)}
>
<Text style={globals(color).txt}>
{name}
</Text>
<View
style={[
styles.radioBtn,
{ borderColor: color.BORDER, backgroundColor: selected ? hi[highlight] : 'transparent' }
]}
/>
</TouchableOpacity>
<>
<TouchableOpacity style={styles.settingsRow}
onPress={() => setHighlight(name)}
>
<Txt txt={name} />
<View
style={[
globals(color, highlight).radioBtn,
{ backgroundColor: selected ? hi[highlight] : 'transparent' }
]}
/>
</TouchableOpacity>
{hasSeparator && <Separator style={[{ marginHorizontal: 20, marginVertical: 10 }]} />}
</>
)
}

Expand Down
Loading

0 comments on commit 6ad04a9

Please sign in to comment.