diff --git a/components/editor/plugins/mentions.js b/components/editor/plugins/mentions.js index f273ddc15..b4cb260e7 100644 --- a/components/editor/plugins/mentions.js +++ b/components/editor/plugins/mentions.js @@ -1,7 +1,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createPortal } from 'react-dom' import Dropdown from 'react-bootstrap/Dropdown' -import { useLazyQuery } from '@apollo/client/react' +import { useApolloClient } from '@apollo/client/react' import { LexicalTypeaheadMenuPlugin, MenuOption } from '@lexical/react/LexicalTypeaheadMenuPlugin' import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' import useDebounceCallback from '@/components/use-debounce-callback' @@ -18,12 +18,12 @@ const MENTION_PATTERN = /(^|\s|\()([@~]\w{0,75})$/ const MAX_SUGGESTIONS = 5 const SUGGESTION_DEBOUNCE_MS = 150 -function getSuggestionLookup (trigger, getUserSuggestions, getSubSuggestions) { +function getSuggestionLookup (trigger) { switch (trigger) { case '@': - return { getSuggestions: getUserSuggestions, itemsField: 'userSuggestions' } + return { query: USER_SUGGESTIONS, itemsField: 'userSuggestions' } case '~': - return { getSuggestions: getSubSuggestions, itemsField: 'subSuggestions' } + return { query: SUB_SUGGESTIONS, itemsField: 'subSuggestions' } default: return null } @@ -31,11 +31,10 @@ function getSuggestionLookup (trigger, getUserSuggestions, getSubSuggestions) { /** takes the full \@user or \~sub and fetches the suggestions */ function useSuggestions ({ query }) { + const client = useApolloClient() const [suggestions, setSuggestions] = useState([]) const requestIdRef = useRef(0) - const [getUserSuggestions] = useLazyQuery(USER_SUGGESTIONS) - const [getSubSuggestions] = useLazyQuery(SUB_SUGGESTIONS) const setCurrentSuggestions = useCallback((requestId, nextSuggestions = []) => { if (requestId === requestIdRef.current) { setSuggestions(nextSuggestions) @@ -43,7 +42,7 @@ function useSuggestions ({ query }) { }, []) const fetchSuggestions = useDebounceCallback(async (nextQuery, requestId) => { - const lookup = getSuggestionLookup(nextQuery[0], getUserSuggestions, getSubSuggestions) + const lookup = getSuggestionLookup(nextQuery[0]) if (!lookup) { setCurrentSuggestions(requestId) @@ -51,14 +50,15 @@ function useSuggestions ({ query }) { } try { - const { data } = await lookup.getSuggestions({ + const { data } = await client.query({ + query: lookup.query, variables: { q: nextQuery.slice(1), limit: MAX_SUGGESTIONS } }) setCurrentSuggestions(requestId, data?.[lookup.itemsField] || []) } catch { setCurrentSuggestions(requestId) } - }, SUGGESTION_DEBOUNCE_MS, [getUserSuggestions, getSubSuggestions, setCurrentSuggestions]) + }, SUGGESTION_DEBOUNCE_MS, [client, setCurrentSuggestions]) useEffect(() => { if (!query) { diff --git a/components/editor/plugins/upload.js b/components/editor/plugins/upload.js index ce114f348..7e050a426 100644 --- a/components/editor/plugins/upload.js +++ b/components/editor/plugins/upload.js @@ -1,5 +1,4 @@ import { useEffect, useRef, useCallback } from 'react' -import { isAbortError } from '@/lib/error' import { COMMAND_PRIORITY_EDITOR, $getRoot, @@ -29,6 +28,7 @@ import styles from '@/lib/lexical/theme/editor.module.css' import { $insertTextAtSelection } from '@/lib/lexical/utils' import { isMarkdownMode } from '@/lib/lexical/commands/utils' import { $createMediaNode, MediaNode } from '@/lib/lexical/nodes/content/media' +import { isAbortError } from '@/lib/error' // submit disabled reason for upload export const UPLOAD_SUBMIT_DISABLED_REASON = 'upload' @@ -263,8 +263,7 @@ function useLexicalUploadFees (editor) { const { merge } = useFeeButton() const [updateUploadFees] = useLazyQuery(UPLOAD_FEES_QUERY, { - fetchPolicy: 'no-cache', - nextFetchPolicy: 'no-cache' + fetchPolicy: 'no-cache' }) const handleUploadFeesData = useCallback(({ data }) => { diff --git a/components/form.js b/components/form.js index be1bc1244..0e2c88b73 100644 --- a/components/form.js +++ b/components/form.js @@ -1,7 +1,6 @@ import Button from 'react-bootstrap/Button' import InputGroup from 'react-bootstrap/InputGroup' import BootstrapForm from 'react-bootstrap/Form' -import { isAbortError } from '@/lib/error' import { Formik, Form as FormikForm, useFormikContext, useField, FieldArray } from 'formik' import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react' import copy from 'clipboard-copy' @@ -33,6 +32,7 @@ import dynamic from 'next/dynamic' import { useIsClient } from './use-client' import PageLoading from './page-loading' import { SNEditor } from './editor' +import { isAbortError } from '@/lib/error' export { MultiSelect } from './multi-select' export class SessionRequiredError extends Error { constructor () { @@ -489,6 +489,7 @@ export function BaseSuggest ({ resetSuggestions() } }, [query, resetSuggestions, getSuggestions]) + const onKeyDown = useCallback(e => { switch (e.code) { case 'ArrowUp': diff --git a/components/payIn/hooks/use-auto-retry-pay-ins.js b/components/payIn/hooks/use-auto-retry-pay-ins.js index 11914d7c3..831410c91 100644 --- a/components/payIn/hooks/use-auto-retry-pay-ins.js +++ b/components/payIn/hooks/use-auto-retry-pay-ins.js @@ -1,7 +1,6 @@ import { usePreferredSendProtocolId, useWalletPayment } from '@/wallets/client/hooks' -import { isAbortError } from '@/lib/error' import usePayInHelper from './use-pay-in-helper' -import { useLazyQuery } from '@apollo/client/react' +import { useApolloClient } from '@apollo/client/react' import { FAILED_PAY_INS } from '@/fragments/payIn' import { useMe } from '@/components/me' import { useEffect } from 'react' @@ -12,7 +11,7 @@ export function useAutoRetryPayIns () { const waitForWalletPayment = useWalletPayment() const sendProtocolId = usePreferredSendProtocolId() const payInHelper = usePayInHelper() - const [getFailedPayIns] = useLazyQuery(FAILED_PAY_INS, { fetchPolicy: 'network-only', nextFetchPolicy: 'network-only', errorPolicy: 'all' }) + const client = useApolloClient() const { me } = useMe() useEffect(() => { @@ -39,11 +38,15 @@ export function useAutoRetryPayIns () { let failedPayIns try { - const { data, error } = await getFailedPayIns() + const { data, error } = await client.query({ + query: FAILED_PAY_INS, + fetchPolicy: 'network-only', + errorPolicy: 'all' + }) if (error) throw error failedPayIns = data.failedPayIns } catch (err) { - !isAbortError(err) && console.error('failed to fetch invoices to retry:', err) + console.error('failed to fetch invoices to retry:', err) return } @@ -80,7 +83,7 @@ export function useAutoRetryPayIns () { queuePoll() return stopPolling - }, [me?.id, sendProtocolId, getFailedPayIns, payInHelper, waitForWalletPayment]) + }, [me?.id, sendProtocolId, client, payInHelper, waitForWalletPayment]) } export function isAutoRetryEligiblePayIn (payIn) { diff --git a/components/sub-select.js b/components/sub-select.js index 6e6b36ecb..d0349b54b 100644 --- a/components/sub-select.js +++ b/components/sub-select.js @@ -1,10 +1,9 @@ import { useEffect, useState } from 'react' -import { isAbortError } from '@/lib/error' import { useRouter } from 'next/router' import { MultiSelect, Select } from './form' import { EXTRA_LONG_POLL_INTERVAL_MS, SSR } from '@/lib/constants' import { ACTIVE_SUBS, SUB_FULL } from '@/fragments/subs' -import { useLazyQuery, useQuery } from '@apollo/client/react' +import { useApolloClient, useQuery } from '@apollo/client/react' import styles from './sub-select.module.css' import { useMe } from './me' import { useShowModal } from './modal' @@ -141,6 +140,7 @@ export default function SubSelect ({ prependSubs, sub, onChange, size, appendSub export function SubMultiSelect ({ prependSubs, subs, onChange, size, appendSubs, filterSubs, className, ...props }) { const router = useRouter() + const client = useApolloClient() const activeSubs = useSubs({ prependSubs, subs, filterSubs, appendSubs }) const valueProps = props.noForm ? { @@ -151,16 +151,18 @@ export function SubMultiSelect ({ prependSubs, subs, onChange, size, appendSubs, } const showModal = useShowModal() - const [getSub] = useLazyQuery(SUB_FULL) const handleTerritoryClick = async (subName) => { try { - const { data } = await getSub({ variables: { sub: subName } }) + const { data } = await client.query({ + query: SUB_FULL, + variables: { sub: subName } + }) if (data?.sub) { showModal(() => ) } } catch (err) { - !isAbortError(err) && console.error(err) + console.error(err) } } diff --git a/components/territory-form.js b/components/territory-form.js index f1d81edfe..216a692ce 100644 --- a/components/territory-form.js +++ b/components/territory-form.js @@ -1,5 +1,4 @@ import AccordianItem from './accordian-item' -import { isAbortError } from '@/lib/error' import { Col, InputGroup, Row, Form as BootstrapForm, Badge } from 'react-bootstrap' import { Checkbox, CheckboxGroup, Form, Input, SNInput, Range } from './form' import { useFormikContext } from 'formik' @@ -20,6 +19,7 @@ import Link from 'next/link' import usePayInMutation from '@/components/payIn/hooks/use-pay-in-mutation' import { UNARCHIVE_TERRITORY, UPSERT_SUB } from '@/fragments/payIn' import LinkExternal from '@/svgs/link-external.svg' +import { isAbortError } from '@/lib/error' function SatFilterRanges () { const { values } = useFormikContext() diff --git a/components/use-crossposter.js b/components/use-crossposter.js index fa06492f0..20792f4f0 100644 --- a/components/use-crossposter.js +++ b/components/use-crossposter.js @@ -1,10 +1,9 @@ import { useCallback } from 'react' -import { isAbortError } from '@/lib/error' import { useToast } from './toast' import { Button } from 'react-bootstrap' import Nostr, { DEFAULT_CROSSPOSTING_RELAYS } from '@/lib/nostr' import { gql } from '@apollo/client' -import { useLazyQuery, useMutation, useQuery } from '@apollo/client/react' +import { useApolloClient, useMutation, useQuery } from '@apollo/client/react' import { SETTINGS } from '@/fragments/users' import { ITEM_FULL_FIELDS, POLL_FIELDS } from '@/fragments/items' @@ -85,24 +84,11 @@ function bountyToEvent (item) { export default function useCrossposter () { const toaster = useToast() + const client = useApolloClient() const { data } = useQuery(SETTINGS) const userRelays = data?.settings?.privates?.nostrRelays || [] const relays = [...DEFAULT_CROSSPOSTING_RELAYS, ...userRelays] - const [fetchItem] = useLazyQuery( - gql` - ${ITEM_FULL_FIELDS} - ${POLL_FIELDS} - query Item($id: ID!) { - item(id: $id) { - ...ItemFullFields - ...PollFields - } - }`, { - fetchPolicy: 'no-cache' - } - ) - const [updateNoteId] = useMutation( gql` mutation updateNoteId($id: ID!, $noteId: String!) { @@ -186,11 +172,23 @@ export default function useCrossposter () { const fetchItemData = async (itemId) => { try { - const { data } = await fetchItem({ variables: { id: itemId } }) + const { data } = await client.query({ + query: gql` + ${ITEM_FULL_FIELDS} + ${POLL_FIELDS} + query Item($id: ID!) { + item(id: $id) { + ...ItemFullFields + ...PollFields + } + }`, + variables: { id: itemId }, + fetchPolicy: 'no-cache' + }) return data?.item } catch (e) { - !isAbortError(e) && console.error(e) + console.error(e) return null } } diff --git a/pages/items/[id]/edit.js b/pages/items/[id]/edit.js index 045712a4b..83d43aa2d 100644 --- a/pages/items/[id]/edit.js +++ b/pages/items/[id]/edit.js @@ -1,5 +1,4 @@ import { ITEM } from '@/fragments/items' -import { isAbortError } from '@/lib/error' import { getGetServerSideProps } from '@/api/ssrApollo' import { DiscussionForm } from '@/components/discussion-form' import { LinkForm } from '@/components/link-form' @@ -7,8 +6,8 @@ import { CenterLayout } from '@/components/layout' import JobForm from '@/components/job-form' import { PollForm } from '@/components/poll-form' import { BountyForm } from '@/components/bounty-form' -import { useEffect, useState } from 'react' -import { useLazyQuery, useQuery } from '@apollo/client/react' +import { useMemo, useState } from 'react' +import { useQuery } from '@apollo/client/react' import { useRouter } from 'next/router' import PageLoading from '@/components/page-loading' import { FeeButtonProvider } from '@/components/fee-button' @@ -26,49 +25,29 @@ export const getServerSideProps = getGetServerSideProps({ export default function PostEdit ({ ssrData }) { const router = useRouter() const { data } = useQuery(ITEM, { variables: { id: router.query.id } }) - const [fetchSubs] = useLazyQuery(SUBS) if (!data && !ssrData) return const { item } = data || ssrData const [subs, setSubs] = useState(item.subNames) - const [baseLineItems, setBaseLineItems] = useState({}) - useEffect(() => { - const territoryAddPrefix = 'territory-add-' - const addedSubs = subsDiff(subs, item.subNames) - if (!addedSubs.length) { - setBaseLineItems(prev => Object.entries(prev).reduce((acc, [key, value]) => { - if (!key.startsWith(territoryAddPrefix)) { - acc[key] = value - } - return acc - }, {})) - return - } - fetchSubs({ variables: { subNames: addedSubs } }).then(res => { - setBaseLineItems(prev => { - const newBaseLineItems = Object.entries(prev).reduce((acc, [key, value]) => { - if (!key.startsWith(territoryAddPrefix)) { - acc[key] = value - } - return acc - }, {}) - const territoryAdds = res.data.subs.reduce((acc, sub) => ({ - ...acc, - [`${territoryAddPrefix}${sub.name}`]: { - label: `~${sub.name} post`, - term: `+ ${sub.baseCost}`, - op: '+', - modifier: cost => cost + sub.baseCost - } - }), {}) - return { - ...newBaseLineItems, - ...territoryAdds - } - }) - }).catch(err => !isAbortError(err) && console.error(err)) - }, [subs, fetchSubs]) + const addedSubs = useMemo(() => subsDiff(subs, item.subNames), [subs, item.subNames]) + const { data: subsData } = useQuery(SUBS, { + variables: { subNames: addedSubs }, + skip: !addedSubs.length + }) + + const baseLineItems = useMemo(() => { + if (!addedSubs.length || !subsData?.subs) return {} + return subsData.subs.reduce((acc, sub) => ({ + ...acc, + [`territory-add-${sub.name}`]: { + label: `~${sub.name} post`, + term: `+ ${sub.baseCost}`, + op: '+', + modifier: cost => cost + sub.baseCost + } + }), {}) + }, [addedSubs, subsData]) const [,, editThreshold] = useCanEdit(item) const EditInfo = editThreshold && item.payIn?.payInState === 'PAID'