Skip to content

Commit

Permalink
Add in-app registration for Lightning Box
Browse files Browse the repository at this point in the history
New menu item in the left menu drawer (under Advanced).
  • Loading branch information
hsjoberg committed Nov 19, 2023
1 parent d3106bb commit 31224d4
Show file tree
Hide file tree
Showing 14 changed files with 648 additions and 7 deletions.
15 changes: 15 additions & 0 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,10 @@
"name": {
"ask": "Send my name together with this payment",
"mandatory": "Your name has to be sent together with this payment"
},
"identifier": {
"ask": "Send my Lightning Address together with this payment",
"mandatory": "Your Lightning Address has to be sent together with this payment"
}
},
"payloadErrors": {
Expand Down Expand Up @@ -818,6 +822,17 @@
"msg": "Would you like to restore to the default LN channel sync server"
}
},
"lightningBoxServer": {
"title": "Set Lightning Box server",
"subtitle": "",
"setDialog": {
"title": "Set Lightning Box server"
},
"restoreDialog": {
"title": "Restore Lightning Box Server",
"msg": "Would you like to restore to the default Lightning Box server"
}
},
"graphSync": {
"title": "Wait for graph sync before paying",
"subtitle": "Synchronized graph leads to optimal payment paths"
Expand Down
7 changes: 7 additions & 0 deletions src/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Welcome from "./windows/Welcome";
import LNURL from "./windows/LNURL";
import KeysendTest from "./windows/Keysend/Test";
import KeysendExperiment from "./windows/Keysend/Experiment";
import LightingBox from "./windows/LightningBox";
import GoogleDriveTestbed from "./windows/Google/GoogleDriveTestbed";
import TransactionDetails from "./windows/TransactionDetails";
import SyncInfo from "./windows/SyncInfo";
Expand Down Expand Up @@ -66,6 +67,7 @@ export type RootStackParamList = {
GoogleDriveTestbed: undefined;
KeysendTest: undefined;
KeysendExperiment: undefined;
LightningBox: undefined;
DeeplinkChecker: undefined;
WebLNBrowser:
| {
Expand Down Expand Up @@ -290,6 +292,11 @@ export default function Main() {
component={KeysendExperiment}
options={horizontalTransition}
/>
<RootStack.Screen
name="LightningBox"
component={LightingBox}
options={horizontalTransition}
/>
<RootStack.Screen name="Prompt" component={Prompt} options={animationDisabled} />
<RootStack.Screen name="DEV_CommandsX" component={DEV_Commands} options={animationDisabled} />
</RootStack.Navigator>
Expand Down
13 changes: 13 additions & 0 deletions src/components/Drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,19 @@ export default function Drawer({ navigation }: DrawerContentComponentProps) {
<Text style={style.menuItemText}>{t("menu.keysendExperiment")}</Text>
</View>
</TouchableOpacity>
{PLATFORM === "android" && (
<TouchableOpacity onPress={() => goToScreen("LightningBox")}>
<View style={style.menuItem}>
<Icon
style={[style.menuItemIcon, { fontSize: 25 }]}
color={blixtTheme.dark}
type="FontAwesome"
name="inbox"
/>
<Text style={style.menuItemText}>Lightning Box</Text>
</View>
</TouchableOpacity>
)}
</View>
</View>
</ScrollView>
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/i18n.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export const namespaces = {
keysend: {
experiment: "keysend.experiment",
},
lightningBox: {
manage: "lightningBox.manage",
},
lightningInfo: {
lightningInfo: "lightningInfo.lightningInfo",
openChannel: "lightningInfo.openChannel",
Expand Down
2 changes: 1 addition & 1 deletion src/lndmobile/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,7 @@ export const sendCustomMessage = async (
options: {
type,
peer: hexToUint8Array(peerPubkeyHex),
data: stringToUint8Array(dataString),
data: unicodeStringToUint8Array(dataString),
},
});
return response;
Expand Down
7 changes: 4 additions & 3 deletions src/state/LightningBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ export const lightningBox: ILightningBoxModel = {
log.d("request === LNURLPAY_REQUEST1");

const maxSendable = getStoreState().channel.remoteBalance;
const lnurlpDesc = getStoreState().settings.lightningBoxLnurlPayDesc; // TODO move to store initialization

const metadata = [["text/plain", "Cheers!"]];
const metadata = [["text/plain", lnurlpDesc]];
if (payload?.metadata?.lightningAddress) {
metadata.push(["text/identifier", payload?.metadata.lightningAddress]);
}
Expand All @@ -91,8 +92,8 @@ export const lightningBox: ILightningBoxModel = {
);
} else if (payload.request === "LNURLPAY_REQUEST2") {
log.d("request === LNURLPAY_REQUEST2");

const metadata = [["text/plain", "Cheers!"]];
const lnurlpDesc = getStoreState().settings.lightningBoxLnurlPayDesc;
const metadata = [["text/plain", lnurlpDesc]];
if (payload?.metadata?.lightningAddress) {
metadata.push(["text/identifier", payload?.metadata.lightningAddress]);
}
Expand Down
45 changes: 45 additions & 0 deletions src/state/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Action, Thunk, action, thunk } from "easy-peasy";
import {
BLIXT_NODE_PUBKEY,
DEFAULT_INVOICE_EXPIRY,
DEFAULT_LIGHTNINGBOX_LNURLPDESC,
DEFAULT_LIGHTNINGBOX_SERVER,
DEFAULT_LND_LOG_LEVEL,
DEFAULT_MAX_LN_FEE_PERCENTAGE,
DEFAULT_PATHFINDING_ALGORITHM,
Expand Down Expand Up @@ -88,6 +90,9 @@ export interface ISettingsModel {
changePersistentServicesWarningShown: Thunk<ISettingsModel, boolean, any, IStoreModel>;
changeCustomInvoicePreimageEnabled: Thunk<ISettingsModel, boolean>;
changeSpeedloaderServer: Thunk<ISettingsModel, string>;
changeLightningBoxServer: Thunk<ISettingsModel, string>;
changeLightningBoxAddress: Thunk<ISettingsModel, string>;
changeLightningBoxLnurlPayDesc: Thunk<ISettingsModel, string>;

setBitcoinUnit: Action<ISettingsModel, keyof IBitcoinUnits>;
setFiatUnit: Action<ISettingsModel, keyof IFiatRates>;
Expand Down Expand Up @@ -133,6 +138,9 @@ export interface ISettingsModel {
setPersistentServicesWarningShown: Action<ISettingsModel, boolean>;
setCustomInvoicePreimageEnabled: Action<ISettingsModel, boolean>;
setSpeedloaderServer: Action<ISettingsModel, string>;
setLightningBoxServer: Action<ISettingsModel, string>;
setLightningBoxAddress: Action<ISettingsModel, string>;
SetLightningBoxLnurlPayDesc: Action<ISettingsModel, string>;

bitcoinUnit: keyof IBitcoinUnits;
fiatUnit: keyof IFiatRates;
Expand Down Expand Up @@ -178,6 +186,9 @@ export interface ISettingsModel {
persistentServicesWarningShown: boolean;
customInvoicePreimageEnabled: boolean;
speedloaderServer: string;
lightningBoxServer: string;
lightningBoxAddress: string;
lightningBoxLnurlPayDesc: string;
}

export const settings: ISettingsModel = {
Expand Down Expand Up @@ -265,6 +276,13 @@ export const settings: ISettingsModel = {
actions.setSpeedloaderServer(
(await getItem(StorageItem.speedloaderServer)) ?? DEFAULT_SPEEDLOADER_SERVER,
);
actions.setLightningBoxServer(
(await getItem(StorageItem.lightningBoxServer)) ?? DEFAULT_LIGHTNINGBOX_SERVER,
);
actions.setLightningBoxAddress((await getItem(StorageItem.lightningBoxAddress)) ?? "");
actions.SetLightningBoxLnurlPayDesc(
(await getItem(StorageItem.lightningBoxLnurlPayDesc)) ?? DEFAULT_LIGHTNINGBOX_LNURLPDESC,
);

log.d("Done");
}),
Expand Down Expand Up @@ -493,6 +511,21 @@ export const settings: ISettingsModel = {
actions.setSpeedloaderServer(payload);
}),

changeLightningBoxServer: thunk(async (actions, payload) => {
await setItem(StorageItem.lightningBoxServer, payload);
actions.setLightningBoxServer(payload);
}),

changeLightningBoxAddress: thunk(async (actions, payload) => {
await setItem(StorageItem.lightningBoxAddress, payload);
actions.setLightningBoxAddress(payload);
}),

changeLightningBoxLnurlPayDesc: thunk(async (actions, payload) => {
await setItem(StorageItem.lightningBoxLnurlPayDesc, payload);
actions.SetLightningBoxLnurlPayDesc(payload);
}),

setBitcoinUnit: action((state, payload) => {
state.bitcoinUnit = payload;
}),
Expand Down Expand Up @@ -625,6 +658,15 @@ export const settings: ISettingsModel = {
setSpeedloaderServer: action((state, payload) => {
state.speedloaderServer = payload;
}),
setLightningBoxServer: action((state, payload) => {
state.lightningBoxServer = payload;
}),
setLightningBoxAddress: action((state, payload) => {
state.lightningBoxAddress = payload;
}),
SetLightningBoxLnurlPayDesc: action((state, payload) => {
state.lightningBoxLnurlPayDesc = payload;
}),

bitcoinUnit: "bitcoin",
fiatUnit: "USD",
Expand Down Expand Up @@ -670,4 +712,7 @@ export const settings: ISettingsModel = {
persistentServicesWarningShown: false,
customInvoicePreimageEnabled: false,
speedloaderServer: DEFAULT_SPEEDLOADER_SERVER,
lightningBoxServer: DEFAULT_LIGHTNINGBOX_SERVER,
lightningBoxAddress: "",
lightningBoxLnurlPayDesc: DEFAULT_LIGHTNINGBOX_LNURLPDESC,
};
11 changes: 11 additions & 0 deletions src/storage/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
BLIXT_NODE_PUBKEY,
DEFAULT_DUNDER_SERVER,
DEFAULT_INVOICE_EXPIRY,
DEFAULT_LIGHTNINGBOX_LNURLPDESC,
DEFAULT_LIGHTNINGBOX_SERVER,
DEFAULT_LND_LOG_LEVEL,
DEFAULT_MAX_LN_FEE_PERCENTAGE,
DEFAULT_NEUTRINO_NODE,
Expand Down Expand Up @@ -84,6 +86,9 @@ export enum StorageItem { // const enums not supported in Babel 7...
persistentServicesWarningShown = "persistentServicesWarningShown",
customInvoicePreimageEnabled = "customInvoicePreimageEnabled",
speedloaderServer = "speedloaderServer",
lightningBoxServer = "lightningBoxServer",
lightningBoxAddress = "lightningBoxAddress",
lightningBoxLnurlPayDesc = "lightningBoxLnurlPayDesc",
}

export const setItem = async (key: StorageItem, value: string) =>
Expand Down Expand Up @@ -184,6 +189,9 @@ export const clearApp = async () => {
removeItem(StorageItem.persistentServicesEnabled),
removeItem(StorageItem.persistentServicesWarningShown),
removeItem(StorageItem.speedloaderServer),
removeItem(StorageItem.lightningBoxServer),
removeItem(StorageItem.lightningBoxAddress),
removeItem(StorageItem.lightningBoxLnurlPayDesc),
]);
};

Expand Down Expand Up @@ -272,5 +280,8 @@ export const setupApp = async () => {
setItemObject<string[]>(StorageItem.zeroConfPeers, [BLIXT_NODE_PUBKEY]),
setItemObject<boolean>(StorageItem.customInvoicePreimageEnabled, false),
setItem(StorageItem.speedloaderServer, DEFAULT_SPEEDLOADER_SERVER),
setItem(StorageItem.lightningBoxServer, DEFAULT_LIGHTNINGBOX_SERVER),
// setItem(StorageItem.lightningBoxAddress, ""),
setItem(StorageItem.lightningBoxLnurlPayDesc, DEFAULT_LIGHTNINGBOX_LNURLPDESC),
]);
};
7 changes: 7 additions & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ export const DEFAULT_SPEEDLOADER_SERVER = chainSelect({
regtest: "",
});

export const DEFAULT_LIGHTNINGBOX_SERVER = chainSelect({
mainnet: "https://blixtwallet.com/lightning-box",
testnet: "",
regtest: "",
});
export const DEFAULT_LIGHTNINGBOX_LNURLPDESC = "Thanks for the sats! ⚡️";

export const HEADER_MIN_HEIGHT =
Platform.select({
android: (StatusBar.currentHeight ?? 0) + 53,
Expand Down
2 changes: 1 addition & 1 deletion src/windows/LNURL/PayRequest/PaymentCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default function PaymentCard({ onPaid, lnUrlObject, callback }: IPaymentC
const commentAllowed = lnUrlObject.commentAllowed ?? undefined;
const domain = getDomainFromURL(lnurlStr ?? "");
const name = useStoreState((store) => store.settings.name);
const identifier = undefined; //"hampus@blixtwallet.com"; // TODO needs to get data from Lightning Box store
const identifier = useStoreState((store) => store.settings.lightningBoxAddress) || undefined;
const [sendName, setSendName] = useState<boolean | undefined>(
lnUrlObject.commentAllowed !== undefined ? false : undefined,
);
Expand Down
78 changes: 78 additions & 0 deletions src/windows/LightningBox/LightningBoxInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React, { useState, useLayoutEffect } from "react";

import { Button, Text, View, H1 } from "native-base";
import Clipboard from "@react-native-community/clipboard";

import Content from "../../components/Content";
import Container from "../../components/Container";
import { StackNavigationProp } from "@react-navigation/stack";
import { RootStackParamList } from "../../Main";
import { toast } from "../../utils";
import { useTranslation } from "react-i18next";
import { namespaces } from "../../i18n/i18n.constants";
import { useStoreActions, useStoreState } from "../../state/store";
import QrCode from "../../components/QrCode";
import CopyAddress from "../../components/CopyAddress";

interface ILightningBoxProps {
navigation: StackNavigationProp<RootStackParamList, "LightningBox">;
}
export default function LightningBoxRegistration({ navigation }: ILightningBoxProps) {
const t = useTranslation(namespaces.lightningBox.manage).t;

const lightningBoxAddress = useStoreState((store) => store.settings.lightningBoxAddress);
// const lightningBoxLnurlPayDesc = useStoreState(
// (store) => store.settings.lightningBoxLnurlPayDesc,
// );
const changeLightningBoxAddress = useStoreActions(
(store) => store.settings.changeLightningBoxAddress,
);

const [showQrCode, setShowQrCode] = useState(false);

const onPressShowQrCode = () => {
setShowQrCode(true);
};

useLayoutEffect(() => {
navigation.setOptions({
headerTitle: t("title"),
headerBackTitle: t("buttons.back", { ns: namespaces.common }),
headerShown: true,
});
}, [navigation]);

const identifier = lightningBoxAddress.split("@")[0];
const domain = lightningBoxAddress.split("@")[1];
const lud17 = `lnurlp://${domain}/.well-known/lnurlp/${identifier}`;

const onPressLightningAddress = () => {
Clipboard.setString(lightningBoxAddress);
toast(t("msg.clipboardCopy", { ns: namespaces.common }), undefined, "warning");
};

const onPressQrCode = () => {
Clipboard.setString(lud17);
toast(t("msg.clipboardCopy", { ns: namespaces.common }), undefined, "warning");
};

return (
<Container>
<Content centered>
<H1 style={{ marginBottom: 10 }}>Lightning Address</H1>

<Text>Your Lightning Address by Lightning Box is:</Text>
<View style={{ width: "89%", marginBottom: 16 }} testID="payment-request-string">
<CopyAddress text={lightningBoxAddress} onPress={onPressLightningAddress} />
</View>
{/* <Text>Message to payer: {lightningBoxLnurlPayDesc}</Text> */}
{showQrCode && <QrCode size={150} border={15} data={lud17} onPress={onPressQrCode} />}
{!showQrCode && (
<Button onPress={onPressShowQrCode}>
<Text>Show QR code</Text>
</Button>
)}
</Content>
</Container>
);
}
Loading

1 comment on commit 31224d4

@vercel
Copy link

@vercel vercel bot commented on 31224d4 Nov 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

blixt-wallet – ./

blixt-wallet-hsjoberg.vercel.app
blixt-wallet-git-master-hsjoberg.vercel.app

Please sign in to comment.