File size: 2,600 Bytes
f0743f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import { AgentListResponse } from 'librechat-data-provider';
import React, { useState, useEffect } from 'react';

interface SmartLoaderProps {
  /** Whether the content is currently loading */
  isLoading: boolean;
  /** Whether there is existing data to show */
  hasData: boolean;
  /** Delay before showing loading state (in ms) - prevents flashing for quick loads */
  delay?: number;
  /** Loading skeleton/spinner component */
  loadingComponent: React.ReactNode;
  /** Content to show when loaded */
  children: React.ReactNode;
  /** Additional CSS classes */
  className?: string;
}

/**
 * SmartLoader - Intelligent loading wrapper that prevents flashing
 *
 * Only shows loading states when:
 * 1. Actually loading AND no existing data
 * 2. Loading has lasted longer than the delay threshold
 *
 * This prevents brief loading flashes for cached/fast responses
 */
export const SmartLoader: React.FC<SmartLoaderProps> = ({
  isLoading,
  hasData,
  delay = 150,
  loadingComponent,
  children,
  className = '',
}) => {
  const [shouldShowLoading, setShouldShowLoading] = useState(false);

  useEffect(() => {
    let timeoutId: NodeJS.Timeout;

    if (isLoading && !hasData) {
      // Only show loading after delay to prevent flashing
      timeoutId = setTimeout(() => {
        setShouldShowLoading(true);
      }, delay);
    } else {
      // Immediately hide loading when done
      setShouldShowLoading(false);
    }

    return () => {
      if (timeoutId) {
        clearTimeout(timeoutId);
      }
    };
  }, [isLoading, hasData, delay]);

  // Show loading state only if we've determined it should be shown
  if (shouldShowLoading) {
    return <div className={className}>{loadingComponent}</div>;
  }

  // Show content (including when loading but we have existing data)
  return <div className={className}>{children}</div>;
};

/**
 * Hook to determine if we have meaningful data to show
 * Helps prevent loading states when we already have cached content
 */
export const useHasData = (data: AgentListResponse | undefined): boolean => {
  if (!data) return false;

  // Type guard for object data
  if (typeof data === 'object' && data !== null) {
    // Check for agent list data
    if ('agents' in data) {
      const agents = (data as any).agents;
      return Array.isArray(agents) && agents.length > 0;
    }

    // Check for single agent data
    if ('id' in data || 'name' in data) {
      return true;
    }
  }

  // Check for categories data (array)
  if (Array.isArray(data)) {
    return data.length > 0;
  }

  return false;
};

export default SmartLoader;