This commit is contained in:
parent
0442c3512c
commit
9f88b44b91
@ -40,5 +40,9 @@
|
|||||||
"wss://nostr.wine/": { "read": true, "write": false },
|
"wss://nostr.wine/": { "read": true, "write": false },
|
||||||
"wss://eden.nostr.land/": { "read": true, "write": false }
|
"wss://eden.nostr.land/": { "read": true, "write": false }
|
||||||
},
|
},
|
||||||
"useIndexedDBEvents": false
|
"useIndexedDBEvents": false,
|
||||||
|
"alby": {
|
||||||
|
"clientId": "pohiJjPhQR",
|
||||||
|
"clientSecret": "GAl1YKLA3FveK1gLBYok"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
5
packages/app/custom.d.ts
vendored
5
packages/app/custom.d.ts
vendored
@ -85,6 +85,11 @@ declare const CONFIG: {
|
|||||||
profileLinkPrefix: NostrPrefix;
|
profileLinkPrefix: NostrPrefix;
|
||||||
defaultRelays: Record<string, RelaySettings>;
|
defaultRelays: Record<string, RelaySettings>;
|
||||||
useIndexedDBEvents: boolean;
|
useIndexedDBEvents: boolean;
|
||||||
|
// Alby wallet oAuth config
|
||||||
|
alby?: {
|
||||||
|
clientId: string;
|
||||||
|
clientSecret: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,7 +71,7 @@ export default function NoteReaction(props: NoteReactionProps) {
|
|||||||
const opt = {
|
const opt = {
|
||||||
showHeader: ev?.kind === EventKind.Repost || ev?.kind === EventKind.TextNote,
|
showHeader: ev?.kind === EventKind.Repost || ev?.kind === EventKind.TextNote,
|
||||||
showFooter: false,
|
showFooter: false,
|
||||||
truncate: true
|
truncate: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
return shouldNotBeRendered ? null : (
|
return shouldNotBeRendered ? null : (
|
||||||
|
@ -11,8 +11,10 @@ type ProxyImgProps = HTMLProps<HTMLImageElement> & {
|
|||||||
missingImageElement?: ReactNode;
|
missingImageElement?: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ProxyImg = forwardRef<HTMLImageElement, ProxyImgProps>(
|
export const ProxyImg = forwardRef<HTMLImageElement, ProxyImgProps>(function ProxyImg(
|
||||||
function ProxyImg({ size, className, promptToLoadDirectly, missingImageElement, sha256, ...props }: ProxyImgProps, ref) {
|
{ size, className, promptToLoadDirectly, missingImageElement, sha256, ...props }: ProxyImgProps,
|
||||||
|
ref,
|
||||||
|
) {
|
||||||
const { proxy } = useImgProxy();
|
const { proxy } = useImgProxy();
|
||||||
const [loadFailed, setLoadFailed] = useState(false);
|
const [loadFailed, setLoadFailed] = useState(false);
|
||||||
const [bypass, setBypass] = useState(CONFIG.media.bypassImgProxyError);
|
const [bypass, setBypass] = useState(CONFIG.media.bypassImgProxyError);
|
||||||
@ -61,5 +63,4 @@ export const ProxyImg = forwardRef<HTMLImageElement, ProxyImgProps>(
|
|||||||
return (
|
return (
|
||||||
<img {...props} ref={ref} src={src} width={size} height={size} className={className} onError={handleImageError} />
|
<img {...props} ref={ref} src={src} width={size} height={size} className={className} onError={handleImageError} />
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
@ -42,7 +42,14 @@ export default function TrendingHashtags({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return <HashTagHeader key={a.hashtag} tag={a.hashtag} events={a.posts} className={classNames("bb", { p: !short })} />;
|
return (
|
||||||
|
<HashTagHeader
|
||||||
|
key={a.hashtag}
|
||||||
|
tag={a.hashtag}
|
||||||
|
events={a.posts}
|
||||||
|
className={classNames("bb", { p: !short })}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
|
@ -18,7 +18,7 @@ import useCachedFetch from "@/Hooks/useCachedFetch";
|
|||||||
import { System } from "@/index";
|
import { System } from "@/index";
|
||||||
import { removeUndefined } from "@snort/shared";
|
import { removeUndefined } from "@snort/shared";
|
||||||
|
|
||||||
export default function TrendingNotes({ count = Infinity, small = false }: { count: number, small: boolean }) {
|
export default function TrendingNotes({ count = Infinity, small = false }: { count: number; small: boolean }) {
|
||||||
const api = new NostrBandApi();
|
const api = new NostrBandApi();
|
||||||
const { lang } = useLocale();
|
const { lang } = useLocale();
|
||||||
const trendingNotesUrl = api.trendingNotesUrl(lang);
|
const trendingNotesUrl = api.trendingNotesUrl(lang);
|
||||||
@ -29,7 +29,8 @@ export default function TrendingNotes({ count = Infinity, small = false }: { cou
|
|||||||
isLoading,
|
isLoading,
|
||||||
error,
|
error,
|
||||||
} = useCachedFetch<{ notes: Array<{ event: NostrEvent }> }, Array<NostrEvent>>(trendingNotesUrl, storageKey, data => {
|
} = useCachedFetch<{ notes: Array<{ event: NostrEvent }> }, Array<NostrEvent>>(trendingNotesUrl, storageKey, data => {
|
||||||
return removeUndefined(data.notes.map(a => {
|
return removeUndefined(
|
||||||
|
data.notes.map(a => {
|
||||||
const ev = a.event;
|
const ev = a.event;
|
||||||
if (!System.Optimizer.schnorrVerify(ev)) {
|
if (!System.Optimizer.schnorrVerify(ev)) {
|
||||||
console.error(`Event with invalid sig\n\n${ev}\n\nfrom ${trendingNotesUrl}`);
|
console.error(`Event with invalid sig\n\n${ev}\n\nfrom ${trendingNotesUrl}`);
|
||||||
@ -37,7 +38,8 @@ export default function TrendingNotes({ count = Infinity, small = false }: { cou
|
|||||||
}
|
}
|
||||||
System.HandleEvent(ev as TaggedNostrEvent);
|
System.HandleEvent(ev as TaggedNostrEvent);
|
||||||
return ev;
|
return ev;
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
|
@ -105,7 +105,11 @@ export default function WalletPage(props: { showHistory: boolean }) {
|
|||||||
<div>
|
<div>
|
||||||
<select className="w-max" onChange={e => Wallets.switch(e.target.value)} value={walletState.config?.id}>
|
<select className="w-max" onChange={e => Wallets.switch(e.target.value)} value={walletState.config?.id}>
|
||||||
{Wallets.list().map(a => {
|
{Wallets.list().map(a => {
|
||||||
return <option value={a.id} key={a.id}>{a.info.alias}</option>;
|
return (
|
||||||
|
<option value={a.id} key={a.id}>
|
||||||
|
{a.info.alias}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,17 +10,27 @@ import AlbyIcon from "@/Icons/Alby";
|
|||||||
import Icon from "@/Icons/Icon";
|
import Icon from "@/Icons/Icon";
|
||||||
import { getAlbyOAuth } from "./wallet/Alby";
|
import { getAlbyOAuth } from "./wallet/Alby";
|
||||||
|
|
||||||
const WalletRow = (props: { logo: ReactNode; name: ReactNode; url: string; desc?: ReactNode }) => {
|
const WalletRow = (props: {
|
||||||
|
logo: ReactNode;
|
||||||
|
name: ReactNode;
|
||||||
|
url: string;
|
||||||
|
desc?: ReactNode;
|
||||||
|
onClick?: () => void;
|
||||||
|
}) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="flex items-center gap-4 px-4 py-2 bg-[--gray-superdark] rounded-xl hover:bg-[--gray-ultradark]"
|
className="flex items-center gap-4 px-4 py-2 bg-[--gray-superdark] rounded-xl hover:bg-[--gray-ultradark]"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (props.onClick) {
|
||||||
|
props.onClick();
|
||||||
|
} else {
|
||||||
if (props.url.startsWith("http")) {
|
if (props.url.startsWith("http")) {
|
||||||
window.location.href = props.url;
|
window.location.href = props.url;
|
||||||
} else {
|
} else {
|
||||||
navigate(props.url);
|
navigate(props.url);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}}>
|
}}>
|
||||||
<div className="rounded-xl aspect-square h-[4rem] bg-[--gray-dark] p-3 flex items-center justify-center">
|
<div className="rounded-xl aspect-square h-[4rem] bg-[--gray-dark] p-3 flex items-center justify-center">
|
||||||
{props.logo}
|
{props.logo}
|
||||||
@ -35,7 +45,6 @@ const WalletRow = (props: { logo: ReactNode; name: ReactNode; url: string; desc?
|
|||||||
};
|
};
|
||||||
|
|
||||||
const WalletSettings = () => {
|
const WalletSettings = () => {
|
||||||
const alby = getAlbyOAuth();
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h3>
|
<h3>
|
||||||
@ -68,12 +77,18 @@ const WalletSettings = () => {
|
|||||||
url="/settings/wallet/cashu"
|
url="/settings/wallet/cashu"
|
||||||
desc={<FormattedMessage defaultMessage="Cashu mint wallet" id="3natuV" />}
|
desc={<FormattedMessage defaultMessage="Cashu mint wallet" id="3natuV" />}
|
||||||
/>
|
/>
|
||||||
|
{CONFIG.alby && (
|
||||||
<WalletRow
|
<WalletRow
|
||||||
logo={<AlbyIcon size={64} />}
|
logo={<AlbyIcon size={64} />}
|
||||||
name="Alby"
|
name="Alby"
|
||||||
url={alby.authUrl}
|
url={""}
|
||||||
|
onClick={() => {
|
||||||
|
const alby = getAlbyOAuth();
|
||||||
|
window.location.href = alby.getAuthUrl();
|
||||||
|
}}
|
||||||
desc={<FormattedMessage defaultMessage="Alby wallet connection" id="XPB8VV" />}
|
desc={<FormattedMessage defaultMessage="Alby wallet connection" id="XPB8VV" />}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,18 +1,40 @@
|
|||||||
import PageSpinner from "@/Element/PageSpinner";
|
import PageSpinner from "@/Element/PageSpinner";
|
||||||
|
import { WalletConfig, WalletKind, Wallets } from "@/Wallet";
|
||||||
|
import AlbyWallet from "@/Wallet/AlbyWallet";
|
||||||
import { sha256 } from "@noble/hashes/sha256";
|
import { sha256 } from "@noble/hashes/sha256";
|
||||||
import { randomBytes } from "@noble/hashes/utils";
|
import { randomBytes } from "@noble/hashes/utils";
|
||||||
import { base64, base64urlnopad, hex } from "@scure/base";
|
import { base64, base64urlnopad, hex } from "@scure/base";
|
||||||
|
import { unixNow } from "@snort/shared";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import { v4 as uuid } from "uuid";
|
||||||
|
|
||||||
export default function AlbyOAuth() {
|
export default function AlbyOAuth() {
|
||||||
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const alby = getAlbyOAuth();
|
const alby = getAlbyOAuth();
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
async function setupWallet(token: string) {
|
async function setupWallet(token: string) {
|
||||||
|
try {
|
||||||
const auth = await alby.getToken(token);
|
const auth = await alby.getToken(token);
|
||||||
console.debug(auth);
|
console.debug(auth);
|
||||||
|
const connection = new AlbyWallet(auth, () => {});
|
||||||
|
const info = await connection.getInfo();
|
||||||
|
|
||||||
|
const newWallet = {
|
||||||
|
id: uuid(),
|
||||||
|
kind: WalletKind.Alby,
|
||||||
|
active: true,
|
||||||
|
info,
|
||||||
|
data: JSON.stringify(auth),
|
||||||
|
} as WalletConfig;
|
||||||
|
Wallets.add(newWallet);
|
||||||
|
|
||||||
|
navigate("/settings/wallet");
|
||||||
|
} catch (e) {
|
||||||
|
setError((e as Error).message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -38,12 +60,17 @@ export default function AlbyOAuth() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getAlbyOAuth() {
|
export function getAlbyOAuth() {
|
||||||
const clientId = "35EQp6crss";
|
const clientId = CONFIG.alby?.clientId ?? "";
|
||||||
const clientSecret = "DTUPIqOjsjwxZXcJwF5C";
|
const clientSecret = CONFIG.alby?.clientSecret ?? "";
|
||||||
const redirectUrl = `${window.location.protocol}//${window.location.host}/settings/wallet/alby`;
|
const redirectUrl = `${window.location.protocol}//${window.location.host}/settings/wallet/alby`;
|
||||||
const scopes = ["invoices:create", "invoices:read", "transactions:read", "balance:read", "payments:send"];
|
const scopes = ["invoices:create", "invoices:read", "transactions:read", "balance:read", "payments:send"];
|
||||||
|
|
||||||
const ec = new TextEncoder();
|
const ec = new TextEncoder();
|
||||||
|
const tokenUrl = "https://api.getalby.com/oauth/token";
|
||||||
|
|
||||||
|
return {
|
||||||
|
tokenUrl,
|
||||||
|
getAuthUrl: () => {
|
||||||
const code_verifier = hex.encode(randomBytes(64));
|
const code_verifier = hex.encode(randomBytes(64));
|
||||||
window.sessionStorage.setItem("alby-code", code_verifier);
|
window.sessionStorage.setItem("alby-code", code_verifier);
|
||||||
|
|
||||||
@ -55,12 +82,8 @@ export function getAlbyOAuth() {
|
|||||||
params.set("redirect_uri", redirectUrl);
|
params.set("redirect_uri", redirectUrl);
|
||||||
params.set("scope", scopes.join(" "));
|
params.set("scope", scopes.join(" "));
|
||||||
|
|
||||||
const tokenUrl = "https://api.getalby.com/oauth/token";
|
return `https://getalby.com/oauth?${params}`;
|
||||||
const authUrl = `https://getalby.com/oauth?${params}`;
|
},
|
||||||
|
|
||||||
return {
|
|
||||||
tokenUrl,
|
|
||||||
authUrl,
|
|
||||||
getToken: async (token: string) => {
|
getToken: async (token: string) => {
|
||||||
const code = window.sessionStorage.getItem("alby-code");
|
const code = window.sessionStorage.getItem("alby-code");
|
||||||
if (!code) throw new Error("Alby code is missing!");
|
if (!code) throw new Error("Alby code is missing!");
|
||||||
@ -85,10 +108,19 @@ export function getAlbyOAuth() {
|
|||||||
|
|
||||||
const data = await req.json();
|
const data = await req.json();
|
||||||
if (req.ok) {
|
if (req.ok) {
|
||||||
return data.access_token as string;
|
return { ...data, created_at: unixNow() } as OAuthToken;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(data.error_description as string);
|
throw new Error(data.error_description as string);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OAuthToken {
|
||||||
|
access_token: string;
|
||||||
|
created_at: number;
|
||||||
|
expires_in: number;
|
||||||
|
refresh_token: string;
|
||||||
|
scope: string;
|
||||||
|
token_type: string;
|
||||||
|
}
|
||||||
|
@ -16,7 +16,7 @@ const ConnectLNDHub = () => {
|
|||||||
|
|
||||||
async function tryConnect(config: string) {
|
async function tryConnect(config: string) {
|
||||||
try {
|
try {
|
||||||
const connection = new LNDHubWallet(config);
|
const connection = new LNDHubWallet(config, () => {});
|
||||||
await connection.login();
|
await connection.login();
|
||||||
const info = await connection.getInfo();
|
const info = await connection.getInfo();
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ const ConnectNostrWallet = () => {
|
|||||||
|
|
||||||
async function tryConnect(config: string) {
|
async function tryConnect(config: string) {
|
||||||
try {
|
try {
|
||||||
const connection = new NostrConnectWallet(config);
|
const connection = new NostrConnectWallet(config, () => {});
|
||||||
await connection.login();
|
await connection.login();
|
||||||
const info = await connection.getInfo();
|
const info = await connection.getInfo();
|
||||||
|
|
||||||
|
157
packages/app/src/Wallet/AlbyWallet.ts
Normal file
157
packages/app/src/Wallet/AlbyWallet.ts
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
import { OAuthToken } from "@/Pages/settings/wallet/Alby";
|
||||||
|
import {
|
||||||
|
InvoiceRequest,
|
||||||
|
LNWallet,
|
||||||
|
WalletError,
|
||||||
|
WalletErrorCode,
|
||||||
|
WalletInfo,
|
||||||
|
WalletInvoice,
|
||||||
|
WalletInvoiceState,
|
||||||
|
prToWalletInvoice,
|
||||||
|
} from ".";
|
||||||
|
import { unixNow, unwrap } from "@snort/shared";
|
||||||
|
|
||||||
|
export default class AlbyWallet implements LNWallet {
|
||||||
|
#token: OAuthToken;
|
||||||
|
constructor(
|
||||||
|
token: OAuthToken,
|
||||||
|
readonly onChange: () => void,
|
||||||
|
) {
|
||||||
|
this.#token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
isReady() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
canAutoLogin() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
canGetInvoices() {
|
||||||
|
return this.#token.scope.includes("invoices:read");
|
||||||
|
}
|
||||||
|
canGetBalance() {
|
||||||
|
return this.#token.scope.includes("balance:read");
|
||||||
|
}
|
||||||
|
|
||||||
|
async getInfo() {
|
||||||
|
const me = await this.#fetch<GetUserResponse>("/user/me");
|
||||||
|
return { alias: me.lightning_address } as WalletInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
async login() {
|
||||||
|
await this.#refreshToken();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
return Promise.resolve(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBalance() {
|
||||||
|
const bal = await this.#fetch<GetBalanceResponse>("/balance");
|
||||||
|
return bal.balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createInvoice(req: InvoiceRequest) {
|
||||||
|
const inv = await this.#fetch<CreateInvoiceResponse>("/invoices", "POST", {
|
||||||
|
amount: req.amount,
|
||||||
|
memo: req.memo,
|
||||||
|
});
|
||||||
|
|
||||||
|
return unwrap(prToWalletInvoice(inv.payment_request));
|
||||||
|
}
|
||||||
|
|
||||||
|
async payInvoice(pr: string) {
|
||||||
|
const pay = await this.#fetch<PayInvoiceResponse>("/payments/bolt11", "POST", {
|
||||||
|
invoice: pr,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prToWalletInvoice(pay.payment_request),
|
||||||
|
fees: pay.fee,
|
||||||
|
preimage: pay.payment_preimage,
|
||||||
|
state: WalletInvoiceState.Paid,
|
||||||
|
direction: "out",
|
||||||
|
} as WalletInvoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getInvoices() {
|
||||||
|
const invoices = await this.#fetch<Array<GetInvoiceResponse>>("/invoices?page=1&items=20");
|
||||||
|
return invoices.map(a => {
|
||||||
|
return {
|
||||||
|
...prToWalletInvoice(a.payment_request),
|
||||||
|
memo: a.comment,
|
||||||
|
preimage: a.preimage,
|
||||||
|
state: a.settled ? WalletInvoiceState.Paid : WalletInvoiceState.Pending,
|
||||||
|
direction: a.type === "incoming" ? "in" : "out",
|
||||||
|
} as WalletInvoice;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async #fetch<T>(path: string, method: "GET" | "POST" = "GET", body?: object) {
|
||||||
|
const req = await fetch(`https://api.getalby.com${path}`, {
|
||||||
|
method: method,
|
||||||
|
body: body ? JSON.stringify(body) : undefined,
|
||||||
|
headers: {
|
||||||
|
accept: "application/json",
|
||||||
|
authorization: `Bearer ${this.#token.access_token}`,
|
||||||
|
...(body ? { "content-type": "application/json" } : {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const json = await req.text();
|
||||||
|
if (req.ok) {
|
||||||
|
return JSON.parse(json) as T;
|
||||||
|
} else {
|
||||||
|
if (json.length > 0) {
|
||||||
|
throw new WalletError(WalletErrorCode.GeneralError, JSON.parse(json).message as string);
|
||||||
|
} else {
|
||||||
|
throw new WalletError(WalletErrorCode.GeneralError, `Error: ${json} (${req.status})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async #refreshToken() {
|
||||||
|
if (this.#token.created_at + this.#token.expires_in < unixNow()) {
|
||||||
|
// refresh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetBalanceResponse {
|
||||||
|
balance: number;
|
||||||
|
currency: string;
|
||||||
|
unit: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateInvoiceResponse {
|
||||||
|
expires_at: string;
|
||||||
|
payment_hash: string;
|
||||||
|
payment_request: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PayInvoiceResponse {
|
||||||
|
amount: number;
|
||||||
|
description?: string;
|
||||||
|
destination: string;
|
||||||
|
fee: number;
|
||||||
|
payment_hash: string;
|
||||||
|
payment_preimage: string;
|
||||||
|
payment_request: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetInvoiceResponse {
|
||||||
|
amount: number;
|
||||||
|
comment?: string;
|
||||||
|
created_at: string;
|
||||||
|
creation_date: number;
|
||||||
|
currency: string;
|
||||||
|
expires_at: string;
|
||||||
|
preimage: string;
|
||||||
|
payment_request: string;
|
||||||
|
settled: boolean;
|
||||||
|
settled_at: string;
|
||||||
|
type: "incoming" | "outgoing";
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetUserResponse {
|
||||||
|
lightning_address: string;
|
||||||
|
}
|
@ -5,6 +5,7 @@ import { unwrap } from "@/SnortUtils";
|
|||||||
import LNDHubWallet from "./LNDHub";
|
import LNDHubWallet from "./LNDHub";
|
||||||
import { NostrConnectWallet } from "./NostrWalletConnect";
|
import { NostrConnectWallet } from "./NostrWalletConnect";
|
||||||
import { WebLNWallet } from "./WebLN";
|
import { WebLNWallet } from "./WebLN";
|
||||||
|
import AlbyWallet from "./AlbyWallet";
|
||||||
|
|
||||||
export enum WalletKind {
|
export enum WalletKind {
|
||||||
LNDHub = 1,
|
LNDHub = 1,
|
||||||
@ -12,6 +13,7 @@ export enum WalletKind {
|
|||||||
WebLN = 3,
|
WebLN = 3,
|
||||||
NWC = 4,
|
NWC = 4,
|
||||||
Cashu = 5,
|
Cashu = 5,
|
||||||
|
Alby = 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum WalletErrorCode {
|
export enum WalletErrorCode {
|
||||||
@ -240,6 +242,9 @@ export class WalletStore extends ExternalStore<WalletStoreSnapshot> {
|
|||||||
case WalletKind.NWC: {
|
case WalletKind.NWC: {
|
||||||
return new NostrConnectWallet(unwrap(cfg.data), () => this.notifyChange());
|
return new NostrConnectWallet(unwrap(cfg.data), () => this.notifyChange());
|
||||||
}
|
}
|
||||||
|
case WalletKind.Alby: {
|
||||||
|
return new AlbyWallet(JSON.parse(unwrap(cfg.data)), () => this.notifyChange());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user