Compare commits
4 Commits
a7066a34cd
...
d9024ff837
Author | SHA1 | Date | |
---|---|---|---|
|
d9024ff837 | ||
|
69916117b1 | ||
|
67799a508a | ||
|
7428738b23 |
@ -3,6 +3,7 @@ import { Checkbox, Input } from './form'
|
||||
import { useMe } from './me'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { isNumber } from '@/lib/validate'
|
||||
import { useIsClient } from './use-client'
|
||||
|
||||
function autoWithdrawThreshold ({ me }) {
|
||||
return isNumber(me?.privates?.autoWithdrawThreshold) ? me?.privates?.autoWithdrawThreshold : 10000
|
||||
@ -25,15 +26,12 @@ export function AutowithdrawSettings ({ wallet }) {
|
||||
setSendThreshold(Math.max(Math.floor(threshold / 10), 1))
|
||||
}, [autoWithdrawThreshold])
|
||||
|
||||
const [mounted, setMounted] = useState(false)
|
||||
useEffect(() => {
|
||||
setMounted(true)
|
||||
}, [])
|
||||
const isClient = useIsClient()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Checkbox
|
||||
disabled={mounted && !wallet.isConfigured}
|
||||
disabled={isClient && !wallet.isConfigured}
|
||||
label='enabled'
|
||||
id='enabled'
|
||||
name='enabled'
|
||||
|
@ -803,7 +803,7 @@ const StorageKeyPrefixContext = createContext()
|
||||
|
||||
export function Form ({
|
||||
initial, validate, schema, onSubmit, children, initialError, validateImmediately,
|
||||
storageKeyPrefix, validateOnChange = true, requireSession, innerRef,
|
||||
storageKeyPrefix, validateOnChange = true, requireSession, innerRef, enableReinitialize,
|
||||
...props
|
||||
}) {
|
||||
const toaster = useToast()
|
||||
@ -855,6 +855,7 @@ export function Form ({
|
||||
return (
|
||||
<Formik
|
||||
initialValues={initial}
|
||||
enableReinitialize={enableReinitialize}
|
||||
validateOnChange={validateOnChange}
|
||||
validate={validate}
|
||||
validationSchema={schema}
|
||||
|
@ -13,7 +13,7 @@ export default function HoverablePopover ({ trigger, body, onShow }) {
|
||||
onShow?.()
|
||||
timeoutId.current = setTimeout(() => setShow(true), 500)
|
||||
} else {
|
||||
timeoutId.current = setTimeout(() => setShow(!!popRef.current?.matches(':hover')), 500)
|
||||
timeoutId.current = setTimeout(() => setShow(!!popRef.current?.matches(':hover')), 300)
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,8 +23,7 @@ export default function HoverablePopover ({ trigger, body, onShow }) {
|
||||
trigger={['hover', 'focus']}
|
||||
show={show}
|
||||
onToggle={onToggle}
|
||||
delay={1}
|
||||
transition={false}
|
||||
transition
|
||||
rootClose
|
||||
overlay={
|
||||
<Popover style={{ position: 'fixed' }} onPointerLeave={() => onToggle(false)}>
|
||||
|
@ -57,7 +57,7 @@ function ImageOriginal ({ src, topLevel, rel, tab, children, onClick, ...props }
|
||||
target='_blank'
|
||||
rel={rel ?? UNKNOWN_LINK_REL}
|
||||
href={src}
|
||||
>{isRawURL ? src : children}
|
||||
>{isRawURL || !children ? src : children}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ export default memo(function Text ({ rel, imgproxyUrls, children, tab, itemId, o
|
||||
return url
|
||||
}
|
||||
const srcSet = imgproxyUrls?.[url]
|
||||
return <ZoomableImage srcSet={srcSet} tab={tab} src={src} rel={rel ?? UNKNOWN_LINK_REL} {...props} topLevel />
|
||||
return <ZoomableImage srcSet={srcSet} tab={tab} src={src} rel={rel ?? UNKNOWN_LINK_REL} {...props} topLevel={topLevel} />
|
||||
}, [imgproxyUrls, topLevel, tab])
|
||||
|
||||
return (
|
||||
|
@ -129,16 +129,13 @@
|
||||
}
|
||||
|
||||
.text img {
|
||||
--height: 35vh;
|
||||
--width: 100%;
|
||||
display: block;
|
||||
margin-top: .5rem;
|
||||
margin-bottom: .5rem;
|
||||
width: auto;
|
||||
max-width: min(var(--width), 100%);
|
||||
max-width: 100%;
|
||||
cursor: zoom-in;
|
||||
height: auto;
|
||||
max-height: min(var(--height), 25vh);
|
||||
max-height: 25vh;
|
||||
object-fit: contain;
|
||||
object-position: left top;
|
||||
min-width: 50%;
|
||||
@ -148,7 +145,7 @@
|
||||
.text img.topLevel {
|
||||
margin-top: .75rem;
|
||||
margin-bottom: .75rem;
|
||||
max-height: min(var(--height), 35vh);
|
||||
max-height: 35vh;
|
||||
}
|
||||
|
||||
img.fullScreen {
|
||||
|
12
components/use-client.js
Normal file
12
components/use-client.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
// https://usehooks-ts.com/react-hook/use-is-client#hook
|
||||
export function useIsClient () {
|
||||
const [isClient, setClient] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setClient(true)
|
||||
}, [])
|
||||
|
||||
return isClient
|
||||
}
|
@ -1,15 +1,16 @@
|
||||
import { getGetServerSideProps } from '@/api/ssrApollo'
|
||||
import { Form, ClientInput, ClientCheckbox, PasswordInput, CheckboxGroup } from '@/components/form'
|
||||
import { Form, ClientInput, PasswordInput, CheckboxGroup, Checkbox } from '@/components/form'
|
||||
import { CenterLayout } from '@/components/layout'
|
||||
import { WalletSecurityBanner } from '@/components/banners'
|
||||
import { WalletLogs } from '@/components/wallet-logger'
|
||||
import { useToast } from '@/components/toast'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useWallet, Status } from 'wallets'
|
||||
import { useWallet } from 'wallets'
|
||||
import Info from '@/components/info'
|
||||
import Text from '@/components/text'
|
||||
import { AutowithdrawSettings } from '@/components/autowithdraw-shared'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useIsClient } from '@/components/use-client'
|
||||
|
||||
const WalletButtonBar = dynamic(() => import('@/components/wallet-buttonbar.js'), { ssr: false })
|
||||
|
||||
@ -45,6 +46,7 @@ export default function WalletSettings () {
|
||||
{wallet.canSend && wallet.hasConfig > 0 && <WalletSecurityBanner />}
|
||||
<Form
|
||||
initial={initial}
|
||||
enableReinitialize
|
||||
{...validateProps}
|
||||
onSubmit={async ({ amount, ...values }) => {
|
||||
try {
|
||||
@ -70,9 +72,8 @@ export default function WalletSettings () {
|
||||
? <AutowithdrawSettings wallet={wallet} />
|
||||
: (
|
||||
<CheckboxGroup name='enabled'>
|
||||
<ClientCheckbox
|
||||
<Checkbox
|
||||
disabled={!wallet.isConfigured}
|
||||
initialValue={wallet.status === Status.Enabled}
|
||||
label='enabled'
|
||||
name='enabled'
|
||||
groupClassName='mb-0'
|
||||
@ -101,13 +102,15 @@ export default function WalletSettings () {
|
||||
}
|
||||
|
||||
function WalletFields ({ wallet: { config, fields, isConfigured } }) {
|
||||
const isClient = useIsClient()
|
||||
|
||||
return fields
|
||||
.map(({ name, label = '', type, help, optional, editable, clientOnly, serverOnly, ...props }, i) => {
|
||||
const rawProps = {
|
||||
...props,
|
||||
name,
|
||||
initialValue: config?.[name],
|
||||
readOnly: isConfigured && editable === false && !!config?.[name],
|
||||
readOnly: isClient && isConfigured && editable === false && !!config?.[name],
|
||||
groupClassName: props.hidden ? 'd-none' : undefined,
|
||||
label: label
|
||||
? (
|
||||
|
@ -3,8 +3,9 @@ import Layout from '@/components/layout'
|
||||
import styles from '@/styles/wallet.module.css'
|
||||
import Link from 'next/link'
|
||||
import { useWallets, walletPrioritySort } from 'wallets'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useIsClient } from '@/components/use-client'
|
||||
|
||||
const WalletCard = dynamic(() => import('@/components/wallet-card'), { ssr: false })
|
||||
|
||||
@ -29,17 +30,10 @@ async function reorder (wallets, sourceIndex, targetIndex) {
|
||||
export default function Wallet ({ ssrData }) {
|
||||
const { wallets } = useWallets()
|
||||
|
||||
const [mounted, setMounted] = useState(false)
|
||||
const isClient = useIsClient()
|
||||
const [sourceIndex, setSourceIndex] = useState(null)
|
||||
const [targetIndex, setTargetIndex] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
// mounted is required since draggable is false
|
||||
// for wallets only available on the client during SSR
|
||||
// and thus we need to render the component again on the client
|
||||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
const onDragStart = (i) => (e) => {
|
||||
// e.dataTransfer.dropEffect = 'move'
|
||||
// We can only use the DataTransfer API inside the drop event
|
||||
@ -94,7 +88,7 @@ export default function Wallet ({ ssrData }) {
|
||||
return walletPrioritySort(w1, w2)
|
||||
})
|
||||
.map((w, i) => {
|
||||
const draggable = mounted && w.enabled
|
||||
const draggable = isClient && w.enabled
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -42,9 +42,6 @@ A _server.js_ file is only required for wallets that support receiving by exposi
|
||||
>
|
||||
> If a wallet does not support paying invoices, this is all that client.js of this wallet does. The reason for this structure is to make sure the client does not import dependencies that can only be imported on the server and would thus break the build.
|
||||
|
||||
> [!WARNING]
|
||||
> Wallets that support spending **AND** receiving have not been tested yet. For now, only implement either the interface for spending **OR** receiving until this warning is removed.
|
||||
|
||||
> [!TIP]
|
||||
> Don't hesitate to use the implementation of existing wallets as a reference.
|
||||
|
||||
@ -173,6 +170,7 @@ The first argument is the [BOLT11 payment request](https://github.com/lightning/
|
||||
> As mentioned above, this file must exist for every wallet and at least reexport everything in index.js so make sure that the following line is included:
|
||||
>
|
||||
> ```js
|
||||
> // wallets/<wallet>/client.js
|
||||
> export * from 'wallets/<name>'
|
||||
> ```
|
||||
>
|
||||
@ -207,15 +205,16 @@ It should attempt to create a test invoice to make sure that this wallet can lat
|
||||
|
||||
Again, like `testSendPayment`, the first argument is the wallet configuration that we should validate and this should thrown an error if validation fails. However, unlike `testSendPayment`, the `context` argument here contains `me` (the user object) and `models` (the Prisma client).
|
||||
|
||||
- `createInvoice: async (amount: int, config, context) => Promise<bolt11: string>`
|
||||
- `createInvoice: async (invoiceParams, config, context) => Promise<bolt11: string>`
|
||||
|
||||
`createInvoice` will be called whenever this wallet should receive a payment. It should return a BOLT11 payment request. The first argument `amount` specifies the amount in satoshis. The second argument `config` is the current configuration of this wallet. The third argument `context` is the same as in `testCreateInvoice` except it also includes `lnd` which is the return value of [`authenticatedLndGrpc`](https://github.com/alexbosworth/ln-service?tab=readme-ov-file#authenticatedlndgrpc) using the SN node credentials.
|
||||
`createInvoice` will be called whenever this wallet should receive a payment. It should return a BOLT11 payment request. The first argument `invoiceParams` is an object that contains the invoice parameters. These include `msats`, `description`, `descriptionHash` and `expiry`. The second argument `config` is the current configuration of this wallet. The third argument `context` is the same as in `testCreateInvoice` except it also includes `lnd` which is the return value of [`authenticatedLndGrpc`](https://github.com/alexbosworth/ln-service?tab=readme-ov-file#authenticatedlndgrpc) using the SN node credentials.
|
||||
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Don't forget to include the following line:
|
||||
>
|
||||
> ```js
|
||||
> // wallets/<wallet>/server.js
|
||||
> export * from 'wallets/<name>'
|
||||
> ```
|
||||
>
|
||||
|
Loading…
x
Reference in New Issue
Block a user