This commit is contained in:
parent
0442c3512c
commit
9f88b44b91
@ -40,5 +40,9 @@
|
||||
"wss://nostr.wine/": { "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;
|
||||
defaultRelays: Record<string, RelaySettings>;
|
||||
useIndexedDBEvents: boolean;
|
||||
// Alby wallet oAuth config
|
||||
alby?: {
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -354,18 +354,18 @@ export function NoteCreator() {
|
||||
onChange={e => {
|
||||
note.update(
|
||||
v =>
|
||||
(v.selectedCustomRelays =
|
||||
// set false if all relays selected
|
||||
e.target.checked &&
|
||||
(v.selectedCustomRelays =
|
||||
// set false if all relays selected
|
||||
e.target.checked &&
|
||||
note.selectedCustomRelays &&
|
||||
note.selectedCustomRelays.length == a.length - 1
|
||||
? undefined
|
||||
: // otherwise return selectedCustomRelays with target relay added / removed
|
||||
a.filter(el =>
|
||||
el === r
|
||||
? e.target.checked
|
||||
: !note.selectedCustomRelays || note.selectedCustomRelays.includes(el),
|
||||
)),
|
||||
? undefined
|
||||
: // otherwise return selectedCustomRelays with target relay added / removed
|
||||
a.filter(el =>
|
||||
el === r
|
||||
? e.target.checked
|
||||
: !note.selectedCustomRelays || note.selectedCustomRelays.includes(el),
|
||||
)),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
@ -434,9 +434,9 @@ export function NoteCreator() {
|
||||
onChange={e =>
|
||||
note.update(
|
||||
v =>
|
||||
(v.zapSplits = arr.map((vv, ii) =>
|
||||
ii === i ? { ...vv, weight: Number(e.target.value) } : vv,
|
||||
)),
|
||||
(v.zapSplits = arr.map((vv, ii) =>
|
||||
ii === i ? { ...vv, weight: Number(e.target.value) } : vv,
|
||||
)),
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
@ -71,7 +71,7 @@ export default function NoteReaction(props: NoteReactionProps) {
|
||||
const opt = {
|
||||
showHeader: ev?.kind === EventKind.Repost || ev?.kind === EventKind.TextNote,
|
||||
showFooter: false,
|
||||
truncate: true
|
||||
truncate: true,
|
||||
};
|
||||
|
||||
return shouldNotBeRendered ? null : (
|
||||
|
@ -11,55 +11,56 @@ type ProxyImgProps = HTMLProps<HTMLImageElement> & {
|
||||
missingImageElement?: ReactNode;
|
||||
};
|
||||
|
||||
export const ProxyImg = forwardRef<HTMLImageElement, ProxyImgProps>(
|
||||
function ProxyImg({ size, className, promptToLoadDirectly, missingImageElement, sha256, ...props }: ProxyImgProps, ref) {
|
||||
const { proxy } = useImgProxy();
|
||||
const [loadFailed, setLoadFailed] = useState(false);
|
||||
const [bypass, setBypass] = useState(CONFIG.media.bypassImgProxyError);
|
||||
const proxiedSrc = useMemo(() => proxy(props.src ?? "", size, sha256), [props.src, size, sha256]);
|
||||
const [src, setSrc] = useState(proxiedSrc);
|
||||
export const ProxyImg = forwardRef<HTMLImageElement, ProxyImgProps>(function ProxyImg(
|
||||
{ size, className, promptToLoadDirectly, missingImageElement, sha256, ...props }: ProxyImgProps,
|
||||
ref,
|
||||
) {
|
||||
const { proxy } = useImgProxy();
|
||||
const [loadFailed, setLoadFailed] = useState(false);
|
||||
const [bypass, setBypass] = useState(CONFIG.media.bypassImgProxyError);
|
||||
const proxiedSrc = useMemo(() => proxy(props.src ?? "", size, sha256), [props.src, size, sha256]);
|
||||
const [src, setSrc] = useState(proxiedSrc);
|
||||
|
||||
useEffect(() => {
|
||||
setLoadFailed(false);
|
||||
setSrc(proxy(props.src, size, sha256));
|
||||
}, [props.src, size, sha256]);
|
||||
|
||||
if (loadFailed && !bypass && (promptToLoadDirectly ?? true)) {
|
||||
return (
|
||||
<div
|
||||
className="note-invoice error"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
setBypass(true);
|
||||
}}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Failed to proxy image from {host}, click here to load directly"
|
||||
id="65BmHb"
|
||||
values={{
|
||||
host: getUrlHostname(props.src),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const handleImageError = e => {
|
||||
if (props.onError) {
|
||||
props.onError(e);
|
||||
} else {
|
||||
console.error("Failed to load image: ", props.src, e);
|
||||
if (bypass && src === proxiedSrc) {
|
||||
setSrc(props.src ?? "");
|
||||
} else {
|
||||
setLoadFailed(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!src || loadFailed) return missingImageElement ?? <div>Image not available</div>;
|
||||
useEffect(() => {
|
||||
setLoadFailed(false);
|
||||
setSrc(proxy(props.src, size, sha256));
|
||||
}, [props.src, size, sha256]);
|
||||
|
||||
if (loadFailed && !bypass && (promptToLoadDirectly ?? true)) {
|
||||
return (
|
||||
<img {...props} ref={ref} src={src} width={size} height={size} className={className} onError={handleImageError} />
|
||||
<div
|
||||
className="note-invoice error"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
setBypass(true);
|
||||
}}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Failed to proxy image from {host}, click here to load directly"
|
||||
id="65BmHb"
|
||||
values={{
|
||||
host: getUrlHostname(props.src),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const handleImageError = e => {
|
||||
if (props.onError) {
|
||||
props.onError(e);
|
||||
} else {
|
||||
console.error("Failed to load image: ", props.src, e);
|
||||
if (bypass && src === proxiedSrc) {
|
||||
setSrc(props.src ?? "");
|
||||
} else {
|
||||
setLoadFailed(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!src || loadFailed) return missingImageElement ?? <div>Image not available</div>;
|
||||
|
||||
return (
|
||||
<img {...props} ref={ref} src={src} width={size} height={size} className={className} onError={handleImageError} />
|
||||
);
|
||||
});
|
||||
|
@ -42,7 +42,14 @@ export default function TrendingHashtags({
|
||||
</div>
|
||||
);
|
||||
} 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 { 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 { lang } = useLocale();
|
||||
const trendingNotesUrl = api.trendingNotesUrl(lang);
|
||||
@ -29,15 +29,17 @@ export default function TrendingNotes({ count = Infinity, small = false }: { cou
|
||||
isLoading,
|
||||
error,
|
||||
} = useCachedFetch<{ notes: Array<{ event: NostrEvent }> }, Array<NostrEvent>>(trendingNotesUrl, storageKey, data => {
|
||||
return removeUndefined(data.notes.map(a => {
|
||||
const ev = a.event;
|
||||
if (!System.Optimizer.schnorrVerify(ev)) {
|
||||
console.error(`Event with invalid sig\n\n${ev}\n\nfrom ${trendingNotesUrl}`);
|
||||
return;
|
||||
}
|
||||
System.HandleEvent(ev as TaggedNostrEvent);
|
||||
return ev;
|
||||
}));
|
||||
return removeUndefined(
|
||||
data.notes.map(a => {
|
||||
const ev = a.event;
|
||||
if (!System.Optimizer.schnorrVerify(ev)) {
|
||||
console.error(`Event with invalid sig\n\n${ev}\n\nfrom ${trendingNotesUrl}`);
|
||||
return;
|
||||
}
|
||||
System.HandleEvent(ev as TaggedNostrEvent);
|
||||
return ev;
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
const login = useLogin();
|
||||
|
@ -105,7 +105,11 @@ export default function WalletPage(props: { showHistory: boolean }) {
|
||||
<div>
|
||||
<select className="w-max" onChange={e => Wallets.switch(e.target.value)} value={walletState.config?.id}>
|
||||
{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>
|
||||
</div>
|
||||
|
@ -10,16 +10,26 @@ import AlbyIcon from "@/Icons/Alby";
|
||||
import Icon from "@/Icons/Icon";
|
||||
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();
|
||||
return (
|
||||
<div
|
||||
className="flex items-center gap-4 px-4 py-2 bg-[--gray-superdark] rounded-xl hover:bg-[--gray-ultradark]"
|
||||
onClick={() => {
|
||||
if (props.url.startsWith("http")) {
|
||||
window.location.href = props.url;
|
||||
if (props.onClick) {
|
||||
props.onClick();
|
||||
} else {
|
||||
navigate(props.url);
|
||||
if (props.url.startsWith("http")) {
|
||||
window.location.href = props.url;
|
||||
} else {
|
||||
navigate(props.url);
|
||||
}
|
||||
}
|
||||
}}>
|
||||
<div className="rounded-xl aspect-square h-[4rem] bg-[--gray-dark] p-3 flex items-center justify-center">
|
||||
@ -35,7 +45,6 @@ const WalletRow = (props: { logo: ReactNode; name: ReactNode; url: string; desc?
|
||||
};
|
||||
|
||||
const WalletSettings = () => {
|
||||
const alby = getAlbyOAuth();
|
||||
return (
|
||||
<>
|
||||
<h3>
|
||||
@ -68,12 +77,18 @@ const WalletSettings = () => {
|
||||
url="/settings/wallet/cashu"
|
||||
desc={<FormattedMessage defaultMessage="Cashu mint wallet" id="3natuV" />}
|
||||
/>
|
||||
<WalletRow
|
||||
logo={<AlbyIcon size={64} />}
|
||||
name="Alby"
|
||||
url={alby.authUrl}
|
||||
desc={<FormattedMessage defaultMessage="Alby wallet connection" id="XPB8VV" />}
|
||||
/>
|
||||
{CONFIG.alby && (
|
||||
<WalletRow
|
||||
logo={<AlbyIcon size={64} />}
|
||||
name="Alby"
|
||||
url={""}
|
||||
onClick={() => {
|
||||
const alby = getAlbyOAuth();
|
||||
window.location.href = alby.getAuthUrl();
|
||||
}}
|
||||
desc={<FormattedMessage defaultMessage="Alby wallet connection" id="XPB8VV" />}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -1,18 +1,40 @@
|
||||
import PageSpinner from "@/Element/PageSpinner";
|
||||
import { WalletConfig, WalletKind, Wallets } from "@/Wallet";
|
||||
import AlbyWallet from "@/Wallet/AlbyWallet";
|
||||
import { sha256 } from "@noble/hashes/sha256";
|
||||
import { randomBytes } from "@noble/hashes/utils";
|
||||
import { base64, base64urlnopad, hex } from "@scure/base";
|
||||
import { unixNow } from "@snort/shared";
|
||||
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() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const alby = getAlbyOAuth();
|
||||
const [error, setError] = useState("");
|
||||
|
||||
async function setupWallet(token: string) {
|
||||
const auth = await alby.getToken(token);
|
||||
console.debug(auth);
|
||||
try {
|
||||
const auth = await alby.getToken(token);
|
||||
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(() => {
|
||||
@ -38,29 +60,30 @@ export default function AlbyOAuth() {
|
||||
}
|
||||
|
||||
export function getAlbyOAuth() {
|
||||
const clientId = "35EQp6crss";
|
||||
const clientSecret = "DTUPIqOjsjwxZXcJwF5C";
|
||||
const clientId = CONFIG.alby?.clientId ?? "";
|
||||
const clientSecret = CONFIG.alby?.clientSecret ?? "";
|
||||
const redirectUrl = `${window.location.protocol}//${window.location.host}/settings/wallet/alby`;
|
||||
const scopes = ["invoices:create", "invoices:read", "transactions:read", "balance:read", "payments:send"];
|
||||
|
||||
const ec = new TextEncoder();
|
||||
const code_verifier = hex.encode(randomBytes(64));
|
||||
window.sessionStorage.setItem("alby-code", code_verifier);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.set("client_id", clientId);
|
||||
params.set("response_type", "code");
|
||||
params.set("code_challenge", base64urlnopad.encode(sha256(code_verifier)));
|
||||
params.set("code_challenge_method", "S256");
|
||||
params.set("redirect_uri", redirectUrl);
|
||||
params.set("scope", scopes.join(" "));
|
||||
|
||||
const tokenUrl = "https://api.getalby.com/oauth/token";
|
||||
const authUrl = `https://getalby.com/oauth?${params}`;
|
||||
|
||||
return {
|
||||
tokenUrl,
|
||||
authUrl,
|
||||
getAuthUrl: () => {
|
||||
const code_verifier = hex.encode(randomBytes(64));
|
||||
window.sessionStorage.setItem("alby-code", code_verifier);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.set("client_id", clientId);
|
||||
params.set("response_type", "code");
|
||||
params.set("code_challenge", base64urlnopad.encode(sha256(code_verifier)));
|
||||
params.set("code_challenge_method", "S256");
|
||||
params.set("redirect_uri", redirectUrl);
|
||||
params.set("scope", scopes.join(" "));
|
||||
|
||||
return `https://getalby.com/oauth?${params}`;
|
||||
},
|
||||
getToken: async (token: string) => {
|
||||
const code = window.sessionStorage.getItem("alby-code");
|
||||
if (!code) throw new Error("Alby code is missing!");
|
||||
@ -85,10 +108,19 @@ export function getAlbyOAuth() {
|
||||
|
||||
const data = await req.json();
|
||||
if (req.ok) {
|
||||
return data.access_token as string;
|
||||
return { ...data, created_at: unixNow() } as OAuthToken;
|
||||
} else {
|
||||
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) {
|
||||
try {
|
||||
const connection = new LNDHubWallet(config);
|
||||
const connection = new LNDHubWallet(config, () => {});
|
||||
await connection.login();
|
||||
const info = await connection.getInfo();
|
||||
|
||||
|
@ -16,7 +16,7 @@ const ConnectNostrWallet = () => {
|
||||
|
||||
async function tryConnect(config: string) {
|
||||
try {
|
||||
const connection = new NostrConnectWallet(config);
|
||||
const connection = new NostrConnectWallet(config, () => {});
|
||||
await connection.login();
|
||||
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 { NostrConnectWallet } from "./NostrWalletConnect";
|
||||
import { WebLNWallet } from "./WebLN";
|
||||
import AlbyWallet from "./AlbyWallet";
|
||||
|
||||
export enum WalletKind {
|
||||
LNDHub = 1,
|
||||
@ -12,6 +13,7 @@ export enum WalletKind {
|
||||
WebLN = 3,
|
||||
NWC = 4,
|
||||
Cashu = 5,
|
||||
Alby = 6,
|
||||
}
|
||||
|
||||
export enum WalletErrorCode {
|
||||
@ -240,6 +242,9 @@ export class WalletStore extends ExternalStore<WalletStoreSnapshot> {
|
||||
case WalletKind.NWC: {
|
||||
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