Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9a70347
improved dataflow
X-Hades-X Sep 12, 2024
70c8f00
wallet mode
X-Hades-X Sep 12, 2024
e679ce6
cleanup and linting
X-Hades-X Nov 4, 2024
7ad67b6
show invoice QR
X-Hades-X Nov 12, 2024
c14b110
use wallet to set amount on bitcoin address without amount
X-Hades-X Nov 12, 2024
61eba3e
use Invoice for lnurlW and lnurlP
X-Hades-X Nov 12, 2024
4795b6a
add Decoder screen to choose what to do with the scanned LN URL/Addre…
X-Hades-X Nov 13, 2024
2187b42
Styling; problem with picker
X-Hades-X Mar 19, 2025
ecc5496
Update styled.ts
X-Hades-X Mar 19, 2025
dcf41f1
fix issues with LNURLw with defined amount
X-Hades-X Mar 27, 2025
229f2d0
Merge branch 'main' of https://github.com/X-Hades-X/boltcard-tools-te…
X-Hades-X May 22, 2025
4b36d35
make it look a bit nicer
X-Hades-X May 22, 2025
4d51ae4
one less container
X-Hades-X May 22, 2025
ad0c345
change on button not needed since round button is used
X-Hades-X May 22, 2025
9f47165
nicer look for decoder and check limits in wallet
X-Hades-X May 28, 2025
fd088e1
fix auto redirect; use list items in new screens
X-Hades-X May 29, 2025
af6e9d1
refactor style a bit to use same table layout as Invoice
X-Hades-X May 29, 2025
1b971d7
fix handling of user navigation in Invoice
X-Hades-X May 29, 2025
2feb76c
bitcoin address without amount goes directly to wallet
X-Hades-X May 29, 2025
f718454
cleanup and fix decoder; add todo for invoices without amount
X-Hades-X Jun 30, 2025
b1b6df8
show error for invoices without amount
X-Hades-X Jun 30, 2025
4e0b630
support http url with lightning param
X-Hades-X Jul 2, 2025
1d958cf
create CurrencySelect.tsx and revert regular SelectField.tsx; store s…
X-Hades-X Jul 2, 2025
08137c2
move more things into useRates; also calculate fiat when scanning/loa…
X-Hades-X Jul 2, 2025
c82404a
move more things into useRates
X-Hades-X Jul 2, 2025
8b9b29f
add loading indicator
X-Hades-X Jul 2, 2025
dde0e43
fixed loading indicator
X-Hades-X Jul 2, 2025
da49886
Merge remote-tracking branch 'origin/develop' into develop
X-Hades-X Jul 2, 2025
b6e10d2
no needed anymore
X-Hades-X Jul 2, 2025
fbf2c82
cleanup and fixup
X-Hades-X Jul 9, 2025
bb6f2dc
fix update of currency in wallet screen
X-Hades-X Aug 12, 2025
b3fb6b3
spec defines mSat on pinLimit
X-Hades-X Sep 4, 2025
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
34 changes: 34 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@fortawesome/react-fontawesome": "0.2.0",
"@fortawesome/react-native-fontawesome": "0.3.0",
"@os-team/i18next-react-native-language-detector": "1.0.29",
"@react-native-async-storage/async-storage": "^2.2.0",
"@react-native-clipboard/clipboard": "1.13.2",
"@react-native-community/checkbox": "0.5.16",
"@react-native-picker/picker": "2.6.1",
Expand Down
4 changes: 3 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Routes, Route } from "@components/Router";
import { StatusBar } from "@components";
import { Home, QRScanner, Invoice } from "@screens";
import { Home, QRScanner, Invoice, Wallet, Decoder } from "@screens";
import { useBackHandler, useDeepLink, useSplashScreen } from "@hooks";

