From e771af4a7af8ca48e7d57d98ef04f98bebc3e40f Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Wed, 16 Apr 2025 08:13:43 +0200 Subject: [PATCH] Add pull to refresh to credential list (#771) --- mobile-app/app/(tabs)/(credentials)/index.tsx | 63 +++++++++++++++---- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/mobile-app/app/(tabs)/(credentials)/index.tsx b/mobile-app/app/(tabs)/(credentials)/index.tsx index ab2de8d93..11f16c7fa 100644 --- a/mobile-app/app/(tabs)/(credentials)/index.tsx +++ b/mobile-app/app/(tabs)/(credentials)/index.tsx @@ -1,5 +1,5 @@ -import { StyleSheet, View, Text, FlatList, ActivityIndicator, useColorScheme, TouchableOpacity, TextInput, Keyboard, Image } from 'react-native'; -import { useState, useEffect } from 'react'; +import { StyleSheet, View, Text, FlatList, ActivityIndicator, useColorScheme, TouchableOpacity, TextInput, Keyboard, Image, RefreshControl } from 'react-native'; +import { useState, useEffect, useCallback } from 'react'; import { router, Stack } from 'expo-router'; import { ThemedText } from '@/components/ThemedText'; @@ -14,6 +14,7 @@ export default function CredentialsScreen() { const colorScheme = useColorScheme(); const isDarkMode = colorScheme === 'dark'; const [searchQuery, setSearchQuery] = useState(''); + const [refreshing, setRefreshing] = useState(false); const dynamicStyles = { credentialItem: { @@ -49,22 +50,50 @@ export default function CredentialsScreen() { router.push(`/(tabs)/(credentials)/${credentialId}`); }; + const loadCredentials = async () => { + setIsLoadingCredentials(true); + try { + const credentials = await dbContext.sqliteClient!.getAllCredentials(); + setCredentialsList(credentials); + } catch (err) { + console.error('Error loading credentials:', err); + } + setIsLoadingCredentials(false); + }; + + const onRefresh = useCallback(async () => { + setRefreshing(true); + + try { + // Record start time + const startTime = Date.now(); + + // Load data + const credentials = await dbContext.sqliteClient!.getAllCredentials(); + + // Calculate remaining time needed to reach 350ms minimum + const elapsedTime = Date.now() - startTime; + const remainingDelay = Math.max(0, 350 - elapsedTime); + + // Only delay if needed to reach minimum 350ms + if (remainingDelay > 0) { + await new Promise(resolve => setTimeout(resolve, remainingDelay)); + } + + // Update the data + setCredentialsList(credentials); + setRefreshing(false); + } catch (err) { + console.error('Error refreshing credentials:', err); + setRefreshing(false); + } + }, []); + useEffect(() => { if (!isAuthenticated || !isDatabaseAvailable) { return; } - const loadCredentials = async () => { - setIsLoadingCredentials(true); - try { - const credentialsList = await dbContext.sqliteClient!.getAllCredentials(); - setCredentialsList(credentialsList); - } catch (err) { - console.error('Error loading credentials:', err); - } - setIsLoadingCredentials(false); - }; - loadCredentials(); }, [isAuthenticated, isDatabaseAvailable]); @@ -104,6 +133,14 @@ export default function CredentialsScreen() { data={filteredCredentials} keyExtractor={(item) => item.Id} keyboardShouldPersistTaps='handled' + refreshControl={ + + } renderItem={({ item }) => ( handleCredentialPress(item.Id)}