mirror of
https://github.com/Zproger/Aurum.git
synced 2026-10-16 12:36:40 +07:00
Свободное текстовое поле на холдинге — какой сети принадлежит конкретная монета (Ethereum, Tron и т.д.), с редактированием прямо в таблице и разбивкой % по сетям на отдельной вкладке. Помогает увидеть, что часть стейблкоинов лежит на рискованных сетях и стоит перевести на безопасные.
173 lines
5.5 KiB
TypeScript
173 lines
5.5 KiB
TypeScript
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
import {
|
|
addCryptoTransaction,
|
|
createCryptoHolding,
|
|
createCryptoPortfolio,
|
|
deleteCryptoHolding,
|
|
deleteCryptoPortfolio,
|
|
deleteCryptoTransaction,
|
|
fetchCrypto90dPerformance,
|
|
fetchCryptoHistory,
|
|
fetchCryptoHoldings,
|
|
fetchCryptoPortfolios,
|
|
fetchCryptoTransactions,
|
|
refreshCryptoPrices,
|
|
updateCryptoHolding,
|
|
updateCryptoHoldingRisk,
|
|
updateCryptoPortfolio,
|
|
updateCryptoTransaction,
|
|
} from "@/api/crypto";
|
|
import type {
|
|
CryptoHoldingCreateInput,
|
|
CryptoHoldingUpdateInput,
|
|
CryptoPortfolioInput,
|
|
CryptoRange,
|
|
CryptoTransactionInput,
|
|
RiskLevel,
|
|
} from "@/types";
|
|
|
|
function useInvalidateCrypto() {
|
|
const queryClient = useQueryClient();
|
|
return () => {
|
|
queryClient.invalidateQueries({ queryKey: ["crypto-holdings"] });
|
|
queryClient.invalidateQueries({ queryKey: ["crypto-transactions"] });
|
|
queryClient.invalidateQueries({ queryKey: ["crypto-history"] });
|
|
// A crypto holding is a normal Asset under the hood — Net Worth's own
|
|
// numbers change the moment one is added/traded/repriced/removed.
|
|
queryClient.invalidateQueries({ queryKey: ["net-worth-summary"] });
|
|
};
|
|
}
|
|
|
|
export function useCryptoPortfolios(includeArchived = false) {
|
|
return useQuery({
|
|
queryKey: ["crypto-portfolios", includeArchived],
|
|
queryFn: () => fetchCryptoPortfolios(includeArchived),
|
|
});
|
|
}
|
|
|
|
export function useCreateCryptoPortfolio() {
|
|
const queryClient = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: (input: CryptoPortfolioInput) => createCryptoPortfolio(input),
|
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["crypto-portfolios"] }),
|
|
});
|
|
}
|
|
|
|
export function useUpdateCryptoPortfolio() {
|
|
const queryClient = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: ({ id, input }: { id: number; input: Partial<CryptoPortfolioInput> }) =>
|
|
updateCryptoPortfolio(id, input),
|
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["crypto-portfolios"] }),
|
|
});
|
|
}
|
|
|
|
export function useDeleteCryptoPortfolio() {
|
|
const queryClient = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: (id: number) => deleteCryptoPortfolio(id),
|
|
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["crypto-portfolios"] }),
|
|
});
|
|
}
|
|
|
|
export function useCryptoHoldings(portfolioId?: number | null) {
|
|
return useQuery({
|
|
queryKey: ["crypto-holdings", portfolioId ?? null],
|
|
queryFn: () => fetchCryptoHoldings(portfolioId),
|
|
});
|
|
}
|
|
|
|
export function useCryptoHistory(range: CryptoRange, portfolioId?: number | null) {
|
|
return useQuery({
|
|
queryKey: ["crypto-history", range, portfolioId ?? null],
|
|
queryFn: () => fetchCryptoHistory(range, portfolioId),
|
|
});
|
|
}
|
|
|
|
// Only fires while the 90d range is actually selected — a real CoinGecko
|
|
// call per held coin (see get_90d_performance's docstring), not something
|
|
// to run on every Crypto tab visit the way 7d/30d/1y already do for free.
|
|
export function useCrypto90dPerformance(range: CryptoRange, portfolioId?: number | null) {
|
|
return useQuery({
|
|
queryKey: ["crypto-performance-90d", portfolioId ?? null],
|
|
queryFn: () => fetchCrypto90dPerformance(portfolioId),
|
|
enabled: range === "90d",
|
|
});
|
|
}
|
|
|
|
export function useCryptoTransactions(assetId: number | null) {
|
|
return useQuery({
|
|
queryKey: ["crypto-transactions", assetId],
|
|
queryFn: () => fetchCryptoTransactions(assetId!),
|
|
enabled: assetId !== null,
|
|
});
|
|
}
|
|
|
|
export function useRefreshCryptoPrices() {
|
|
const invalidate = useInvalidateCrypto();
|
|
// Always a bare call — sync covers every portfolio regardless of which
|
|
// tab is active (see refresh_prices' own docstring), so there's no
|
|
// portfolio_id variable for callers to pass here.
|
|
return useMutation({ mutationFn: () => refreshCryptoPrices(), onSuccess: invalidate });
|
|
}
|
|
|
|
export function useCreateCryptoHolding() {
|
|
const invalidate = useInvalidateCrypto();
|
|
return useMutation({
|
|
mutationFn: (input: CryptoHoldingCreateInput) => createCryptoHolding(input),
|
|
onSuccess: invalidate,
|
|
});
|
|
}
|
|
|
|
export function useAddCryptoTransaction() {
|
|
const invalidate = useInvalidateCrypto();
|
|
return useMutation({
|
|
mutationFn: ({ assetId, input }: { assetId: number; input: CryptoTransactionInput }) =>
|
|
addCryptoTransaction(assetId, input),
|
|
onSuccess: invalidate,
|
|
});
|
|
}
|
|
|
|
export function useUpdateCryptoTransaction() {
|
|
const invalidate = useInvalidateCrypto();
|
|
return useMutation({
|
|
mutationFn: ({ transactionId, input }: { transactionId: number; input: CryptoTransactionInput }) =>
|
|
updateCryptoTransaction(transactionId, input),
|
|
onSuccess: invalidate,
|
|
});
|
|
}
|
|
|
|
export function useDeleteCryptoTransaction() {
|
|
const invalidate = useInvalidateCrypto();
|
|
return useMutation({
|
|
mutationFn: (transactionId: number) => deleteCryptoTransaction(transactionId),
|
|
onSuccess: invalidate,
|
|
});
|
|
}
|
|
|
|
export function useDeleteCryptoHolding() {
|
|
const invalidate = useInvalidateCrypto();
|
|
return useMutation({
|
|
mutationFn: (assetId: number) => deleteCryptoHolding(assetId),
|
|
onSuccess: invalidate,
|
|
});
|
|
}
|
|
|
|
export function useUpdateCryptoHoldingRisk() {
|
|
const invalidate = useInvalidateCrypto();
|
|
return useMutation({
|
|
mutationFn: ({ assetId, riskLevel }: { assetId: number; riskLevel: RiskLevel }) =>
|
|
updateCryptoHoldingRisk(assetId, riskLevel),
|
|
onSuccess: invalidate,
|
|
});
|
|
}
|
|
|
|
export function useUpdateCryptoHolding() {
|
|
const invalidate = useInvalidateCrypto();
|
|
return useMutation({
|
|
mutationFn: ({ assetId, input }: { assetId: number; input: CryptoHoldingUpdateInput }) =>
|
|
updateCryptoHolding(assetId, input),
|
|
onSuccess: invalidate,
|
|
});
|
|
}
|