Skip to content

Commit 437370c

Browse files
fix: improve pull-to-refresh behavior and fix code review issues
- Add error handling to prevent UI from getting stuck on refresh failure - Fix dependency array issue by using ref to avoid unnecessary re-renders - Implement platform-specific refresh behavior: - Desktop/Web: scroll up when at top to refresh - Mobile: touch-based pull-to-refresh (unchanged) - Uses getPlatformInfo() to detect platform for appropriate behavior Co-authored-by: Anthony <AnthonyRonning@users.noreply.github.com>
1 parent ec7326f commit 437370c

1 file changed

Lines changed: 51 additions & 15 deletions

File tree

‎frontend/src/components/ChatHistoryList.tsx‎

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
CheckSquare,
1010
RefreshCw
1111
}from"lucide-react";
12+
import{getPlatformInfo}from"@/utils/platform";
1213
import{
1314
DropdownMenu,
1415
DropdownMenuContent,
@@ -87,6 +88,7 @@ export function ChatHistoryList({
8788
const[pullDistance,setPullDistance]=useState(0);
8889
constpullStartY=useRef(0);
8990
constisPulling=useRef(false);
91+
constpullDistanceRef=useRef(0);
9092

9193
// Fetch initial conversations from API using the OpenSecret SDK
9294
const{ isPending, error }=useQuery({
@@ -182,19 +184,27 @@ export function ChatHistoryList({
182184
// Pull-to-refresh handler
183185
consthandleRefresh=useCallback(async()=>{
184186
setIsPullRefreshing(true);
185-
awaitpollForUpdates();
186-
// Add a small delay to show the refresh indicator
187-
setTimeout(()=>{
188-
setIsPullRefreshing(false);
189-
setPullDistance(0);
190-
},300);
187+
try{
188+
awaitpollForUpdates();
189+
}catch(error){
190+
console.error("Refresh failed:",error);
191+
}finally{
192+
// Add a small delay to show the refresh indicator
193+
setTimeout(()=>{
194+
setIsPullRefreshing(false);
195+
setPullDistance(0);
196+
},300);
197+
}
191198
},[pollForUpdates]);
192199

193200
// Pull-to-refresh event handlers
194201
useEffect(()=>{
195202
constcontainer=containerRef?.current;
196203
if(!container)return;
197204

205+
constplatformInfo=getPlatformInfo();
206+
constisDesktopPlatform=platformInfo.isDesktop||platformInfo.isWeb;
207+
198208
consthandleTouchStart=(e: TouchEvent)=>{
199209
// Only start pull if we're at the top of the scroll
200210
if(container.scrollTop===0&&!isPullRefreshing){
@@ -216,6 +226,7 @@ export function ChatHistoryList({
216226
// Apply resistance: diminishing returns as you pull further
217227
constresistanceFactor=0.4;
218228
constadjustedDistance=Math.min(distance*resistanceFactor,80);
229+
pullDistanceRef.current=adjustedDistance;
219230
setPullDistance(adjustedDistance);
220231
}
221232
};
@@ -226,7 +237,7 @@ export function ChatHistoryList({
226237
isPulling.current=false;
227238

228239
// Trigger refresh if pulled far enough (threshold: 60px)
229-
if(pullDistance>60){
240+
if(pullDistanceRef.current>60){
230241
handleRefresh();
231242
}else{
232243
// Reset if not pulled far enough
@@ -235,6 +246,9 @@ export function ChatHistoryList({
235246
};
236247

237248
consthandleMouseDown=(e: MouseEvent)=>{
249+
// Only for mobile platforms - desktop uses wheel event
250+
if(isDesktopPlatform)return;
251+
238252
// Only start pull if we're at the top of the scroll
239253
if(container.scrollTop===0&&!isPullRefreshing){
240254
pullStartY.current=e.clientY;
@@ -243,7 +257,7 @@ export function ChatHistoryList({
243257
};
244258

245259
consthandleMouseMove=(e: MouseEvent)=>{
246-
if(!isPulling.current||isPullRefreshing)return;
260+
if(!isPulling.current||isPullRefreshing||isDesktopPlatform)return;
247261

248262
constcurrentY=e.clientY;
249263
constdistance=currentY-pullStartY.current;
@@ -253,41 +267,63 @@ export function ChatHistoryList({
253267
// Apply resistance: diminishing returns as you pull further
254268
constresistanceFactor=0.4;
255269
constadjustedDistance=Math.min(distance*resistanceFactor,80);
270+
pullDistanceRef.current=adjustedDistance;
256271
setPullDistance(adjustedDistance);
257272
}
258273
};
259274

260275
consthandleMouseUp=()=>{
261-
if(!isPulling.current)return;
276+
if(!isPulling.current||isDesktopPlatform)return;
262277

263278
isPulling.current=false;
264279

265280
// Trigger refresh if pulled far enough (threshold: 60px)
266-
if(pullDistance>60){
281+
if(pullDistanceRef.current>60){
267282
handleRefresh();
268283
}else{
269284
// Reset if not pulled far enough
270285
setPullDistance(0);
271286
}
272287
};
273288

274-
// Add event listeners for touch (mobile) and mouse (desktop)
289+
// Desktop: detect scroll up when already at top (overscroll)
290+
consthandleWheel=(e: WheelEvent)=>{
291+
if(!isDesktopPlatform||isPullRefreshing)return;
292+
293+
// Check if we're at the top and trying to scroll up
294+
if(container.scrollTop===0&&e.deltaY<0){
295+
// Prevent default to avoid browser overscroll bounce
296+
e.preventDefault();
297+
// Trigger refresh on upward scroll attempt
298+
handleRefresh();
299+
}
300+
};
301+
302+
// Add event listeners based on platform
275303
container.addEventListener("touchstart",handleTouchStart,{passive: true});
276304
container.addEventListener("touchmove",handleTouchMove,{passive: false});
277305
container.addEventListener("touchend",handleTouchEnd);
278-
container.addEventListener("mousedown",handleMouseDown);
279-
window.addEventListener("mousemove",handleMouseMove);
280-
window.addEventListener("mouseup",handleMouseUp);
306+
307+
if(isDesktopPlatform){
308+
// Desktop: use wheel event for scroll-to-refresh
309+
container.addEventListener("wheel",handleWheel,{passive: false});
310+
}else{
311+
// Mobile: use mouse events for pull-to-refresh
312+
container.addEventListener("mousedown",handleMouseDown);
313+
window.addEventListener("mousemove",handleMouseMove);
314+
window.addEventListener("mouseup",handleMouseUp);
315+
}
281316

282317
return()=>{
283318
container.removeEventListener("touchstart",handleTouchStart);
284319
container.removeEventListener("touchmove",handleTouchMove);
285320
container.removeEventListener("touchend",handleTouchEnd);
321+
container.removeEventListener("wheel",handleWheel);
286322
container.removeEventListener("mousedown",handleMouseDown);
287323
window.removeEventListener("mousemove",handleMouseMove);
288324
window.removeEventListener("mouseup",handleMouseUp);
289325
};
290-
},[containerRef,isPullRefreshing,pullDistance,handleRefresh]);
326+
},[containerRef,isPullRefreshing,handleRefresh]);
291327

292328
// Set up polling every 60 seconds
293329
useEffect(()=>{

0 commit comments

Comments
 (0)