Fix caching and loop query on the get current user
This commit is contained in:
105
src/Managing.WebApp/src/app/store/userStore.tsx
Normal file
105
src/Managing.WebApp/src/app/store/userStore.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import {create} from 'zustand'
|
||||
import {useQuery, useQueryClient} from '@tanstack/react-query'
|
||||
import {User, UserClient} from '../../generated/ManagingApi'
|
||||
import useApiUrlStore from './apiStore'
|
||||
import useCookie from '../../hooks/useCookie'
|
||||
// Import React for useEffect
|
||||
import React from 'react'
|
||||
|
||||
interface UserStore {
|
||||
currentUser: User | null
|
||||
isLoading: boolean
|
||||
error: Error | null
|
||||
setCurrentUser: (user: User | null) => void
|
||||
setLoading: (loading: boolean) => void
|
||||
setError: (error: Error | null) => void
|
||||
clearUser: () => void
|
||||
}
|
||||
|
||||
export const useUserStore = create<UserStore>((set, get) => ({
|
||||
currentUser: null,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
setCurrentUser: (user: User | null) => set({ currentUser: user }),
|
||||
setLoading: (loading: boolean) => set({ isLoading: loading }),
|
||||
setError: (error: Error | null) => set({ error }),
|
||||
clearUser: () => set({ currentUser: null, error: null }),
|
||||
}))
|
||||
|
||||
// Custom hook that integrates TanStack Query with Zustand store
|
||||
export const useCurrentUser = () => {
|
||||
const { apiUrl } = useApiUrlStore()
|
||||
const { getCookie } = useCookie()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
// Get JWT token from cookies
|
||||
const jwtToken = getCookie('token')
|
||||
|
||||
// TanStack Query for fetching user data
|
||||
const query = useQuery({
|
||||
queryKey: ['currentUser'],
|
||||
queryFn: async (): Promise<User> => {
|
||||
const userClient = new UserClient({}, apiUrl)
|
||||
return await userClient.user_GetCurrentUser()
|
||||
},
|
||||
enabled: !!jwtToken, // Only fetch when JWT token exists
|
||||
staleTime: 5 * 60 * 1000, // Consider data fresh for 5 minutes
|
||||
gcTime: 10 * 60 * 1000, // Keep in cache for 10 minutes
|
||||
retry: 2,
|
||||
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
|
||||
})
|
||||
|
||||
// Zustand store state
|
||||
const store = useUserStore()
|
||||
|
||||
// Sync TanStack Query state with Zustand store
|
||||
React.useEffect(() => {
|
||||
if (query.data) {
|
||||
store.setCurrentUser(query.data)
|
||||
}
|
||||
store.setLoading(query.isLoading)
|
||||
store.setError(query.error as Error | null)
|
||||
}, [query.data, query.isLoading, query.error, store])
|
||||
|
||||
// Return both TanStack Query data and store state for flexibility
|
||||
return {
|
||||
// TanStack Query data (preferred for most use cases)
|
||||
user: query.data,
|
||||
isLoading: query.isLoading,
|
||||
error: query.error,
|
||||
refetch: query.refetch,
|
||||
|
||||
// Store state (for components that need it)
|
||||
storeUser: store.currentUser,
|
||||
storeLoading: store.isLoading,
|
||||
storeError: store.error,
|
||||
|
||||
// Utility functions
|
||||
clearCache: () => {
|
||||
queryClient.removeQueries({ queryKey: ['currentUser'] })
|
||||
store.clearUser()
|
||||
},
|
||||
invalidateCache: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['currentUser'] })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Hook for components that only need the user data without loading states
|
||||
export const useCurrentUserData = () => {
|
||||
const { user } = useCurrentUser()
|
||||
return user
|
||||
}
|
||||
|
||||
// Hook for components that need to check if current user owns something
|
||||
export const useIsBotOwner = (botAgentName: string) => {
|
||||
const { user } = useCurrentUser()
|
||||
return user?.agentName === botAgentName
|
||||
}
|
||||
|
||||
// Hook for getting just the agent name
|
||||
export const useCurrentAgentName = () => {
|
||||
const { user } = useCurrentUser()
|
||||
return user?.agentName || null
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import {ChartBarIcon, CogIcon, EyeIcon, PlayIcon, PlusCircleIcon, StopIcon, Tras
|
||||
import React, {useState} from 'react'
|
||||
|
||||
import useApiUrlStore from '../../app/store/apiStore'
|
||||
import {useIsBotOwner} from '../../app/store/userStore'
|
||||
import {CardPosition, CardSignal, CardText, Toast,} from '../../components/mollecules'
|
||||
import ManualPositionModal from '../../components/mollecules/ManualPositionModal'
|
||||
import TradesModal from '../../components/mollecules/TradesModal/TradesModal'
|
||||
@@ -12,13 +13,10 @@ import {
|
||||
MoneyManagement,
|
||||
Position,
|
||||
TradingBotConfig,
|
||||
TradingBotResponse,
|
||||
UserClient
|
||||
TradingBotResponse
|
||||
} from '../../generated/ManagingApi'
|
||||
import type {IBotList} from '../../global/type.tsx'
|
||||
import MoneyManagementModal from '../settingsPage/moneymanagement/moneyManagementModal'
|
||||
import {useQuery} from '@tanstack/react-query'
|
||||
import useCookie from '../../hooks/useCookie'
|
||||
|
||||
function baseBadgeClass(isOutlined = false) {
|
||||
let classes = 'text-xs badge badge-sm transition-all duration-200 hover:scale-105 '
|
||||
@@ -53,17 +51,9 @@ function cardClasses(botStatus: BotStatus) {
|
||||
const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
const { apiUrl } = useApiUrlStore()
|
||||
const client = new BotClient({}, apiUrl)
|
||||
const userClient = new UserClient({}, apiUrl)
|
||||
const { getCookie } = useCookie()
|
||||
|
||||
// Get JWT token from cookies
|
||||
const jwtToken = getCookie('token')
|
||||
|
||||
const { data: currentUser } = useQuery({
|
||||
queryFn: () => userClient.user_GetCurrentUser(),
|
||||
queryKey: ['currentUser'],
|
||||
enabled: !!jwtToken, // Only fetch when JWT token exists
|
||||
})
|
||||
// Use the new user store hook for bot ownership checking
|
||||
const checkIsBotOwner = useIsBotOwner
|
||||
|
||||
const [showMoneyManagementModal, setShowMoneyManagementModal] =
|
||||
useState(false)
|
||||
@@ -79,11 +69,6 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
config: TradingBotConfig
|
||||
} | null>(null)
|
||||
|
||||
// Helper function to check if current user owns the bot
|
||||
const isBotOwner = (botAgentName: string): boolean => {
|
||||
return currentUser?.agentName === botAgentName
|
||||
}
|
||||
|
||||
function getDeleteBadge(identifier: string) {
|
||||
const classes = baseBadgeClass() + 'bg-error'
|
||||
return (
|
||||
@@ -259,7 +244,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
||||
<div className="flex flex-wrap gap-1 sm:gap-2">
|
||||
|
||||
{/* Action Badges - Only show for bot owners */}
|
||||
{isBotOwner(bot.agentName) && (
|
||||
{checkIsBotOwner(bot.agentName) && (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{getToggleBotStatusBadge(bot.status as BotStatus, bot.identifier)}
|
||||
{getUpdateBotBadge(bot)}
|
||||
|
||||
@@ -2,11 +2,12 @@ import {ViewGridAddIcon} from '@heroicons/react/solid'
|
||||
import React, {useState} from 'react'
|
||||
import 'react-toastify/dist/ReactToastify.css'
|
||||
import useApiUrlStore from '../../app/store/apiStore'
|
||||
import {useCurrentAgentName} from '../../app/store/userStore'
|
||||
import {UnifiedTradingModal} from '../../components/organism'
|
||||
import {BotClient, BotSortableColumn, BotStatus, UserClient} from '../../generated/ManagingApi'
|
||||
import {BotClient, BotSortableColumn, BotStatus} from '../../generated/ManagingApi'
|
||||
import {useQuery} from '@tanstack/react-query'
|
||||
|
||||
import BotList from './botList'
|
||||
import {useQuery} from '@tanstack/react-query'
|
||||
|
||||
const Bots: React.FC = () => {
|
||||
const [activeTab, setActiveTab] = useState(0)
|
||||
@@ -21,12 +22,9 @@ const Bots: React.FC = () => {
|
||||
}
|
||||
const { apiUrl } = useApiUrlStore()
|
||||
const botClient = new BotClient({}, apiUrl)
|
||||
const userClient = new UserClient({}, apiUrl)
|
||||
|
||||
const { data: currentUser } = useQuery({
|
||||
queryFn: () => userClient.user_GetCurrentUser(),
|
||||
queryKey: ['currentUser'],
|
||||
})
|
||||
|
||||
// Use the new user store hook to get current agent name
|
||||
const currentAgentName = useCurrentAgentName()
|
||||
|
||||
// Query for paginated bots using the new endpoint
|
||||
const { data: paginatedBots } = useQuery({
|
||||
@@ -35,17 +33,17 @@ const Bots: React.FC = () => {
|
||||
case 0: // All Active Bots
|
||||
return botClient.bot_GetBotsPaginated(pageNumber, pageSize, BotStatus.Running, undefined, undefined, undefined, BotSortableColumn.Roi, 'Desc')
|
||||
case 1: // My Active Bots
|
||||
return botClient.bot_GetBotsPaginated(pageNumber, pageSize, BotStatus.Running, undefined, undefined, currentUser?.agentName, BotSortableColumn.Roi, 'Desc')
|
||||
return botClient.bot_GetBotsPaginated(pageNumber, pageSize, BotStatus.Running, undefined, undefined, currentAgentName, BotSortableColumn.Roi, 'Desc')
|
||||
case 2: // My Down Bots
|
||||
return botClient.bot_GetBotsPaginated(pageNumber, pageSize, BotStatus.Stopped, undefined, undefined, currentUser?.agentName, BotSortableColumn.Roi, 'Desc')
|
||||
return botClient.bot_GetBotsPaginated(pageNumber, pageSize, BotStatus.Stopped, undefined, undefined, currentAgentName, BotSortableColumn.Roi, 'Desc')
|
||||
case 3: // Saved Bots
|
||||
return botClient.bot_GetBotsPaginated(pageNumber, pageSize, BotStatus.Saved, undefined, undefined, currentUser?.agentName, BotSortableColumn.Roi, 'Desc')
|
||||
return botClient.bot_GetBotsPaginated(pageNumber, pageSize, BotStatus.Saved, undefined, undefined, currentAgentName, BotSortableColumn.Roi, 'Desc')
|
||||
default:
|
||||
return botClient.bot_GetBotsPaginated(pageNumber, pageSize, undefined, undefined, undefined, undefined, BotSortableColumn.Roi, 'Desc')
|
||||
}
|
||||
},
|
||||
queryKey: ['paginatedBots', activeTab, pageNumber, pageSize, currentUser?.agentName],
|
||||
enabled: !!currentUser,
|
||||
queryKey: ['paginatedBots', activeTab, pageNumber, pageSize, currentAgentName],
|
||||
enabled: !!currentAgentName,
|
||||
})
|
||||
|
||||
const filteredBots = paginatedBots?.items || []
|
||||
|
||||
@@ -32,7 +32,7 @@ function UserInfoSettings() {
|
||||
const jwtToken = getCookie('token')
|
||||
|
||||
const { data: user } = useQuery({
|
||||
queryKey: ['user'],
|
||||
queryKey: ['currentUser'],
|
||||
queryFn: () => api.user_GetCurrentUser(),
|
||||
enabled: !!jwtToken, // Only fetch when JWT token exists
|
||||
})
|
||||
@@ -59,7 +59,7 @@ function UserInfoSettings() {
|
||||
const toast = new Toast('Updating agent name')
|
||||
try {
|
||||
await api.user_UpdateAgentName(data.agentName)
|
||||
queryClient.invalidateQueries({ queryKey: ['user'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['currentUser'] })
|
||||
setShowUpdateModal(false)
|
||||
toast.update('success', 'Agent name updated successfully')
|
||||
} catch (error) {
|
||||
@@ -72,7 +72,7 @@ function UserInfoSettings() {
|
||||
const toast = new Toast('Updating avatar')
|
||||
try {
|
||||
await api.user_UpdateAvatarUrl(data.avatarUrl)
|
||||
queryClient.invalidateQueries({ queryKey: ['user'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['currentUser'] })
|
||||
setShowAvatarModal(false)
|
||||
toast.update('success', 'Avatar updated successfully')
|
||||
} catch (error) {
|
||||
@@ -85,7 +85,7 @@ function UserInfoSettings() {
|
||||
const toast = new Toast('Updating telegram channel')
|
||||
try {
|
||||
await api.user_UpdateTelegramChannel(data.telegramChannel)
|
||||
queryClient.invalidateQueries({ queryKey: ['user'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['currentUser'] })
|
||||
setShowTelegramModal(false)
|
||||
toast.update('success', 'Telegram channel updated successfully')
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user