mirror of https://github.com/kortix-ai/suna.git
Merge branch 'kortix-ai:main' into feat/vnc_ui
This commit is contained in:
commit
a95cdb6382
|
@ -20,7 +20,7 @@ import { ThreadContent } from '@/components/thread/content/ThreadContent';
|
|||
import { ThreadSkeleton } from '@/components/thread/content/ThreadSkeleton';
|
||||
import { useAddUserMessageMutation } from '@/hooks/react-query/threads/use-messages';
|
||||
import { useStartAgentMutation, useStopAgentMutation } from '@/hooks/react-query/threads/use-agent-run';
|
||||
import { useSubscription } from '@/hooks/react-query/subscriptions/use-subscriptions';
|
||||
import { useSharedSubscription } from '@/contexts/SubscriptionContext';
|
||||
import { SubscriptionStatus } from '@/components/thread/chat-input/_use-model-selection';
|
||||
|
||||
import { UnifiedMessage, ApiMessageType, ToolCallInput, Project } from '../_types';
|
||||
|
@ -164,7 +164,7 @@ export default function ThreadPage({
|
|||
}
|
||||
}, [threadAgentData, agents, initializeFromAgents]);
|
||||
|
||||
const { data: subscriptionData } = useSubscription();
|
||||
const { data: subscriptionData } = useSharedSubscription();
|
||||
const subscriptionStatus: SubscriptionStatus = subscriptionData?.status === 'active'
|
||||
? 'active'
|
||||
: 'no_subscription';
|
||||
|
|
|
@ -7,7 +7,8 @@ import { isLocalMode } from '@/lib/config';
|
|||
import { createPortalSession } from '@/lib/api';
|
||||
import { useAuth } from '@/components/AuthProvider';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { useSubscription, useSubscriptionCommitment } from '@/hooks/react-query';
|
||||
import { useSharedSubscription } from '@/contexts/SubscriptionContext';
|
||||
import { useSubscriptionCommitment } from '@/hooks/react-query';
|
||||
import Link from 'next/link';
|
||||
import { OpenInNewWindowIcon } from '@radix-ui/react-icons';
|
||||
import SubscriptionManagementModal from './subscription-management-modal';
|
||||
|
@ -26,7 +27,7 @@ export default function AccountBillingStatus({ accountId, returnUrl }: Props) {
|
|||
data: subscriptionData,
|
||||
isLoading,
|
||||
error: subscriptionQueryError,
|
||||
} = useSubscription();
|
||||
} = useSharedSubscription();
|
||||
|
||||
const {
|
||||
data: commitmentInfo,
|
||||
|
|
|
@ -14,7 +14,7 @@ import { isLocalMode } from '@/lib/config';
|
|||
import { createPortalSession } from '@/lib/api';
|
||||
import { useAuth } from '@/components/AuthProvider';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { useSubscription } from '@/hooks/react-query';
|
||||
import { useSharedSubscription } from '@/contexts/SubscriptionContext';
|
||||
import Link from 'next/link';
|
||||
import { CreditCard, Settings, HelpCircle } from 'lucide-react';
|
||||
import { OpenInNewWindowIcon } from '@radix-ui/react-icons';
|
||||
|
@ -44,7 +44,7 @@ export default function SubscriptionManagementModal({
|
|||
data: subscriptionData,
|
||||
isLoading,
|
||||
error: subscriptionQueryError,
|
||||
} = useSubscription();
|
||||
} = useSharedSubscription();
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import { useMaintenanceNoticeQuery } from '@/hooks/react-query/edge-flags';
|
|||
import { useProjects, useThreads } from '@/hooks/react-query/sidebar/use-sidebar';
|
||||
import { useIsMobile } from '@/hooks/use-mobile';
|
||||
import { useAgents } from '@/hooks/react-query/agents/use-agents';
|
||||
import { SubscriptionProvider } from '@/contexts/SubscriptionContext';
|
||||
|
||||
interface DashboardLayoutContentProps {
|
||||
children: React.ReactNode;
|
||||
|
@ -126,32 +127,34 @@ export default function DashboardLayoutContent({
|
|||
|
||||
return (
|
||||
<DeleteOperationProvider>
|
||||
<SidebarProvider>
|
||||
<SidebarLeft />
|
||||
<SidebarInset>
|
||||
{mantenanceBanner}
|
||||
<div className="bg-background">{children}</div>
|
||||
</SidebarInset>
|
||||
<SubscriptionProvider>
|
||||
<SidebarProvider>
|
||||
<SidebarLeft />
|
||||
<SidebarInset>
|
||||
{mantenanceBanner}
|
||||
<div className="bg-background">{children}</div>
|
||||
</SidebarInset>
|
||||
|
||||
{/* <PricingAlert
|
||||
open={showPricingAlert}
|
||||
onOpenChange={setShowPricingAlert}
|
||||
closeable={false}
|
||||
accountId={personalAccount?.account_id}
|
||||
/> */}
|
||||
{/* <PricingAlert
|
||||
open={showPricingAlert}
|
||||
onOpenChange={setShowPricingAlert}
|
||||
closeable={false}
|
||||
accountId={personalAccount?.account_id}
|
||||
/> */}
|
||||
|
||||
<MaintenanceAlert
|
||||
open={showMaintenanceAlert}
|
||||
onOpenChange={setShowMaintenanceAlert}
|
||||
closeable={true}
|
||||
/>
|
||||
<MaintenanceAlert
|
||||
open={showMaintenanceAlert}
|
||||
onOpenChange={setShowMaintenanceAlert}
|
||||
closeable={true}
|
||||
/>
|
||||
|
||||
{/* Status overlay for deletion operations */}
|
||||
<StatusOverlay />
|
||||
|
||||
{/* Floating mobile menu button */}
|
||||
<FloatingMobileMenuButton />
|
||||
</SidebarProvider>
|
||||
{/* Status overlay for deletion operations */}
|
||||
<StatusOverlay />
|
||||
|
||||
{/* Floating mobile menu button */}
|
||||
<FloatingMobileMenuButton />
|
||||
</SidebarProvider>
|
||||
</SubscriptionProvider>
|
||||
</DeleteOperationProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { useSubscription } from '@/hooks/react-query/subscriptions/use-subscriptions';
|
||||
import { useSharedSubscription } from '@/contexts/SubscriptionContext';
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { isLocalMode } from '@/lib/config';
|
||||
import { useAvailableModels } from '@/hooks/react-query/subscriptions/use-model';
|
||||
|
@ -170,7 +170,7 @@ export const useModelSelection = () => {
|
|||
const [customModels, setCustomModels] = useState<CustomModel[]>([]);
|
||||
const [hasInitialized, setHasInitialized] = useState(false);
|
||||
|
||||
const { data: subscriptionData } = useSubscription();
|
||||
const { data: subscriptionData } = useSharedSubscription();
|
||||
const { data: modelsData, isLoading: isLoadingModels } = useAvailableModels({
|
||||
refetchOnMount: false,
|
||||
});
|
||||
|
|
|
@ -25,7 +25,7 @@ import { Skeleton } from '@/components/ui/skeleton';
|
|||
|
||||
import { IntegrationsRegistry } from '@/components/agents/integrations-registry';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { useSubscriptionWithStreaming } from '@/hooks/react-query/subscriptions/use-subscriptions';
|
||||
import { useSharedSubscription } from '@/contexts/SubscriptionContext';
|
||||
import { isLocalMode } from '@/lib/config';
|
||||
import { BillingModal } from '@/components/billing/billing-modal';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
@ -153,7 +153,7 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
|
|||
refreshCustomModels,
|
||||
} = useModelSelection();
|
||||
|
||||
const { data: subscriptionData } = useSubscriptionWithStreaming(isAgentRunning);
|
||||
const { data: subscriptionData } = useSharedSubscription();
|
||||
const deleteFileMutation = useFileDelete();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
'use client';
|
||||
|
||||
import React, { createContext, useContext, ReactNode } from 'react';
|
||||
import { useSubscription as useSubscriptionQuery } from '@/hooks/react-query/subscriptions/use-subscriptions';
|
||||
import { SubscriptionStatus } from '@/lib/api';
|
||||
|
||||
interface SubscriptionContextType {
|
||||
subscriptionData: SubscriptionStatus | null;
|
||||
isLoading: boolean;
|
||||
error: Error | null;
|
||||
refetch: () => void;
|
||||
}
|
||||
|
||||
const SubscriptionContext = createContext<SubscriptionContextType | null>(null);
|
||||
|
||||
interface SubscriptionProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function SubscriptionProvider({ children }: SubscriptionProviderProps) {
|
||||
const {
|
||||
data: subscriptionData,
|
||||
isLoading,
|
||||
error,
|
||||
refetch
|
||||
} = useSubscriptionQuery();
|
||||
|
||||
const value: SubscriptionContextType = {
|
||||
subscriptionData: subscriptionData || null,
|
||||
isLoading,
|
||||
error: error as Error | null,
|
||||
refetch,
|
||||
};
|
||||
|
||||
return (
|
||||
<SubscriptionContext.Provider value={value}>
|
||||
{children}
|
||||
</SubscriptionContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useSubscriptionContext() {
|
||||
const context = useContext(SubscriptionContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error('useSubscriptionContext must be used within a SubscriptionProvider');
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
// Convenience hook that provides the same interface as the original useSubscription
|
||||
// but uses the shared context data with fallback for components outside dashboard
|
||||
export function useSharedSubscription() {
|
||||
const context = useContext(SubscriptionContext);
|
||||
|
||||
if (!context) {
|
||||
// Fallback to the original hook if context is not available
|
||||
// This allows components outside the dashboard to still work
|
||||
return useSubscriptionQuery();
|
||||
}
|
||||
|
||||
const { subscriptionData, isLoading, error, refetch } = context;
|
||||
|
||||
return {
|
||||
data: subscriptionData,
|
||||
isLoading,
|
||||
error,
|
||||
refetch,
|
||||
};
|
||||
}
|
|
@ -23,8 +23,11 @@ export const useBillingStatus = createQueryHook(
|
|||
['billing', 'status'],
|
||||
checkBillingStatus,
|
||||
{
|
||||
staleTime: 2 * 60 * 1000,
|
||||
refetchOnWindowFocus: true,
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
gcTime: 10 * 60 * 1000, // 10 minutes cache time
|
||||
refetchOnWindowFocus: false, // Don't refetch on window focus
|
||||
refetchOnMount: false, // Don't refetch on mount if data exists
|
||||
refetchOnReconnect: true, // Only refetch when network reconnects
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -16,8 +16,11 @@ export const useSubscription = createQueryHook(
|
|||
subscriptionKeys.details(),
|
||||
getSubscription,
|
||||
{
|
||||
staleTime: 1000 * 60 * 5,
|
||||
refetchOnWindowFocus: true,
|
||||
staleTime: 1000 * 60 * 10, // 10 minutes - subscription status doesn't change frequently
|
||||
gcTime: 1000 * 60 * 15, // 15 minutes cache time
|
||||
refetchOnWindowFocus: false, // Don't refetch on every window focus
|
||||
refetchOnMount: false, // Don't refetch on every component mount if data exists
|
||||
refetchOnReconnect: true, // Only refetch when network reconnects
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -38,17 +41,20 @@ export const useSubscriptionWithStreaming = (isStreaming: boolean = false) => {
|
|||
return useQuery({
|
||||
queryKey: subscriptionKeys.details(),
|
||||
queryFn: getSubscription,
|
||||
staleTime: 1000 * 60 * 2, // 2 minutes
|
||||
refetchOnWindowFocus: true,
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes - longer stale time
|
||||
gcTime: 1000 * 60 * 15, // 15 minutes cache time
|
||||
refetchOnWindowFocus: false, // Don't refetch on window focus
|
||||
refetchOnMount: false, // Don't refetch on mount if data exists
|
||||
refetchInterval: (data) => {
|
||||
// No refresh if tab is hidden
|
||||
if (!isVisible) return false;
|
||||
|
||||
// If actively streaming: refresh every 5s (costs are changing)
|
||||
if (isStreaming) return 5 * 1000;
|
||||
// If actively streaming: refresh every 2 minutes instead of 5 seconds
|
||||
// Billing data doesn't need to be that real-time
|
||||
if (isStreaming) return 2 * 60 * 1000;
|
||||
|
||||
// If visible but not streaming: refresh every 5min
|
||||
return 5 * 60 * 1000;
|
||||
// If visible but not streaming: refresh every 10 minutes
|
||||
return 10 * 60 * 1000;
|
||||
},
|
||||
refetchIntervalInBackground: false, // Stop when tab backgrounded
|
||||
});
|
||||
|
@ -70,8 +76,11 @@ export const useSubscriptionCommitment = (subscriptionId?: string) => {
|
|||
queryKey: subscriptionKeys.commitment(subscriptionId || ''),
|
||||
queryFn: () => getSubscriptionCommitment(subscriptionId!),
|
||||
enabled: !!subscriptionId,
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
staleTime: 1000 * 60 * 15, // 15 minutes - commitment info changes very rarely
|
||||
gcTime: 1000 * 60 * 30, // 30 minutes cache time
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false, // Don't refetch on mount if data exists
|
||||
refetchOnReconnect: false, // Commitment data rarely changes
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -9,14 +9,15 @@ export const useBillingStatusQuery = (enabled = true) =>
|
|||
{
|
||||
enabled,
|
||||
retry: 1,
|
||||
staleTime: 1000 * 60 * 5,
|
||||
gcTime: 1000 * 60 * 10,
|
||||
staleTime: 1000 * 60 * 10, // 10 minutes - increased stale time
|
||||
gcTime: 1000 * 60 * 15, // 15 minutes cache time
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchInterval: (query: any) => {
|
||||
// Only refetch if billing is in a problematic state and at a slower rate
|
||||
if (query.state.data && !query.state.data.can_run) {
|
||||
return 1000 * 60;
|
||||
return 1000 * 60 * 5; // 5 minutes instead of 1 minute
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
|
|
@ -1878,6 +1878,9 @@ export const createPortalSession = async (
|
|||
|
||||
export const getSubscription = async (): Promise<SubscriptionStatus> => {
|
||||
try {
|
||||
// Log when subscription API is called for debugging
|
||||
console.log('🔍 [BILLING] Making subscription API call:', new Date().toISOString());
|
||||
|
||||
const supabase = createClient();
|
||||
const {
|
||||
data: { session },
|
||||
|
|
Loading…
Reference in New Issue