@adventurelabs/scout-core 1.0.79 → 1.0.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/helpers/devices.d.ts +5 -4
- package/dist/helpers/devices.js +3 -0
- package/dist/helpers/sessions.js +35 -15
- package/dist/hooks/useScoutRefresh.js +56 -13
- package/dist/providers/ScoutRefreshProvider.d.ts +10 -8
- package/dist/store/hooks.d.ts +1 -1
- package/dist/supabase/server.d.ts +2 -963
- package/dist/types/db.d.ts +2 -0
- package/dist/types/supabase.d.ts +9 -9
- package/dist/types/supabase.js +1 -1
- package/package.json +10 -3
- package/dist/helpers/cache-usage-example.d.ts +0 -17
- package/dist/helpers/cache-usage-example.js +0 -82
- package/dist/hooks/data-source-usage-example.d.ts +0 -9
- package/dist/hooks/data-source-usage-example.js +0 -68
- package/dist/hooks/useDataSource.d.ts +0 -43
- package/dist/hooks/useDataSource.js +0 -63
package/dist/types/db.d.ts
CHANGED
package/dist/types/supabase.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type Json = string | number | boolean | null | {
|
|
|
3
3
|
} | Json[];
|
|
4
4
|
export type Database = {
|
|
5
5
|
__InternalSupabase: {
|
|
6
|
-
PostgrestVersion: "
|
|
6
|
+
PostgrestVersion: "13.0.5";
|
|
7
7
|
};
|
|
8
8
|
public: {
|
|
9
9
|
Tables: {
|
|
@@ -427,9 +427,9 @@ export type Database = {
|
|
|
427
427
|
earthranger_url: string | null;
|
|
428
428
|
id: number;
|
|
429
429
|
inserted_at: string;
|
|
430
|
-
locations: unknown;
|
|
430
|
+
locations: unknown | null;
|
|
431
431
|
software_version: string;
|
|
432
|
-
timestamp_end: string;
|
|
432
|
+
timestamp_end: string | null;
|
|
433
433
|
timestamp_start: string;
|
|
434
434
|
velocity_average: number;
|
|
435
435
|
velocity_max: number;
|
|
@@ -445,9 +445,9 @@ export type Database = {
|
|
|
445
445
|
earthranger_url?: string | null;
|
|
446
446
|
id?: number;
|
|
447
447
|
inserted_at?: string;
|
|
448
|
-
locations
|
|
448
|
+
locations?: unknown | null;
|
|
449
449
|
software_version: string;
|
|
450
|
-
timestamp_end
|
|
450
|
+
timestamp_end?: string | null;
|
|
451
451
|
timestamp_start: string;
|
|
452
452
|
velocity_average: number;
|
|
453
453
|
velocity_max: number;
|
|
@@ -463,9 +463,9 @@ export type Database = {
|
|
|
463
463
|
earthranger_url?: string | null;
|
|
464
464
|
id?: number;
|
|
465
465
|
inserted_at?: string;
|
|
466
|
-
locations?: unknown;
|
|
466
|
+
locations?: unknown | null;
|
|
467
467
|
software_version?: string;
|
|
468
|
-
timestamp_end?: string;
|
|
468
|
+
timestamp_end?: string | null;
|
|
469
469
|
timestamp_start?: string;
|
|
470
470
|
velocity_average?: number;
|
|
471
471
|
velocity_max?: number;
|
|
@@ -869,7 +869,7 @@ export type Database = {
|
|
|
869
869
|
device_type: "trail_camera" | "drone_fixed_wing" | "drone_quad" | "gps_tracker" | "sentry_tower" | "smart_buoy" | "radio_mesh_base_station" | "radio_mesh_repeater" | "unknown";
|
|
870
870
|
media_type: "image" | "video" | "audio" | "text";
|
|
871
871
|
plan_type: "mission" | "fence" | "rally" | "markov";
|
|
872
|
-
role: "admin" | "viewer" | "editor";
|
|
872
|
+
role: "admin" | "viewer" | "editor" | "operator";
|
|
873
873
|
tag_observation_type: "manual" | "auto";
|
|
874
874
|
user_status: "ONLINE" | "OFFLINE";
|
|
875
875
|
};
|
|
@@ -1068,7 +1068,7 @@ export declare const Constants: {
|
|
|
1068
1068
|
readonly device_type: readonly ["trail_camera", "drone_fixed_wing", "drone_quad", "gps_tracker", "sentry_tower", "smart_buoy", "radio_mesh_base_station", "radio_mesh_repeater", "unknown"];
|
|
1069
1069
|
readonly media_type: readonly ["image", "video", "audio", "text"];
|
|
1070
1070
|
readonly plan_type: readonly ["mission", "fence", "rally", "markov"];
|
|
1071
|
-
readonly role: readonly ["admin", "viewer", "editor"];
|
|
1071
|
+
readonly role: readonly ["admin", "viewer", "editor", "operator"];
|
|
1072
1072
|
readonly tag_observation_type: readonly ["manual", "auto"];
|
|
1073
1073
|
readonly user_status: readonly ["ONLINE", "OFFLINE"];
|
|
1074
1074
|
};
|
package/dist/types/supabase.js
CHANGED
|
@@ -15,7 +15,7 @@ export const Constants = {
|
|
|
15
15
|
],
|
|
16
16
|
media_type: ["image", "video", "audio", "text"],
|
|
17
17
|
plan_type: ["mission", "fence", "rally", "markov"],
|
|
18
|
-
role: ["admin", "viewer", "editor"],
|
|
18
|
+
role: ["admin", "viewer", "editor", "operator"],
|
|
19
19
|
tag_observation_type: ["manual", "auto"],
|
|
20
20
|
user_status: ["ONLINE", "OFFLINE"],
|
|
21
21
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adventurelabs/scout-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.82",
|
|
4
4
|
"description": "Core utilities and helpers for Adventure Labs Scout applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -38,12 +38,19 @@
|
|
|
38
38
|
"react-dom": ">=18.0.0",
|
|
39
39
|
"react-redux": ">=9.0.0",
|
|
40
40
|
"@supabase/supabase-js": "^2.57.4",
|
|
41
|
-
"@supabase/ssr": "^0.
|
|
41
|
+
"@supabase/ssr": "^0.7.0",
|
|
42
42
|
"@reduxjs/toolkit": "^2.0.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/react": "^18.0.0",
|
|
46
46
|
"@types/react-dom": "^18.0.0",
|
|
47
|
-
"typescript": "^5.0.0"
|
|
47
|
+
"typescript": "^5.0.0",
|
|
48
|
+
"next": ">=15.0.0",
|
|
49
|
+
"react": ">=18.0.0",
|
|
50
|
+
"react-dom": ">=18.0.0",
|
|
51
|
+
"react-redux": ">=9.0.0",
|
|
52
|
+
"@supabase/supabase-js": "^2.57.4",
|
|
53
|
+
"@supabase/ssr": "^0.7.0",
|
|
54
|
+
"@reduxjs/toolkit": "^2.0.0"
|
|
48
55
|
}
|
|
49
56
|
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example usage of the ScoutCache system
|
|
3
|
-
* This demonstrates how to use the cache-first loading pattern
|
|
4
|
-
*/
|
|
5
|
-
import { CacheStats, TimingStats } from "./cache";
|
|
6
|
-
export declare function ExampleBasicUsage(): {
|
|
7
|
-
handleRefresh: () => Promise<void>;
|
|
8
|
-
clearCache: () => Promise<void>;
|
|
9
|
-
stats: () => TimingStats;
|
|
10
|
-
cacheStats: () => Promise<CacheStats>;
|
|
11
|
-
};
|
|
12
|
-
export declare function ExampleAdvancedCacheManagement(): Promise<void>;
|
|
13
|
-
export declare function ExampleBackgroundPreloading(): Promise<void>;
|
|
14
|
-
export declare function ExamplePerformanceMonitoring(): {
|
|
15
|
-
getTimingStats: () => TimingStats;
|
|
16
|
-
getCacheStats: () => Promise<CacheStats>;
|
|
17
|
-
};
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example usage of the ScoutCache system
|
|
3
|
-
* This demonstrates how to use the cache-first loading pattern
|
|
4
|
-
*/
|
|
5
|
-
import { useScoutRefresh } from "../hooks/useScoutRefresh";
|
|
6
|
-
import { scoutCache } from "./cache";
|
|
7
|
-
// Example 1: Basic usage with cache-first loading
|
|
8
|
-
export function ExampleBasicUsage() {
|
|
9
|
-
const { handleRefresh, getTimingStats, clearCache, getCacheStats } = useScoutRefresh({
|
|
10
|
-
autoRefresh: true,
|
|
11
|
-
cacheFirst: true,
|
|
12
|
-
cacheTtlMs: 10 * 60 * 1000, // 10 minutes
|
|
13
|
-
onRefreshComplete: () => {
|
|
14
|
-
console.log("Refresh completed!");
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
// Get timing stats
|
|
18
|
-
const stats = getTimingStats();
|
|
19
|
-
console.log("Performance stats:", stats);
|
|
20
|
-
// Get cache stats
|
|
21
|
-
const cacheStats = getCacheStats();
|
|
22
|
-
console.log("Cache stats:", cacheStats);
|
|
23
|
-
return {
|
|
24
|
-
handleRefresh,
|
|
25
|
-
clearCache,
|
|
26
|
-
stats: getTimingStats,
|
|
27
|
-
cacheStats: getCacheStats,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
// Example 2: Advanced cache management
|
|
31
|
-
export async function ExampleAdvancedCacheManagement() {
|
|
32
|
-
// Check if cache is valid
|
|
33
|
-
const isValid = await scoutCache.isCacheValid(5 * 60 * 1000); // 5 minutes
|
|
34
|
-
console.log("Cache is valid:", isValid);
|
|
35
|
-
// Check if we should refresh
|
|
36
|
-
const shouldRefresh = await scoutCache.shouldRefresh(2 * 60 * 1000, // max age 2 minutes
|
|
37
|
-
false // not forcing refresh
|
|
38
|
-
);
|
|
39
|
-
console.log("Should refresh:", shouldRefresh);
|
|
40
|
-
// Get cache age
|
|
41
|
-
const age = await scoutCache.getCacheAge();
|
|
42
|
-
console.log("Cache age:", Math.round(age / 1000), "seconds");
|
|
43
|
-
// Invalidate cache
|
|
44
|
-
await scoutCache.invalidateHerdModules();
|
|
45
|
-
console.log("Cache invalidated");
|
|
46
|
-
// Clear all cache data
|
|
47
|
-
await scoutCache.clearHerdModules();
|
|
48
|
-
console.log("Cache cleared");
|
|
49
|
-
}
|
|
50
|
-
// Example 3: Background preloading
|
|
51
|
-
export async function ExampleBackgroundPreloading() {
|
|
52
|
-
// Simulate a function that loads herd modules
|
|
53
|
-
const loadHerdModules = async () => {
|
|
54
|
-
// This would be your actual API call
|
|
55
|
-
const response = await fetch("/api/herd-modules");
|
|
56
|
-
return response.json();
|
|
57
|
-
};
|
|
58
|
-
// Preload cache in background
|
|
59
|
-
await scoutCache.preloadCache(loadHerdModules, 15 * 60 * 1000); // 15 minutes TTL
|
|
60
|
-
console.log("Background preload completed");
|
|
61
|
-
}
|
|
62
|
-
// Example 4: Performance monitoring
|
|
63
|
-
export function ExamplePerformanceMonitoring() {
|
|
64
|
-
const { getTimingStats, getCacheStats } = useScoutRefresh({
|
|
65
|
-
cacheFirst: true,
|
|
66
|
-
onRefreshComplete: async () => {
|
|
67
|
-
const stats = getTimingStats();
|
|
68
|
-
const cacheStats = await getCacheStats();
|
|
69
|
-
console.log("=== Performance Report ===");
|
|
70
|
-
console.log("Total duration:", stats.totalDuration, "ms");
|
|
71
|
-
console.log("Cache load:", stats.cacheLoad, "ms");
|
|
72
|
-
console.log("API calls:", stats.herdModulesApi + stats.userApi, "ms");
|
|
73
|
-
console.log("Cache save:", stats.cacheSave, "ms");
|
|
74
|
-
console.log("Data processing:", stats.dataProcessing, "ms");
|
|
75
|
-
console.log("LocalStorage:", stats.localStorage, "ms");
|
|
76
|
-
console.log("Cache hit rate:", (cacheStats.hitRate * 100).toFixed(1) + "%");
|
|
77
|
-
console.log("Cache size:", cacheStats.size, "herd modules");
|
|
78
|
-
console.log("Cache age:", Math.round((Date.now() - cacheStats.lastUpdated) / 1000), "seconds");
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
return { getTimingStats, getCacheStats };
|
|
82
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example components demonstrating how to use data source tracking
|
|
3
|
-
*/
|
|
4
|
-
export declare function DataSourceIndicator(): import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
export declare function ConditionalContent(): import("react/jsx-runtime").JSX.Element;
|
|
6
|
-
export declare function PerformanceMetrics(): import("react/jsx-runtime").JSX.Element;
|
|
7
|
-
export declare function SmartRefreshButton(): import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
export declare function HeaderWithDataSource(): import("react/jsx-runtime").JSX.Element;
|
|
9
|
-
export declare function DataSourceDebugger(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useDataSource, useDataSourceDescription } from './useDataSource';
|
|
3
|
-
import { useScoutRefresh } from './useScoutRefresh';
|
|
4
|
-
// Example 1: Basic data source display
|
|
5
|
-
export function DataSourceIndicator() {
|
|
6
|
-
const { dataSource, isFromCache, cacheAge, isStale } = useDataSource();
|
|
7
|
-
const description = useDataSourceDescription();
|
|
8
|
-
return (_jsxs("div", { className: "data-source-indicator", children: [_jsx("div", { className: `source-badge ${dataSource.toLowerCase()}`, children: dataSource }), _jsxs("div", { className: "source-details", children: [_jsx("p", { children: description }), isFromCache && (_jsxs("div", { className: "cache-info", children: [_jsxs("span", { children: ["Cache age: ", Math.round(cacheAge / 1000), "s"] }), _jsx("span", { className: isStale ? 'stale' : 'fresh', children: isStale ? 'Stale' : 'Fresh' })] }))] })] }));
|
|
9
|
-
}
|
|
10
|
-
// Example 2: Conditional rendering based on data source
|
|
11
|
-
export function ConditionalContent() {
|
|
12
|
-
const { isFromCache, isFromDatabase, isStale } = useDataSource();
|
|
13
|
-
return (_jsxs("div", { children: [isFromCache && (_jsx("div", { className: "cache-notice", children: isStale ? (_jsx("p", { children: "\u26A0\uFE0F Showing cached data (may be outdated)" })) : (_jsx("p", { children: "\u2705 Showing fresh cached data" })) })), isFromDatabase && (_jsx("div", { className: "database-notice", children: _jsx("p", { children: "\uD83D\uDD04 Showing live data from database" }) }))] }));
|
|
14
|
-
}
|
|
15
|
-
// Example 3: Performance metrics with data source
|
|
16
|
-
export function PerformanceMetrics() {
|
|
17
|
-
const { dataSource, cacheAge } = useDataSource();
|
|
18
|
-
const { getTimingStats, getCacheStats } = useScoutRefresh();
|
|
19
|
-
const handleShowMetrics = () => {
|
|
20
|
-
const timing = getTimingStats();
|
|
21
|
-
console.log('Performance Metrics:', {
|
|
22
|
-
dataSource,
|
|
23
|
-
cacheAge: cacheAge ? Math.round(cacheAge / 1000) : 'N/A',
|
|
24
|
-
totalDuration: timing.totalDuration,
|
|
25
|
-
cacheLoad: timing.cacheLoad,
|
|
26
|
-
apiCalls: timing.herdModulesApi + timing.userApi,
|
|
27
|
-
});
|
|
28
|
-
};
|
|
29
|
-
return (_jsxs("div", { className: "performance-metrics", children: [_jsx("h3", { children: "Performance Metrics" }), _jsxs("p", { children: ["Data Source: ", dataSource] }), cacheAge && _jsxs("p", { children: ["Cache Age: ", Math.round(cacheAge / 1000), "s"] }), _jsx("button", { onClick: handleShowMetrics, children: "Show Detailed Metrics" })] }));
|
|
30
|
-
}
|
|
31
|
-
// Example 4: Data source aware refresh button
|
|
32
|
-
export function SmartRefreshButton() {
|
|
33
|
-
const { isFromCache, isStale, cacheAge } = useDataSource();
|
|
34
|
-
const { handleRefresh } = useScoutRefresh();
|
|
35
|
-
const getButtonText = () => {
|
|
36
|
-
if (isFromCache) {
|
|
37
|
-
if (isStale) {
|
|
38
|
-
return 'Refresh Stale Data';
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
const ageSeconds = Math.round(cacheAge / 1000);
|
|
42
|
-
return `Refresh (${ageSeconds}s old)`;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return 'Refresh Data';
|
|
46
|
-
};
|
|
47
|
-
const getButtonStyle = () => {
|
|
48
|
-
if (isFromCache && isStale) {
|
|
49
|
-
return 'refresh-button stale';
|
|
50
|
-
}
|
|
51
|
-
else if (isFromCache) {
|
|
52
|
-
return 'refresh-button cached';
|
|
53
|
-
}
|
|
54
|
-
return 'refresh-button fresh';
|
|
55
|
-
};
|
|
56
|
-
return (_jsx("button", { className: getButtonStyle(), onClick: handleRefresh, children: getButtonText() }));
|
|
57
|
-
}
|
|
58
|
-
// Example 5: Data source status in header
|
|
59
|
-
export function HeaderWithDataSource() {
|
|
60
|
-
const description = useDataSourceDescription();
|
|
61
|
-
const { handleRefresh } = useScoutRefresh();
|
|
62
|
-
return (_jsxs("header", { className: "app-header", children: [_jsx("h1", { children: "Scout Dashboard" }), _jsxs("div", { className: "header-controls", children: [_jsx("span", { className: "data-source-status", children: description }), _jsx("button", { onClick: handleRefresh, children: "Refresh" })] })] }));
|
|
63
|
-
}
|
|
64
|
-
// Example 6: Data source debugging component
|
|
65
|
-
export function DataSourceDebugger() {
|
|
66
|
-
const { dataSource, dataSourceInfo, isFromCache, isFromDatabase, isUnknown } = useDataSource();
|
|
67
|
-
return (_jsxs("div", { className: "data-source-debugger", children: [_jsx("h3", { children: "Data Source Debug Info" }), _jsxs("div", { className: "debug-info", children: [_jsxs("p", { children: [_jsx("strong", { children: "Source:" }), " ", dataSource] }), _jsxs("p", { children: [_jsx("strong", { children: "From Cache:" }), " ", isFromCache ? 'Yes' : 'No'] }), _jsxs("p", { children: [_jsx("strong", { children: "From Database:" }), " ", isFromDatabase ? 'Yes' : 'No'] }), _jsxs("p", { children: [_jsx("strong", { children: "Unknown:" }), " ", isUnknown ? 'Yes' : 'No'] }), dataSourceInfo && (_jsxs("div", { className: "detailed-info", children: [_jsxs("p", { children: [_jsx("strong", { children: "Timestamp:" }), " ", new Date(dataSourceInfo.timestamp).toISOString()] }), dataSourceInfo.cacheAge && (_jsxs("p", { children: [_jsx("strong", { children: "Cache Age:" }), " ", Math.round(dataSourceInfo.cacheAge / 1000), "s"] })), typeof dataSourceInfo.isStale === 'boolean' && (_jsxs("p", { children: [_jsx("strong", { children: "Is Stale:" }), " ", dataSourceInfo.isStale ? 'Yes' : 'No'] }))] }))] })] }));
|
|
68
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { EnumDataSource, IDataSourceInfo } from "../types/data_source";
|
|
2
|
-
/**
|
|
3
|
-
* Hook to access data source information from the Redux store
|
|
4
|
-
*
|
|
5
|
-
* @returns Object containing:
|
|
6
|
-
* - dataSource: The current data source (CACHE, DATABASE, or UNKNOWN)
|
|
7
|
-
* - dataSourceInfo: Detailed information about the data source
|
|
8
|
-
* - isFromCache: Boolean indicating if data is from cache
|
|
9
|
-
* - isFromDatabase: Boolean indicating if data is from database
|
|
10
|
-
* - cacheAge: Age of cached data in milliseconds (if from cache)
|
|
11
|
-
* - isStale: Whether cached data is stale (if from cache)
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```tsx
|
|
15
|
-
* function MyComponent() {
|
|
16
|
-
* const { dataSource, isFromCache, cacheAge, isStale } = useDataSource();
|
|
17
|
-
*
|
|
18
|
-
* return (
|
|
19
|
-
* <div>
|
|
20
|
-
* <p>Data source: {dataSource}</p>
|
|
21
|
-
* {isFromCache && (
|
|
22
|
-
* <p>Cache age: {Math.round(cacheAge! / 1000)}s, Stale: {isStale ? 'Yes' : 'No'}</p>
|
|
23
|
-
* )}
|
|
24
|
-
* </div>
|
|
25
|
-
* );
|
|
26
|
-
* }
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export declare function useDataSource(): {
|
|
30
|
-
dataSource: EnumDataSource;
|
|
31
|
-
dataSourceInfo: IDataSourceInfo | null;
|
|
32
|
-
isFromCache: boolean;
|
|
33
|
-
isFromDatabase: boolean;
|
|
34
|
-
isUnknown: boolean;
|
|
35
|
-
cacheAge: number | undefined;
|
|
36
|
-
isStale: boolean | undefined;
|
|
37
|
-
};
|
|
38
|
-
/**
|
|
39
|
-
* Hook to get a human-readable description of the data source
|
|
40
|
-
*
|
|
41
|
-
* @returns String description of the current data source
|
|
42
|
-
*/
|
|
43
|
-
export declare function useDataSourceDescription(): string;
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { useSelector } from "react-redux";
|
|
2
|
-
import { EnumDataSource } from "../types/data_source";
|
|
3
|
-
/**
|
|
4
|
-
* Hook to access data source information from the Redux store
|
|
5
|
-
*
|
|
6
|
-
* @returns Object containing:
|
|
7
|
-
* - dataSource: The current data source (CACHE, DATABASE, or UNKNOWN)
|
|
8
|
-
* - dataSourceInfo: Detailed information about the data source
|
|
9
|
-
* - isFromCache: Boolean indicating if data is from cache
|
|
10
|
-
* - isFromDatabase: Boolean indicating if data is from database
|
|
11
|
-
* - cacheAge: Age of cached data in milliseconds (if from cache)
|
|
12
|
-
* - isStale: Whether cached data is stale (if from cache)
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```tsx
|
|
16
|
-
* function MyComponent() {
|
|
17
|
-
* const { dataSource, isFromCache, cacheAge, isStale } = useDataSource();
|
|
18
|
-
*
|
|
19
|
-
* return (
|
|
20
|
-
* <div>
|
|
21
|
-
* <p>Data source: {dataSource}</p>
|
|
22
|
-
* {isFromCache && (
|
|
23
|
-
* <p>Cache age: {Math.round(cacheAge! / 1000)}s, Stale: {isStale ? 'Yes' : 'No'}</p>
|
|
24
|
-
* )}
|
|
25
|
-
* </div>
|
|
26
|
-
* );
|
|
27
|
-
* }
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export function useDataSource() {
|
|
31
|
-
const dataSource = useSelector((state) => state.scout.data_source);
|
|
32
|
-
const dataSourceInfo = useSelector((state) => state.scout.data_source_info);
|
|
33
|
-
const isFromCache = dataSource === EnumDataSource.CACHE;
|
|
34
|
-
const isFromDatabase = dataSource === EnumDataSource.DATABASE;
|
|
35
|
-
const isUnknown = dataSource === EnumDataSource.UNKNOWN;
|
|
36
|
-
return {
|
|
37
|
-
dataSource,
|
|
38
|
-
dataSourceInfo,
|
|
39
|
-
isFromCache,
|
|
40
|
-
isFromDatabase,
|
|
41
|
-
isUnknown,
|
|
42
|
-
cacheAge: dataSourceInfo?.cacheAge,
|
|
43
|
-
isStale: dataSourceInfo?.isStale,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Hook to get a human-readable description of the data source
|
|
48
|
-
*
|
|
49
|
-
* @returns String description of the current data source
|
|
50
|
-
*/
|
|
51
|
-
export function useDataSourceDescription() {
|
|
52
|
-
const { dataSource, cacheAge, isStale } = useDataSource();
|
|
53
|
-
switch (dataSource) {
|
|
54
|
-
case EnumDataSource.CACHE:
|
|
55
|
-
const ageSeconds = cacheAge ? Math.round(cacheAge / 1000) : 0;
|
|
56
|
-
return `Loaded from cache (${ageSeconds}s old${isStale ? ', stale' : ', fresh'})`;
|
|
57
|
-
case EnumDataSource.DATABASE:
|
|
58
|
-
return 'Loaded from database (fresh data)';
|
|
59
|
-
case EnumDataSource.UNKNOWN:
|
|
60
|
-
default:
|
|
61
|
-
return 'Data source unknown';
|
|
62
|
-
}
|
|
63
|
-
}
|