1import React, {useState, createContext, useEffect, useCallback, useRef} from "react";
2import {jsonRequest} from "@/common/utils/RequestUtil";
3
4export const SpeedtestContext = createContext({});
5
6export const SpeedtestProvider = (props) => {
7 const [speedtests, setSpeedtests] = useState([]);
8 const [loading, setLoading] = useState(false);
9 const [hasMore, setHasMore] = useState(true);
10 const [lastId, setLastId] = useState(null);
11 const loadingRef = useRef(false);
12 const lastLoadTimeRef = useRef(0);
13
14 const loadInitialTests = useCallback(async () => {
15 if (loadingRef.current) return;
16
17 loadingRef.current = true;
18 setLoading(true);
19 try {
20 const tests = await jsonRequest("/speedtests?limit=30");
21 setSpeedtests(tests);
22 if (tests.length > 0) {
23 setLastId(tests[tests.length - 1].id);
24 setHasMore(tests.length === 30);
25 } else {
26 setLastId(null);
27 setHasMore(false);
28 }
29 } catch (error) {
30 console.error("Failed to load initial tests:", error);
31 setSpeedtests([]);
32 setLastId(null);
33 setHasMore(false);
34 } finally {
35 setLoading(false);
36 loadingRef.current = false;
37 }
38 }, []);
39
40 const loadMoreTests = useCallback(async () => {
41 const now = Date.now();
42 if (loadingRef.current || !hasMore || !lastId || (now - lastLoadTimeRef.current) < 500) return;
43
44 lastLoadTimeRef.current = now;
45 loadingRef.current = true;
46 setLoading(true);
47 try {
48 const newTests = await jsonRequest(`/speedtests?limit=30&afterId=${lastId}`);
49 if (newTests.length > 0) {
50 setSpeedtests(prev => {
51 const existingIds = new Set(prev.map(test => test.id));
52 const uniqueNewTests = newTests.filter(test => !existingIds.has(test.id));
53
54 if (uniqueNewTests.length > 0) {
55 setLastId(newTests[newTests.length - 1].id);
56 return [...prev, ...uniqueNewTests];
57 }
58 return prev;
59 });
60 setHasMore(newTests.length === 30);
61 } else {
62 setHasMore(false);
63 }
64 } catch (error) {
65 console.error("Failed to load more tests:", error);
66 setHasMore(false);
67 setTimeout(() => {
68 if (lastId) setHasMore(true);
69 }, 3000);
70 } finally {
71 setLoading(false);
72 loadingRef.current = false;
73 }
74 }, [hasMore, lastId]);
75
76 const refreshTests = useCallback(async () => {
77 const latestId = speedtests.length > 0 ? speedtests[0].id : null;
78
79 try {
80 const newTests = await jsonRequest("/speedtests?limit=30");
81
82 if (newTests.length > 0) {
83 if (latestId) {
84 const newerTests = newTests.filter(test => test.id > latestId);
85 if (newerTests.length > 0) setSpeedtests(prev => [...newerTests, ...prev]);
86 } else {
87 setSpeedtests(newTests);
88 if (newTests.length > 0) setLastId(newTests[newTests.length - 1].id);
89 setHasMore(newTests.length === 30);
90 }
91 }
92 } catch (error) {
93 console.error("Failed to refresh tests:", error);
94 }
95 }, [speedtests]);
96
97 const deleteTest = useCallback((id) => {
98 setSpeedtests(prev => prev.filter(test => test.id !== id));
99 if (speedtests.length === 1) {
100 setLastId(null);
101 setHasMore(false);
102 } else if (speedtests[speedtests.length - 1].id === id) {
103 setLastId(speedtests[speedtests.length - 2].id);
104 }
105 });
106
107 const updateTests = useCallback(() => {
108 refreshTests();
109 }, [refreshTests]);
110
111 useEffect(() => {
112 loadInitialTests();
113 }, [loadInitialTests]);
114
115 useEffect(() => {
116 const interval = setInterval(() => {
117 refreshTests();
118 }, 5000);
119
120 return () => clearInterval(interval);
121 }, [refreshTests]);
122
123 return (
124 <SpeedtestContext.Provider value={{speedtests, updateTests, deleteTest, loadMoreTests, loading, hasMore}}>
125 {props.children}
126 </SpeedtestContext.Provider>
127 )
128}