Compare commits

..

No commits in common. "d9024ff83722303c8ce6fed039fe3029534b6cf5" and "a7066a34cd2fec21ceaa05853cbadc119d0541d4" have entirely different histories.

10 changed files with 37 additions and 40 deletions

View File

@ -3,7 +3,6 @@ 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
@ -26,12 +25,15 @@ export function AutowithdrawSettings ({ wallet }) {
setSendThreshold(Math.max(Math.floor(threshold / 10), 1))
}, [autoWithdrawThreshold])
const isClient = useIsClient()
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
return (
<>
<Checkbox
disabled={isClient && !wallet.isConfigured}
disabled={mounted && !wallet.isConfigured}
label='enabled'
id='enabled'
name='enabled'

View File

@ -803,7 +803,7 @@ const StorageKeyPrefixContext = createContext()
export function Form ({
initial, validate, schema, onSubmit, children, initialError, validateImmediately,
storageKeyPrefix, validateOnChange = true, requireSession, innerRef, enableReinitialize,
storageKeyPrefix, validateOnChange = true, requireSession, innerRef,
...props
}) {
const toaster = useToast()
@ -855,7 +855,6 @@ export function Form ({
return (
<Formik
initialValues={initial}
enableReinitialize={enableReinitialize}
validateOnChange={validateOnChange}
validate={validate}
validationSchema={schema}

View File

@ -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')), 300)
timeoutId.current = setTimeout(() => setShow(!!popRef.current?.matches(':hover')), 500)
}
}
@ -23,7 +23,8 @@ export default function HoverablePopover ({ trigger, body, onShow }) {
trigger={['hover', 'focus']}
show={show}
onToggle={onToggle}
transition
delay={1}
transition={false}
rootClose
overlay={
<Popover style={{ position: 'fixed' }} onPointerLeave={() => onToggle(false)}>

View File

@ -57,7 +57,7 @@ function ImageOriginal ({ src, topLevel, rel, tab, children, onClick, ...props }
target='_blank'
rel={rel ?? UNKNOWN_LINK_REL}
href={src}
>{isRawURL || !children ? src : children}
>{isRawURL ? src : children}
</a>
)
}

View File

@ -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={topLevel} />
return <ZoomableImage srcSet={srcSet} tab={tab} src={src} rel={rel ?? UNKNOWN_LINK_REL} {...props} topLevel />
}, [imgproxyUrls, topLevel, tab])
return (

View File

@ -129,13 +129,16 @@
}
.text img {
--height: 35vh;
--width: 100%;
display: block;
margin-top: .5rem;
margin-bottom: .5rem;
max-width: 100%;
width: auto;
max-width: min(var(--width), 100%);
cursor: zoom-in;
height: auto;
max-height: 25vh;
max-height: min(var(--height), 25vh);
object-fit: contain;
object-position: left top;
min-width: 50%;
@ -145,7 +148,7 @@
.text img.topLevel {
margin-top: .75rem;
margin-bottom: .75rem;
max-height: 35vh;
max-height: min(var(--height), 35vh);
}
img.fullScreen {

View File

@ -1,12 +0,0 @@
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
}

View File

@ -1,16 +1,15 @@
import { getGetServerSideProps } from '@/api/ssrApollo'
import { Form, ClientInput, PasswordInput, CheckboxGroup, Checkbox } from '@/components/form'
import { Form, ClientInput, ClientCheckbox, PasswordInput, CheckboxGroup } 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 } from 'wallets'
import { useWallet, Status } 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 })
@ -46,7 +45,6 @@ export default function WalletSettings () {
{wallet.canSend && wallet.hasConfig > 0 && <WalletSecurityBanner />}
<Form
initial={initial}
enableReinitialize
{...validateProps}
onSubmit={async ({ amount, ...values }) => {
try {
@ -72,8 +70,9 @@ export default function WalletSettings () {
? <AutowithdrawSettings wallet={wallet} />
: (
<CheckboxGroup name='enabled'>
<Checkbox
<ClientCheckbox
disabled={!wallet.isConfigured}
initialValue={wallet.status === Status.Enabled}
label='enabled'
name='enabled'
groupClassName='mb-0'
@ -102,15 +101,13 @@ 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: isClient && isConfigured && editable === false && !!config?.[name],
readOnly: isConfigured && editable === false && !!config?.[name],
groupClassName: props.hidden ? 'd-none' : undefined,
label: label
? (

View File

@ -3,9 +3,8 @@ import Layout from '@/components/layout'
import styles from '@/styles/wallet.module.css'
import Link from 'next/link'
import { useWallets, walletPrioritySort } from 'wallets'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import dynamic from 'next/dynamic'
import { useIsClient } from '@/components/use-client'
const WalletCard = dynamic(() => import('@/components/wallet-card'), { ssr: false })
@ -30,10 +29,17 @@ async function reorder (wallets, sourceIndex, targetIndex) {
export default function Wallet ({ ssrData }) {
const { wallets } = useWallets()
const isClient = useIsClient()
const [mounted, setMounted] = useState(false)
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
@ -88,7 +94,7 @@ export default function Wallet ({ ssrData }) {
return walletPrioritySort(w1, w2)
})
.map((w, i) => {
const draggable = isClient && w.enabled
const draggable = mounted && w.enabled
return (
<div

View File

@ -42,6 +42,9 @@ 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.
@ -170,7 +173,6 @@ 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>'
> ```
>
@ -205,16 +207,15 @@ 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 (invoiceParams, config, context) => Promise<bolt11: string>`
- `createInvoice: async (amount: int, 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 `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.
`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.
> [!IMPORTANT]
> Don't forget to include the following line:
>
> ```js
> // wallets/<wallet>/server.js
> export * from 'wallets/<name>'
> ```
>