const App = () => {
Expand All @@ -19,6 +19,8 @@ const App = () => {
<Route path="/" element={<Home />} />
<Route path="qr-scanner" element={<QRScanner />} />
<Route path="invoice" element={<Invoice />} />
<Route path="wallet" element={<Wallet />} />
<Route path="decoder" element={<Decoder />} />
</Routes>
</>
);
Expand Down
50 changes: 47 additions & 3 deletions src/assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,26 @@
"scanInvoice": "Scan invoice",
"nfcNotSupported": "NFC not supported",
"moreAboutBoltCard": "More about BoltCard",
"defaultCurrencyLabel": "Default currency",
"builtBy": "Built by",
"foundInvoiceClipboard": "Tap to read invoice from your clipboard"
"walletMode": "Scan Bolt Card",
"foundInvoiceClipboard": "Tap to read invoice from your clipboard",
"foundLnAddrClipboard": "Tap to load LN Address from your clipboard",
"foundLnurlClipboard": "Tap to load LNURL from your clipboard"
},
"invoice": {
"tapYourBoltCard": "Tap your Bolt Card",
"tapYourBoltCard": "Tap your Bolt Card to pay",
"scanInvoice": "or scan invoice",
"scanInvoiceHint": "Scanning the invoice with another device will not be confirmed here.",
"network": "Network",
"lightning": "Lightning",
"lightningTopup": "Lightning Top-Up",
"onchain": "Onchain",
"label": "Label",
"message": "Message",
"invoiceAmount": "Invoice amount",
"withdrawAmount": "Top-Up amount",
"fiatAmount": "Fiat amount",
"fees": "Fees",
"requestSwap": "Request swap",
"nextBatch": "Next batch",
Expand All @@ -50,8 +61,41 @@
"scheduleForNextBatch": "Schedule for next batch",
"startScanning": "Start scanning",
"payingInvoice": "Paying invoice",
"topupCard": "Tap your Bold Card to receive",
"paid": "Paid",
"returnToHome": "Return to Home"
"received": "Received",
"returnToHome": "Return to Home",
"copiedToClipboard": "Copied Invoice to Clipboard",
"errors": {
"noAmountInvoice": "Lightning invoice has no amount"
}
},
"wallet": {
"invoiceTitle": "Request invoice",
"withdrawTitle": "Withdraw",
"lnAddressTitle": "Pay to LN Address",
"btcAddressTitle": "Pay to BTC Address",
"send": "Send",
"receive": "Receive",
"tapYourBoltCard": "Tap your Bolt Card",
"loadingWallet": "Loading wallet",
"notValid": "Amount not valid",
"min": "Min",
"max": "Max",
"pinLimit": "PIN Limit"
},
"decoder": {
"showInfo": "Show card limits",
"hideInfo": "Hide card limits",
"lnAddressTitle": "Pay to LN Address",
"btcAddressTitle": "Pay to BTC Address",
"send": "Send",
"receive": "Receive",
"tapYourBoltCard": "Tap your Bolt Card",
"loadingWallet": "Loading wallet",
"min": "Min",
"max": "Max",
"pinLimit": "PIN Limit"
}
},
"errors": {
Expand Down
2 changes: 1 addition & 1 deletion src/components/BaseField/BaseField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export const BaseField = <T extends FieldProps>({
/>
)}
{typeof value === "string" && (
<S.ValueText numberOfLines={2}>{value}</S.ValueText>
<S.ValueText centered={true} numberOfLines={2}>{value}</S.ValueText>
)}
{left && (
<S.BadgeContainer>
Expand Down
17 changes: 7 additions & 10 deletions src/components/BaseField/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,16 @@ export const BaseFieldContainer = styled(View)<{
border-radius: ${theme.borderRadius}px;
`}
position: relative;
width: 100%;
width: fit-content;
flex-direction: row;
`;

export const ValueText = styled(Text)`
text-align-vertical: center;
padding-top: 8px;
align-items: center;
display: flex;
flex: 1;
height: 100%;
left: ${HORIZONTAL_PADDING + DEFAULT_LEFT}px;
right: ${HORIZONTAL_PADDING + DEFAULT_LEFT}px;
export const ValueText = styled(Text)<{
paddingTop?: number
}>`
width: fit-content;
padding-left: ${DEFAULT_LEFT + HORIZONTAL_PADDING}px;
padding-right: ${DEFAULT_LEFT + HORIZONTAL_PADDING}px;
font-family: Poppins-Medium;
background-color: transparent;
font-size: 16px;
Expand Down
6 changes: 4 additions & 2 deletions src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ export const Button = ({
return 36;
case "large":
return 58;
case "smallCircle":
return 40;
case "circle":
return 80;
}
Expand All @@ -99,7 +101,7 @@ export const Button = ({
size={size}
primaryColor={primaryColor}
disabled={disabled || isLoading}
isRound={(!!icon && !title) || size === "circle"}
isRound={(!!icon && !title) || size === "circle" || size === "smallCircle"}
android_ripple={{
color: "rgba(0, 0, 0, 0.2)",
foreground: true
Expand All @@ -114,7 +116,7 @@ export const Button = ({
)}
{title && (
<S.ButtonText
numberOfLines={size !== "circle" ? 1 : undefined}
numberOfLines={size !== "circle" && size !== "smallCircle" ? 1 : undefined}
weight={700}
buttonSize={size}
color={!isLoading ? secondaryColor : "transparent"}
Expand Down
19 changes: 12 additions & 7 deletions src/components/Button/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { platform } from "@config";
import styled from "styled-components";

type Mode = "normal" | "outline";
type Size = "small" | "medium" | "large" | "circle";
type Size = "small" | "medium" | "large" | "circle" | "smallCircle";

const getPadding = (size: Size) => {
switch (size) {
Expand All @@ -14,6 +14,7 @@ const getPadding = (size: Size) => {
return 18;
case "large":
return 18;
case "smallCircle":
case "circle":
return 0;
}
Expand Down Expand Up @@ -45,6 +46,8 @@ export const Button = styled(Pressable)<{
return 48;
case "large":
return 74;
case "smallCircle":
return 140;
case "circle":
return 280;
default:
Expand Down Expand Up @@ -82,7 +85,7 @@ export const Button = styled(Pressable)<{
}
${disabled ? "opacity: 1;" : "cursor: pointer;"}
${getShadow(
size === "circle" ? { level: 8, shadowColor: primaryColor } : undefined
size === "circle" || size === "smallCircle" ? { level: 8, shadowColor: primaryColor } : undefined
)}
`;
}}
Expand All @@ -107,6 +110,8 @@ const getIconSize = (size: Size) => {
return 22;
case "large":
return 22;
case "smallCircle":
return 34;
case "circle":
return 68;
}
Expand Down Expand Up @@ -135,7 +140,7 @@ export const ButtonText = styled(Text).attrs(
({ buttonSize }: ButtonTextProps) => {
return {
h3: buttonSize === "circle",
h4: buttonSize === "medium" || buttonSize === "large",
h4: buttonSize === "medium" || buttonSize === "large" || buttonSize === "smallCircle",
h5: buttonSize === "small"
};
}
Expand All @@ -147,15 +152,15 @@ export const ButtonText = styled(Text).attrs(

return `
margin-left: ${hasIcon ? TEXT_MARGIN : 0}px;
margin-${buttonSize !== "circle" ? "right" : "top"}: ${
buttonSize === "circle"
margin-${buttonSize !== "circle" && buttonSize !== "smallCircle" ? "right" : "top"}: ${
buttonSize === "circle" || buttonSize === "smallCircle"
? TEXT_MARGIN * 1.5
: buttonSize !== "small"
? iconPlusMarginSize
: 0
}px;
${
buttonSize !== "small" && buttonSize !== "circle"
buttonSize !== "small" && buttonSize !== "circle" && buttonSize !== "smallCircle"
? "flex: 1;"
: !platform.isWeb
? "top: 1px;"
Expand All @@ -166,7 +171,7 @@ export const ButtonText = styled(Text).attrs(

position: relative;
text-align: center;
`;;;;;;;;;;;
`;

export const ButtonLoader = styled(Loader)`
position: absolute;
Expand Down
61 changes: 61 additions & 0 deletions src/components/CurrencySelect/CurrencySelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { forwardRef, useEffect, useState } from "react";
import * as S from "./styled";
import { useRates } from "@hooks";
import { ItemProps } from "@components/Picker/Picker";
import { ItemValue, Picker } from "@react-native-picker/picker/typings/Picker";


type CurrencySelectProps = {
onChange?: (event: { label: string, value: number }) => void;
};

export const CurrencySelect = forwardRef<Picker<ItemValue>, CurrencySelectProps>(
({onChange}, ref) => {

const { rates, getRate, currentRate, updateCurrentRate, loading } = useRates();
const [rateItems, setRateItems] = useState<ItemProps[]>([]);

const onRateChanged = (currencyShort: string) => {
void updateCurrentRate(getRate(currencyShort));
};

const getValue = () => {
return rateItems.find((i) => i.value === currentRate.label)?.value || ""
}

useEffect(() => {
if (rates !== undefined) {
const ratesAsItems = [{ label: "SAT - Satoshi", value: "SAT" }, { label: "BTC - Bitcoin", value: "BTC" }];
for (const pair in rates) {
const currentPair = rates[pair];
const currencyShort = Object.keys(currentPair).filter(key => key !== "currency" && key !== "BTC").pop();
if ("currency" in currentPair && currencyShort) {
ratesAsItems.push({ label: `${currencyShort} - ${currentPair.currency}`, value: currencyShort });
}
}
setRateItems(ratesAsItems);
}
}, [rates]);

useEffect(() => {
onChange?.(currentRate);
}, [currentRate]);

return (
<S.Field
value={loading ? "" : getValue()}
component={
<>
{(loading || getValue() === "") && <S.Spinner />}
<S.Picker
ref={ref}
selectedValue={`${currentRate.label}`}
onValueChange={(val)=>onRateChanged(`${val}`)}
items={rateItems}
/>
</>
}
/>
);
}
);
1 change: 1 addition & 0 deletions src/components/CurrencySelect/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { CurrencySelect } from "./CurrencySelect";
26 changes: 26 additions & 0 deletions src/components/CurrencySelect/styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import styled from "styled-components";
import { BaseField, Picker as RootPicker } from "@components";
import { ActivityIndicator } from "react-native";


export const Field = styled(BaseField)`
background-color: ${({ theme }) => theme.colors.white}
`;

export const Spinner = styled(ActivityIndicator)`
left: -15px;
`;

export const Picker = styled(RootPicker)`
position: absolute;
opacity: 0;
height: 100%;
width: 100%;
border: 0;
background-color: transparent;
padding: 0 12px;
padding-top: 8px;
font-size: 16px;
font-family: Poppins-Medium;
color: ${({ theme }) => theme.colors.primary};
`;
Loading
Loading