Compare commits
9 Commits
c83ff02a85
...
7e1c62fdcb
Author | SHA1 | Date | |
---|---|---|---|
|
7e1c62fdcb | ||
|
91192025e5 | ||
|
7119c2ef0b | ||
|
cf995d0a9f | ||
|
2b2f2d589c | ||
|
9dfdfe7329 | ||
|
4aa3c67527 | ||
|
a71b9be03f | ||
|
a585ba7f0a |
@ -749,7 +749,7 @@ export default {
|
||||
}
|
||||
}
|
||||
})
|
||||
if (subscription.postsSubscribedAt || subscription.commentsSubscribedAt) {
|
||||
if (subscription?.postsSubscribedAt || subscription?.commentsSubscribedAt) {
|
||||
throw new GraphQLError("you can't mute a stacker to whom you've subscribed", { extensions: { code: 'BAD_INPUT' } })
|
||||
}
|
||||
await models.mute.create({ data: { ...lookupData } })
|
||||
|
32
awards.csv
32
awards.csv
@ -74,23 +74,25 @@ SatsAllDay,issue,#1137,#1125,good-first-issue,,,,2k,weareallsatoshi@getalby.com,
|
||||
SatsAllDay,helpfulness,#1137,#1125,good-first-issue,,,,2k,weareallsatoshi@getalby.com,2024-05-04
|
||||
itsrealfake,pr,#1138,#995,good-first-issue,,,,20k,itsrealfake2@stacker.news,2024-05-06
|
||||
SouthKoreaLN,issue,#1138,#995,good-first-issue,,,,2k,south_korea_ln@stacker.news,2024-05-04
|
||||
mateusdeap,helpfulness,#1138,#995,good-first-issue,,,,1k,mateusdeap@stacker.news,???
|
||||
mateusdeap,helpfulness,#1138,#995,good-first-issue,,,,1k,mateusdeap@stacker.news,2024-05-17
|
||||
felipebueno,pr,#1094,,,,2,,80k,felipebueno@getalby.com,2024-05-06
|
||||
benalleng,helpfulness,#1127,#927,good-first-issue,,,,2k,benalleng@mutiny.plus,2024-05-04
|
||||
itsrealfake,pr,#1135,#1016,good-first-issue,,,nonideal solution,10k,itsrealfake2@stacker.news,2024-05-06
|
||||
SatsAllDay,issue,#1135,#1016,good-first-issue,,,,1k,weareallsatoshi@getalby.com,2024-05-04
|
||||
s373nZ,issue,#1136,#1107,medium,high,,,50k,se7enz@minibits.cash,2024-05-05
|
||||
abhiShandy,pr,#1123,#624,good-first-issue,,,,20k,abhishandy@stacker.news,???
|
||||
hkarani,pr,#1147,#1143,good-first-issue,,,,20k,asterisk32@stacker.news,???
|
||||
benalleng,helpfulness,#1147,#1143,good-first-issue,,,,2k,benalleng@mutiny.plus,???
|
||||
abhiShandy,pr,#1157,#1148,good-first-issue,,,,20k,abhishandy@stacker.news,???
|
||||
SatsAllDay,issue,#1157,#1148,good-first-issue,,,,2k,weareallsatoshi@getalby.com,???
|
||||
abhiShandy,pr,#1158,#1139,good-first-issue,,,,20k,abhishandy@stacker.news,???
|
||||
SatsAllDay,issue,#1158,#1139,good-first-issue,,,,2k,weareallsatoshi@getalby.com,???
|
||||
SatsAllDay,pr,#1145,#717,medium,,,,250k,weareallsatoshi@getalby.com,???
|
||||
benalleng,pr,#1129,#491,good-first-issue,,,paid for advice out of band,20k,benalleng@mutiny.plus,???
|
||||
benalleng,pr,#1129,#1045,easy,,2,post-humously upgraded to easy,80k,benalleng@mutiny.plus,???
|
||||
SouthKoreaLN,issue,#1129,#1045,easy,,,,8k,south_korea_ln@stacker.news,???
|
||||
tsmith123,pr,#1171,#1124,good-first-issue,,,bonus for refactor,40k,stickymarch60@walletofsatoshi.com,???
|
||||
SatsAllDay,issue,#1171,#1124,good-first-issue,,,,4k,weareallsatoshi@getalby.com,???
|
||||
felipebueno,pr,#1162,,,,2,,200k,felipebueno@getalby.com,???
|
||||
abhiShandy,pr,#1123,#624,good-first-issue,,,,20k,abhishandy@stacker.news,2024-05-17
|
||||
hkarani,pr,#1147,#1143,good-first-issue,,,,20k,asterisk32@stacker.news,2024-05-17
|
||||
benalleng,helpfulness,#1147,#1143,good-first-issue,,,,2k,benalleng@mutiny.plus,2024-05-17
|
||||
abhiShandy,pr,#1157,#1148,good-first-issue,,,,20k,abhishandy@stacker.news,2024-05-17
|
||||
SatsAllDay,issue,#1157,#1148,good-first-issue,,,,2k,weareallsatoshi@getalby.com,2024-05-17
|
||||
abhiShandy,pr,#1158,#1139,good-first-issue,,,,20k,abhishandy@stacker.news,2024-05-17
|
||||
SatsAllDay,issue,#1158,#1139,good-first-issue,,,,2k,weareallsatoshi@getalby.com,2024-05-17
|
||||
SatsAllDay,pr,#1145,#717,medium,,,,250k,weareallsatoshi@getalby.com,2024-05-17
|
||||
benalleng,pr,#1129,#491,good-first-issue,,,paid for advice out of band,20k,benalleng@mutiny.plus,2024-05-17
|
||||
benalleng,pr,#1129,#1045,easy,,2,post-humously upgraded to easy,80k,benalleng@mutiny.plus,2024-05-17
|
||||
SouthKoreaLN,issue,#1129,#1045,easy,,,,8k,south_korea_ln@stacker.news,2024-05-17
|
||||
tsmith123,pr,#1171,#1124,good-first-issue,,,bonus for refactor,40k,stickymarch60@walletofsatoshi.com,2024-05-17
|
||||
SatsAllDay,issue,#1171,#1124,good-first-issue,,,,4k,weareallsatoshi@getalby.com,2024-05-17
|
||||
felipebueno,pr,#1162,,,,2,,200k,felipebueno@getalby.com,2024-05-17
|
||||
Radentor,issue,,#1177,easy,,,,10k,Radentor@stacker.news,2024-05-17
|
||||
tsmith123,pr,#1179,#790,good-first-issue,high,,,40k,stickymarch60@walletofsatoshi.com,2024-05-17
|
||||
|
|
@ -1,7 +1,7 @@
|
||||
import QRCode from 'qrcode.react'
|
||||
import { CopyInput, InputSkeleton } from './form'
|
||||
import InvoiceStatus from './invoice-status'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { useWebLN } from './webln'
|
||||
import SimpleCountdown from './countdown'
|
||||
import Bolt11Info from './bolt11-info'
|
||||
@ -9,13 +9,10 @@ import Bolt11Info from './bolt11-info'
|
||||
export default function Qr ({ asIs, value, webLn, statusVariant, description, status }) {
|
||||
const qrValue = asIs ? value : 'lightning:' + value.toUpperCase()
|
||||
const provider = useWebLN()
|
||||
// XXX antipattern ... we shouldn't be getting multiple renders
|
||||
const sendPayment = useRef(false)
|
||||
|
||||
useEffect(() => {
|
||||
async function effect () {
|
||||
if (webLn && provider && !sendPayment.current) {
|
||||
sendPayment.current = true
|
||||
if (webLn && provider) {
|
||||
try {
|
||||
await provider.sendPayment({ bolt11: value })
|
||||
} catch (e) {
|
||||
|
@ -63,7 +63,7 @@ function RawWebLNProvider ({ children }) {
|
||||
return null
|
||||
})
|
||||
})
|
||||
}, [...availableProviders])
|
||||
}, [])
|
||||
|
||||
// keep list in sync with underlying providers
|
||||
useEffect(() => {
|
||||
@ -130,7 +130,7 @@ function RawWebLNProvider ({ children }) {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<WebLNContext.Provider value={{ provider: isEnabled(provider) ? { sendPayment: sendPaymentWithToast } : null, enabledProviders, setProvider, clearConfig }}>
|
||||
<WebLNContext.Provider value={{ provider: isEnabled(provider) ? { name: provider.name, sendPayment: sendPaymentWithToast } : null, enabledProviders, setProvider, clearConfig }}>
|
||||
{children}
|
||||
</WebLNContext.Provider>
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { createContext, useCallback, useContext, useEffect, useState } from 'react'
|
||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||
import { useWalletLogger } from '../logger'
|
||||
import { Status, migrateLocalStorage } from '.'
|
||||
import { bolt11Tags } from '@/lib/bolt11'
|
||||
@ -72,7 +72,6 @@ export function LNbitsProvider ({ children }) {
|
||||
const [status, setStatus] = useState()
|
||||
const { logger } = useWalletLogger(Wallet.LNbits)
|
||||
|
||||
const name = 'LNbits'
|
||||
let storageKey = 'webln:provider:lnbits'
|
||||
if (me) {
|
||||
storageKey = `${storageKey}:${me.id}`
|
||||
@ -196,7 +195,9 @@ export function LNbitsProvider ({ children }) {
|
||||
loadConfig().catch(console.error)
|
||||
}, [])
|
||||
|
||||
const value = { name, url, adminKey, status, saveConfig, clearConfig, getInfo, sendPayment }
|
||||
const value = useMemo(
|
||||
() => ({ name: 'LNbits', url, adminKey, status, saveConfig, clearConfig, getInfo, sendPayment }),
|
||||
[url, adminKey, status, saveConfig, clearConfig, getInfo, sendPayment])
|
||||
return (
|
||||
<LNbitsContext.Provider value={value}>
|
||||
{children}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { createContext, useCallback, useContext, useEffect, useState } from 'react'
|
||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||
import { useWalletLogger } from '../logger'
|
||||
import LNC from '@lightninglabs/lnc-web'
|
||||
import { Status, migrateLocalStorage } from '.'
|
||||
@ -191,8 +191,11 @@ export function LNCProvider ({ children }) {
|
||||
})()
|
||||
}, [me, setStatus, setConfig, logger])
|
||||
|
||||
const value = useMemo(
|
||||
() => ({ name: 'lnc', status, unlock, getInfo, sendPayment, config, saveConfig, clearConfig }),
|
||||
[status, unlock, getInfo, sendPayment, config, saveConfig, clearConfig])
|
||||
return (
|
||||
<LNCContext.Provider value={{ name: 'lnc', status, unlock, getInfo, sendPayment, config, saveConfig, clearConfig }}>
|
||||
<LNCContext.Provider value={value}>
|
||||
{children}
|
||||
{modal}
|
||||
</LNCContext.Provider>
|
||||
|
@ -1,6 +1,6 @@
|
||||
// https://github.com/getAlby/js-sdk/blob/master/src/webln/NostrWeblnProvider.ts
|
||||
|
||||
import { createContext, useCallback, useContext, useEffect, useState } from 'react'
|
||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||
import { Relay, finalizeEvent, nip04 } from 'nostr-tools'
|
||||
import { parseNwcUrl } from '@/lib/url'
|
||||
import { useWalletLogger } from '../logger'
|
||||
@ -20,7 +20,6 @@ export function NWCProvider ({ children }) {
|
||||
const [status, setStatus] = useState()
|
||||
const { logger } = useWalletLogger(Wallet.NWC)
|
||||
|
||||
const name = 'NWC'
|
||||
let storageKey = 'webln:provider:nwc'
|
||||
if (me) {
|
||||
storageKey = `${storageKey}:${me.id}`
|
||||
@ -273,7 +272,9 @@ export function NWCProvider ({ children }) {
|
||||
loadConfig().catch(err => logger.error(err.message || err.toString?.()))
|
||||
}, [])
|
||||
|
||||
const value = { name, nwcUrl, relayUrl, walletPubkey, secret, status, saveConfig, clearConfig, getInfo, sendPayment }
|
||||
const value = useMemo(
|
||||
() => ({ name: 'NWC', nwcUrl, relayUrl, walletPubkey, secret, status, saveConfig, clearConfig, getInfo, sendPayment }),
|
||||
[nwcUrl, relayUrl, walletPubkey, secret, status, saveConfig, clearConfig, getInfo, sendPayment])
|
||||
return (
|
||||
<NWCContext.Provider value={value}>
|
||||
{children}
|
||||
|
@ -2,11 +2,12 @@ import fetch from 'cross-fetch'
|
||||
import https from 'https'
|
||||
import crypto from 'crypto'
|
||||
import { HttpProxyAgent, HttpsProxyAgent } from '@/lib/proxy'
|
||||
import { TOR_REGEXP } from '@/lib/url'
|
||||
|
||||
export const createInvoice = async ({ socket, rune, cert, label, description, msats, expiry }) => {
|
||||
let protocol, agent
|
||||
const httpsAgentOptions = { ca: cert ? Buffer.from(cert, 'base64') : undefined }
|
||||
const isOnion = /\.onion(:[0-9]+)?$/.test(socket)
|
||||
const isOnion = TOR_REGEXP.test(socket)
|
||||
if (isOnion) {
|
||||
// we support HTTP and HTTPS over Tor
|
||||
protocol = cert ? 'https:' : 'http:'
|
||||
|
@ -93,3 +93,5 @@ export const MEDIA_DOMAIN_REGEXP = new RegExp(`^https?://${process.env.NEXT_PUBL
|
||||
|
||||
// this regex is not a bullet proof way of checking if a url points to an image. to be sure, fetch the url and check the mimetype
|
||||
export const IMG_URL_REGEXP = /^(https?:\/\/.*\.(?:png|jpg|jpeg|gif))$/
|
||||
|
||||
export const TOR_REGEXP = /\.onion(:[0-9]+)?$/
|
||||
|
@ -10,7 +10,7 @@ import { msatsToSats, numWithUnits, abbrNum, ensureB64, B64_URL_REGEX } from './
|
||||
import * as usersFragments from '@/fragments/users'
|
||||
import * as subsFragments from '@/fragments/subs'
|
||||
import { isInvoicableMacaroon, isInvoiceMacaroon } from './macaroon'
|
||||
import { parseNwcUrl } from './url'
|
||||
import { TOR_REGEXP, parseNwcUrl } from './url'
|
||||
import { datePivot } from './time'
|
||||
import { decodeRune } from '@/lib/cln'
|
||||
import bip39Words from './bip39-words'
|
||||
@ -601,11 +601,27 @@ export const lnAddrSchema = ({ payerData, min, max, commentAllowed } = {}) =>
|
||||
}, {})))
|
||||
|
||||
export const lnbitsSchema = object({
|
||||
url: process.env.NODE_ENV === 'development'
|
||||
url: process.env.NODE_ENV !== 'development'
|
||||
? string()
|
||||
.or([string().matches(/^(http:\/\/)?localhost:\d+$/), string().url()], 'invalid url')
|
||||
.required('required').trim()
|
||||
: string().url().required('required').trim().https(),
|
||||
: string().url().required('required').trim()
|
||||
.test(async (url, context) => {
|
||||
if (TOR_REGEXP.test(url)) {
|
||||
// allow HTTP and HTTPS over Tor
|
||||
if (!/^https?:\/\//.test(url)) {
|
||||
return context.createError({ message: 'http or https required' })
|
||||
}
|
||||
return true
|
||||
}
|
||||
try {
|
||||
// force HTTPS over clearnet
|
||||
await string().https().validate(url)
|
||||
} catch (err) {
|
||||
return context.createError({ message: err.message })
|
||||
}
|
||||
return true
|
||||
}),
|
||||
adminKey: string().length(32)
|
||||
})
|
||||
|
||||
|
@ -38,6 +38,19 @@ function bech32encode (hexString) {
|
||||
return bech32.encode('npub', bech32.toWords(Buffer.from(hexString, 'hex')))
|
||||
}
|
||||
|
||||
// sort to prevent hydration mismatch
|
||||
const getProviders = (authMethods) =>
|
||||
Object.keys(authMethods).filter(k => k !== '__typename' && k !== 'apiKey').sort()
|
||||
|
||||
// Show alert message if user only has one auth method activated
|
||||
// as users are losing access to their accounts
|
||||
const hasOnlyOneAuthMethod = (authMethods) => {
|
||||
const activatedAuths = getProviders(authMethods)
|
||||
.filter(provider => !!authMethods[provider])
|
||||
|
||||
return activatedAuths.length === 1
|
||||
}
|
||||
|
||||
export function SettingsHeader () {
|
||||
const router = useRouter()
|
||||
const pathParts = router.asPath.split('/').filter(segment => !!segment)
|
||||
@ -94,6 +107,7 @@ export default function Settings ({ ssrData }) {
|
||||
return (
|
||||
<Layout>
|
||||
<div className='pb-3 w-100 mt-2' style={{ maxWidth: '600px' }}>
|
||||
{hasOnlyOneAuthMethod(settings?.authMethods) && <div className={styles.alert}>Please add a second auth method to avoid losing access to your account.</div>}
|
||||
<SettingsHeader />
|
||||
<Form
|
||||
initial={{
|
||||
@ -673,8 +687,7 @@ function AuthMethods ({ methods, apiKeyEnabled }) {
|
||||
}
|
||||
)
|
||||
|
||||
// sort to prevent hydration mismatch
|
||||
const providers = Object.keys(methods).filter(k => k !== '__typename' && k !== 'apiKey').sort()
|
||||
const providers = getProviders(methods)
|
||||
|
||||
const unlink = async type => {
|
||||
// if there's only one auth method left
|
||||
|
@ -15,3 +15,8 @@
|
||||
.nav :global .active {
|
||||
border-bottom: 2px solid var(--bs-primary);
|
||||
}
|
||||
|
||||
.alert {
|
||||
color: var(--bs-danger);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
12
scripts/nwc
12
scripts/nwc
@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# https://github.com/benthecarman/nostr-wallet-connect-lnd
|
||||
|
||||
LND_HOST="${STACKER_LND_HOST:-localhost}"
|
||||
LND_PORT="${STACKER_LND_GRPC_PORT:-10010}"
|
||||
|
||||
RUST_LOG=info nostr-wallet-connect-lnd \
|
||||
--relay wss://relay.damus.io \
|
||||
--lnd-host $LND_HOST --lnd-port $LND_PORT \
|
||||
--macaroon-file docker/lnd/stacker/regtest/admin.macaroon --cert-file docker/lnd/stacker/tls.cert \
|
||||
--keys-file scripts/nwc-keys.json
|
Loading…
x
Reference in New Issue
Block a user