Save dedicated enabled flag for server wallets
* wallet table now contains boolean column 'enabled' * 'priority' is now a number everywhere * use consistent order between how autowithdrawals are attempted and server wallets cards
This commit is contained in:
parent
8acf74c787
commit
9bbf2056e9
|
@ -627,7 +627,7 @@ async function upsertWallet (
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id, ...walletData } = data
|
const { id, ...walletData } = data
|
||||||
const { autoWithdrawThreshold, autoWithdrawMaxFeePercent, priority } = settings
|
const { autoWithdrawThreshold, autoWithdrawMaxFeePercent, enabled, priority } = settings
|
||||||
|
|
||||||
const txs = [
|
const txs = [
|
||||||
models.user.update({
|
models.user.update({
|
||||||
|
@ -639,24 +639,13 @@ async function upsertWallet (
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
||||||
if (priority) {
|
|
||||||
txs.push(
|
|
||||||
models.wallet.updateMany({
|
|
||||||
where: {
|
|
||||||
userId: me.id
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
priority: 0
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
txs.push(
|
txs.push(
|
||||||
models.wallet.update({
|
models.wallet.update({
|
||||||
where: { id: Number(id), userId: me.id },
|
where: { id: Number(id), userId: me.id },
|
||||||
data: {
|
data: {
|
||||||
priority: priority ? 1 : 0,
|
enabled,
|
||||||
|
priority,
|
||||||
[wallet.field]: {
|
[wallet.field]: {
|
||||||
update: {
|
update: {
|
||||||
where: { walletId: Number(id) },
|
where: { walletId: Number(id) },
|
||||||
|
@ -670,7 +659,8 @@ async function upsertWallet (
|
||||||
txs.push(
|
txs.push(
|
||||||
models.wallet.create({
|
models.wallet.create({
|
||||||
data: {
|
data: {
|
||||||
priority: Number(priority),
|
enabled,
|
||||||
|
priority,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
type: wallet.type,
|
type: wallet.type,
|
||||||
[wallet.field]: {
|
[wallet.field]: {
|
||||||
|
@ -694,8 +684,8 @@ async function upsertWallet (
|
||||||
data: {
|
data: {
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
wallet: wallet.type,
|
wallet: wallet.type,
|
||||||
level: priority ? 'SUCCESS' : 'INFO',
|
level: enabled ? 'SUCCESS' : 'INFO',
|
||||||
message: priority ? 'wallet enabled' : 'wallet disabled'
|
message: enabled ? 'wallet enabled' : 'wallet disabled'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -30,7 +30,8 @@ export default gql`
|
||||||
id: ID!
|
id: ID!
|
||||||
createdAt: Date!
|
createdAt: Date!
|
||||||
type: String!
|
type: String!
|
||||||
priority: Boolean!
|
enabled: Boolean!
|
||||||
|
priority: Int!
|
||||||
wallet: WalletDetails!
|
wallet: WalletDetails!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +56,8 @@ export default gql`
|
||||||
input AutowithdrawSettings {
|
input AutowithdrawSettings {
|
||||||
autoWithdrawThreshold: Int!
|
autoWithdrawThreshold: Int!
|
||||||
autoWithdrawMaxFeePercent: Float!
|
autoWithdrawMaxFeePercent: Float!
|
||||||
priority: Boolean!
|
priority: Int
|
||||||
|
enabled: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type Invoice {
|
type Invoice {
|
||||||
|
|
|
@ -8,9 +8,8 @@ function autoWithdrawThreshold ({ me }) {
|
||||||
return isNumber(me?.privates?.autoWithdrawThreshold) ? me?.privates?.autoWithdrawThreshold : 10000
|
return isNumber(me?.privates?.autoWithdrawThreshold) ? me?.privates?.autoWithdrawThreshold : 10000
|
||||||
}
|
}
|
||||||
|
|
||||||
export function autowithdrawInitial ({ me, priority = false }) {
|
export function autowithdrawInitial ({ me }) {
|
||||||
return {
|
return {
|
||||||
priority,
|
|
||||||
autoWithdrawThreshold: autoWithdrawThreshold({ me }),
|
autoWithdrawThreshold: autoWithdrawThreshold({ me }),
|
||||||
autoWithdrawMaxFeePercent: isNumber(me?.privates?.autoWithdrawMaxFeePercent) ? me?.privates?.autoWithdrawMaxFeePercent : 1
|
autoWithdrawMaxFeePercent: isNumber(me?.privates?.autoWithdrawMaxFeePercent) ? me?.privates?.autoWithdrawMaxFeePercent : 1
|
||||||
}
|
}
|
||||||
|
@ -31,8 +30,8 @@ export function AutowithdrawSettings ({ wallet }) {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
disabled={!wallet.isConfigured}
|
disabled={!wallet.isConfigured}
|
||||||
label='enabled'
|
label='enabled'
|
||||||
id='priority'
|
id='enabled'
|
||||||
name='priority'
|
name='enabled'
|
||||||
/>
|
/>
|
||||||
<div className='my-4 border border-3 rounded'>
|
<div className='my-4 border border-3 rounded'>
|
||||||
<div className='p-3'>
|
<div className='p-3'>
|
||||||
|
|
|
@ -32,7 +32,7 @@ export function useWallet (name) {
|
||||||
const [config, saveConfig, clearConfig] = useConfig(wallet)
|
const [config, saveConfig, clearConfig] = useConfig(wallet)
|
||||||
const _isConfigured = isConfigured({ ...wallet, config })
|
const _isConfigured = isConfigured({ ...wallet, config })
|
||||||
|
|
||||||
const status = (config?.enabled || config?.priority) ? Status.Enabled : Status.Initialized
|
const status = config?.enabled ? Status.Enabled : Status.Initialized
|
||||||
const enabled = status === Status.Enabled
|
const enabled = status === Status.Enabled
|
||||||
const priority = config?.priority
|
const priority = config?.priority
|
||||||
|
|
||||||
|
@ -81,9 +81,9 @@ export function useWallet (name) {
|
||||||
}, [_isConfigured, saveConfig, me, logger])
|
}, [_isConfigured, saveConfig, me, logger])
|
||||||
|
|
||||||
// delete is a reserved keyword
|
// delete is a reserved keyword
|
||||||
const delete_ = useCallback(() => {
|
const delete_ = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
clearConfig()
|
await clearConfig()
|
||||||
logger.ok('wallet detached')
|
logger.ok('wallet detached')
|
||||||
disable()
|
disable()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -160,14 +160,20 @@ function useServerConfig (wallet) {
|
||||||
const { data, refetch: refetchConfig } = useQuery(WALLET_BY_TYPE, { variables: { type: wallet?.server?.walletType }, skip: !wallet?.server })
|
const { data, refetch: refetchConfig } = useQuery(WALLET_BY_TYPE, { variables: { type: wallet?.server?.walletType }, skip: !wallet?.server })
|
||||||
|
|
||||||
const walletId = data?.walletByType?.id
|
const walletId = data?.walletByType?.id
|
||||||
const serverConfig = { id: walletId, priority: data?.walletByType?.priority, ...data?.walletByType?.wallet }
|
const serverConfig = {
|
||||||
const autowithdrawSettings = autowithdrawInitial({ me, priority: serverConfig?.priority })
|
id: walletId,
|
||||||
|
priority: data?.walletByType?.priority,
|
||||||
|
enabled: data?.walletByType?.enabled,
|
||||||
|
...data?.walletByType?.wallet
|
||||||
|
}
|
||||||
|
const autowithdrawSettings = autowithdrawInitial({ me })
|
||||||
const config = { ...serverConfig, ...autowithdrawSettings }
|
const config = { ...serverConfig, ...autowithdrawSettings }
|
||||||
|
|
||||||
const saveConfig = useCallback(async ({
|
const saveConfig = useCallback(async ({
|
||||||
autoWithdrawThreshold,
|
autoWithdrawThreshold,
|
||||||
autoWithdrawMaxFeePercent,
|
autoWithdrawMaxFeePercent,
|
||||||
priority,
|
priority,
|
||||||
|
enabled,
|
||||||
...config
|
...config
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
|
@ -179,7 +185,8 @@ function useServerConfig (wallet) {
|
||||||
settings: {
|
settings: {
|
||||||
autoWithdrawThreshold: Number(autoWithdrawThreshold),
|
autoWithdrawThreshold: Number(autoWithdrawThreshold),
|
||||||
autoWithdrawMaxFeePercent: Number(autoWithdrawMaxFeePercent),
|
autoWithdrawMaxFeePercent: Number(autoWithdrawMaxFeePercent),
|
||||||
priority: !!priority
|
priority,
|
||||||
|
enabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -160,6 +160,7 @@ export const WALLET_BY_TYPE = gql`
|
||||||
walletByType(type: $type) {
|
walletByType(type: $type) {
|
||||||
id
|
id
|
||||||
createdAt
|
createdAt
|
||||||
|
enabled
|
||||||
priority
|
priority
|
||||||
type
|
type
|
||||||
wallet {
|
wallet {
|
||||||
|
|
|
@ -423,7 +423,7 @@ export function CLNAutowithdrawSchema ({ me } = {}) {
|
||||||
|
|
||||||
export function autowithdrawSchemaMembers ({ me } = {}) {
|
export function autowithdrawSchemaMembers ({ me } = {}) {
|
||||||
return {
|
return {
|
||||||
priority: boolean(),
|
enabled: boolean(),
|
||||||
autoWithdrawThreshold: intValidator.required('required').min(0, 'must be at least 0').max(msatsToSats(BALANCE_LIMIT_MSATS), `must be at most ${abbrNum(msatsToSats(BALANCE_LIMIT_MSATS))}`),
|
autoWithdrawThreshold: intValidator.required('required').min(0, 'must be at least 0').max(msatsToSats(BALANCE_LIMIT_MSATS), `must be at most ${abbrNum(msatsToSats(BALANCE_LIMIT_MSATS))}`),
|
||||||
autoWithdrawMaxFeePercent: floatValidator.required('required').min(0, 'must be at least 0').max(50, 'must not exceed 50')
|
autoWithdrawMaxFeePercent: floatValidator.required('required').min(0, 'must be at least 0').max(50, 'must not exceed 50')
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,11 @@ export default function WalletSettings () {
|
||||||
const wallet = useWallet(name)
|
const wallet = useWallet(name)
|
||||||
|
|
||||||
const initial = wallet.fields.reduce((acc, field) => {
|
const initial = wallet.fields.reduce((acc, field) => {
|
||||||
// we still need to run over all wallet fields via reduce
|
// We still need to run over all wallet fields via reduce
|
||||||
// even though we use wallet.config as the initial value
|
// even though we use wallet.config as the initial value
|
||||||
// since wallet.config is empty when wallet is not configured
|
// since wallet.config is empty when wallet is not configured.
|
||||||
|
// Also, wallet.config includes general fields like
|
||||||
|
// 'enabled' and 'priority' which are not defined in wallet.fields.
|
||||||
return {
|
return {
|
||||||
...acc,
|
...acc,
|
||||||
[field.name]: wallet.config?.[field.name] || ''
|
[field.name]: wallet.config?.[field.name] || ''
|
||||||
|
@ -39,22 +41,18 @@ export default function WalletSettings () {
|
||||||
<Form
|
<Form
|
||||||
initial={initial}
|
initial={initial}
|
||||||
schema={wallet.schema}
|
schema={wallet.schema}
|
||||||
onSubmit={async ({ enabled, ...values }) => {
|
onSubmit={async (values) => {
|
||||||
try {
|
try {
|
||||||
const newConfig = !wallet.isConfigured
|
const newConfig = !wallet.isConfigured
|
||||||
|
|
||||||
// enable wallet if wallet was just configured
|
// enable wallet if wallet was just configured
|
||||||
// local wallets use 'enabled' property
|
|
||||||
// server wallets use 'priority' property
|
|
||||||
// TODO: make both wallet types use 'priority' property
|
|
||||||
if (newConfig) {
|
if (newConfig) {
|
||||||
values.priority = true
|
values.enabled = true
|
||||||
enabled = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await wallet.save(values)
|
await wallet.save(values)
|
||||||
|
|
||||||
if (enabled) wallet.enable()
|
if (values.enabled) wallet.enable()
|
||||||
else wallet.disable()
|
else wallet.disable()
|
||||||
|
|
||||||
toaster.success('saved settings')
|
toaster.success('saved settings')
|
||||||
|
@ -80,7 +78,7 @@ export default function WalletSettings () {
|
||||||
<WalletButtonBar
|
<WalletButtonBar
|
||||||
wallet={wallet} onDelete={async () => {
|
wallet={wallet} onDelete={async () => {
|
||||||
try {
|
try {
|
||||||
wallet.delete()
|
await wallet.delete()
|
||||||
toaster.success('saved settings')
|
toaster.success('saved settings')
|
||||||
router.push('/settings/wallets')
|
router.push('/settings/wallets')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -64,10 +64,23 @@ export default function Wallet ({ ssrData }) {
|
||||||
<div className={styles.walletGrid} onDragEnd={onDragEnd}>
|
<div className={styles.walletGrid} onDragEnd={onDragEnd}>
|
||||||
{wallets
|
{wallets
|
||||||
.sort((w1, w2) => {
|
.sort((w1, w2) => {
|
||||||
if (!w2.isConfigured || !w2.enabled) {
|
// enabled/configured wallets always come before disabled/unconfigured wallets
|
||||||
|
if ((w1.enabled && !w2.enabled) || (w1.isConfigured && !w2.isConfigured)) {
|
||||||
return -1
|
return -1
|
||||||
|
} else if ((w2.enabled && !w1.enabled) || (w2.isConfigured && !w1.isConfigured)) {
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
return w1.priority - w2.priority
|
|
||||||
|
const delta = w1.priority - w2.priority
|
||||||
|
// delta is NaN if either priority is undefined
|
||||||
|
if (!Number.isNaN(delta) && delta !== 0) return delta
|
||||||
|
|
||||||
|
// if both wallets have an id, use that as tie breaker
|
||||||
|
// since that's the order in which autowithdrawals are attempted
|
||||||
|
if (w1.id && w2.id) return Number(w1.id) - Number(w2.id)
|
||||||
|
|
||||||
|
// else we will use the card title as tie breaker
|
||||||
|
return w1.card.title < w2.card.title ? -1 : 1
|
||||||
})
|
})
|
||||||
.map((w, i) => {
|
.map((w, i) => {
|
||||||
const draggable = w.isConfigured
|
const draggable = w.isConfigured
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Wallet" ADD COLUMN "enabled" BOOLEAN NOT NULL DEFAULT true;
|
|
@ -173,6 +173,7 @@ model Wallet {
|
||||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
||||||
userId Int
|
userId Int
|
||||||
label String?
|
label String?
|
||||||
|
enabled Boolean @default(true)
|
||||||
priority Int @default(0)
|
priority Int @default(0)
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,11 @@ export async function autoWithdraw ({ data: { id }, models, lnd }) {
|
||||||
// get the wallets in order of priority
|
// get the wallets in order of priority
|
||||||
const wallets = await models.wallet.findMany({
|
const wallets = await models.wallet.findMany({
|
||||||
where: { userId: user.id },
|
where: { userId: user.id },
|
||||||
orderBy: { priority: 'desc' }
|
orderBy: [
|
||||||
|
{ priority: 'desc' },
|
||||||
|
// use id as tie breaker (older wallet first)
|
||||||
|
{ id: 'asc' }
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
for (const wallet of wallets) {
|
for (const wallet of wallets) {
|
||||||
|
|
Loading…
Reference in New Issue