From 57603a936f569c791a582ffcda915d2ce930f861 Mon Sep 17 00:00:00 2001 From: k00b Date: Thu, 24 Oct 2024 16:35:18 -0500 Subject: [PATCH] reorder priority --- api/resolvers/wallet.js | 9 +++++ api/typeDefs/wallet.js | 1 + fragments/wallet.js | 6 ++++ pages/settings/wallets/index.js | 62 ++++++++++++++++++--------------- wallets/index.js | 30 +++++++++++++--- 5 files changed, 74 insertions(+), 34 deletions(-) diff --git a/api/resolvers/wallet.js b/api/resolvers/wallet.js index c6273300..05ac369e 100644 --- a/api/resolvers/wallet.js +++ b/api/resolvers/wallet.js @@ -525,6 +525,15 @@ const resolvers = { } return { id } }, + setWalletPriority: async (parent, { id, priority }, { me, models }) => { + if (!me) { + throw new GqlAuthenticationError() + } + + await models.wallet.update({ where: { userId: me.id, id: Number(id) }, data: { priority } }) + + return true + }, removeWallet: async (parent, { id }, { me, models }) => { if (!me) { throw new GqlAuthenticationError() diff --git a/api/typeDefs/wallet.js b/api/typeDefs/wallet.js index c0a88ee4..c26a5b9d 100644 --- a/api/typeDefs/wallet.js +++ b/api/typeDefs/wallet.js @@ -81,6 +81,7 @@ const typeDefs = ` dropBolt11(id: ID): Withdrawl removeWallet(id: ID!): Boolean deleteWalletLogs(wallet: String): Boolean + setWalletPriority(id: ID!, priority: Int!): Boolean } type Wallet { diff --git a/fragments/wallet.js b/fragments/wallet.js index 3e1a445c..10e71e3d 100644 --- a/fragments/wallet.js +++ b/fragments/wallet.js @@ -190,3 +190,9 @@ export const WALLET_LOGS = gql` } } ` + +export const SET_WALLET_PRIORITY = gql` + mutation SetWalletPriority($id: ID!, $priority: Int!) { + setWalletPriority(id: $id, priority: $priority) + } +` diff --git a/pages/settings/wallets/index.js b/pages/settings/wallets/index.js index 54a50cb7..fa6487f4 100644 --- a/pages/settings/wallets/index.js +++ b/pages/settings/wallets/index.js @@ -3,65 +3,69 @@ import Layout from '@/components/layout' import styles from '@/styles/wallet.module.css' import Link from 'next/link' import { useWallets } from '@/wallets/index' -import { useState } from 'react' +import { useCallback, useState } from 'react' import { useIsClient } from '@/components/use-client' import WalletCard from '@/components/wallet-card' +import { useToast } from '@/components/toast' export const getServerSideProps = getGetServerSideProps({ authRequired: true }) -async function reorder (wallets, sourceIndex, targetIndex) { - const newOrder = [...wallets] - - const [source] = newOrder.splice(sourceIndex, 1) - const newTargetIndex = sourceIndex < targetIndex ? targetIndex - 1 : targetIndex - const append = sourceIndex < targetIndex - - newOrder.splice(newTargetIndex + (append ? 1 : 0), 0, source) - - await Promise.all( - newOrder.map((w, i) => - w.setPriority(i).catch(console.error) - ) - ) -} - export default function Wallet ({ ssrData }) { - const { wallets } = useWallets() - + const { wallets, setPriorities, reloadLocalWallets } = useWallets() + const toast = useToast() const isClient = useIsClient() const [sourceIndex, setSourceIndex] = useState(null) const [targetIndex, setTargetIndex] = useState(null) - const onDragStart = (i) => (e) => { + const reorder = useCallback(async (sourceIndex, targetIndex) => { + const newOrder = [...wallets.filter(w => w.config?.enabled)] + const [source] = newOrder.splice(sourceIndex, 1) + const newTargetIndex = sourceIndex < targetIndex ? targetIndex - 1 : targetIndex + + const priorities = newOrder.slice(0, newTargetIndex) + .concat(source) + .concat(newOrder.slice(newTargetIndex)) + .map((w, i) => ({ wallet: w, priority: i })) + + await setPriorities(priorities) + reloadLocalWallets() + }, [setPriorities, reloadLocalWallets, wallets]) + + const onDragStart = useCallback((i) => (e) => { // e.dataTransfer.dropEffect = 'move' // We can only use the DataTransfer API inside the drop event // see https://html.spec.whatwg.org/multipage/dnd.html#security-risks-in-the-drag-and-drop-model // e.dataTransfer.setData('text/plain', name) // That's why we use React state instead setSourceIndex(i) - } + }, [setSourceIndex]) - const onDragEnter = (i) => (e) => { + const onDragEnter = useCallback((i) => (e) => { setTargetIndex(i) - } + }, [setTargetIndex]) - const onDragEnd = async (e) => { + const onReorderError = useCallback((err) => { + console.error(err) + toast.danger('failed to reorder wallets') + }, [toast]) + + const onDragEnd = useCallback((e) => { setSourceIndex(null) setTargetIndex(null) if (sourceIndex === targetIndex) return - await reorder(wallets, sourceIndex, targetIndex) - } + reorder(sourceIndex, targetIndex).catch(onReorderError) + }, [sourceIndex, targetIndex, reorder, onReorderError]) - const onTouchStart = (i) => async (e) => { + const onTouchStart = useCallback((i) => (e) => { if (sourceIndex !== null) { - await reorder(wallets, sourceIndex, i) + reorder(sourceIndex, i).catch(onReorderError) setSourceIndex(null) } else { setSourceIndex(i) } - } + }, [sourceIndex, reorder, onReorderError]) return ( diff --git a/wallets/index.js b/wallets/index.js index d5b65434..44b482ad 100644 --- a/wallets/index.js +++ b/wallets/index.js @@ -1,9 +1,9 @@ import { useMe } from '@/components/me' -import { WALLETS } from '@/fragments/wallet' +import { SET_WALLET_PRIORITY, WALLETS } from '@/fragments/wallet' import { SSR } from '@/lib/constants' -import { useQuery } from '@apollo/client' +import { useMutation, useQuery } from '@apollo/client' import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react' -import { getStorageKey, getWalletByType, Status, walletPrioritySort, canSend } from './common' +import { getStorageKey, getWalletByType, Status, walletPrioritySort, canSend, isConfigured } from './common' import useVault from '@/components/vault/use-vault' import { useWalletLogger } from '@/components/wallet-logger' import { bolt11Tags } from '@/lib/bolt11' @@ -21,7 +21,8 @@ function useLocalWallets () { // form wallets into a list of { config, def } const wallets = walletDefs.map(w => { try { - const config = window.localStorage.getItem(getStorageKey(w.name, me?.id)) + const storageKey = getStorageKey(w.name, me?.id) + const config = window.localStorage.getItem(storageKey) return { def: w, config: JSON.parse(config) } } catch (e) { return null @@ -43,10 +44,12 @@ export function WalletsProvider ({ children }) { const { decrypt } = useVault() const { me } = useMe() const { wallets: localWallets, reloadLocalWallets } = useLocalWallets() + const [setWalletPriority] = useMutation(SET_WALLET_PRIORITY) const { data, refetch } = useQuery(WALLETS, SSR ? {} : { nextFetchPolicy: 'cache-and-network' }) + // refetch wallets when the vault key hash changes or wallets are updated useEffect(() => { if (me?.privates?.walletsUpdatedAt) { refetch() @@ -77,9 +80,26 @@ export function WalletsProvider ({ children }) { .map(w => ({ ...w, status: w.config?.enabled ? Status.Enabled : Status.Disabled })) }, [data?.wallets, localWallets]) + const setPriorities = useCallback(async (priorities) => { + for (const { wallet, priority } of priorities) { + if (!isConfigured(wallet)) { + throw new Error(`cannot set priority for unconfigured wallet: ${wallet.def.name}`) + } + + if (wallet.config?.id) { + await setWalletPriority({ variables: { id: wallet.config.id, priority } }) + } else { + const storageKey = getStorageKey(wallet.def.name, me?.id) + const config = window.localStorage.getItem(storageKey) + const newConfig = { ...JSON.parse(config), priority } + window.localStorage.setItem(storageKey, JSON.stringify(newConfig)) + } + } + }, [setWalletPriority, me?.id]) + // provides priority sorted wallets to children return ( - + {children} )