From 05e872f46853ad1abfa5521376bd4e02e9528c93 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 14 Feb 2026 19:39:28 +0000 Subject: [PATCH] [jules] enhance: Add skeleton loading to mobile Home screen Co-authored-by: Devasy23 <110348311+Devasy23@users.noreply.github.com> --- .Jules/changelog.md | 9 ++++ .Jules/knowledge.md | 25 +++++++++++ .Jules/todo.md | 7 +-- .../components/skeletons/GroupCardSkeleton.js | 25 +++++++++++ mobile/components/ui/Skeleton.js | 44 +++++++++++++++++++ mobile/screens/HomeScreen.js | 17 +++---- 6 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 mobile/components/skeletons/GroupCardSkeleton.js create mode 100644 mobile/components/ui/Skeleton.js diff --git a/.Jules/changelog.md b/.Jules/changelog.md index 11fc864..3cbc081 100644 --- a/.Jules/changelog.md +++ b/.Jules/changelog.md @@ -7,6 +7,15 @@ ## [Unreleased] ### Added +- **Mobile Skeleton Loading:** Implemented skeleton loading states for the Home screen group list. + - **Features:** + - Replaced `ActivityIndicator` with a list of `GroupCardSkeleton` components. + - Mimics the exact layout of the group cards (Avatar + Title + Subtitle). + - Uses `Animated` for a smooth pulsing opacity effect. + - Automatically adapts to light/dark mode using `react-native-paper` theme. + - Accessible container with `accessibilityLabel="Loading groups"`. + - **Technical:** Created `mobile/components/ui/Skeleton.js` and `mobile/components/skeletons/GroupCardSkeleton.js`. Integrated into `mobile/screens/HomeScreen.js`. + - **Password Strength Meter:** Added a visual password strength indicator to the signup form. - **Features:** - Real-time strength calculation (Length, Uppercase, Lowercase, Number, Symbol). diff --git a/.Jules/knowledge.md b/.Jules/knowledge.md index 43a9ab0..902307e 100644 --- a/.Jules/knowledge.md +++ b/.Jules/knowledge.md @@ -317,6 +317,31 @@ When building mobile screens with React Native Paper: 3. **Hints:** Use `accessibilityHint` for non-obvious actions (e.g., "Double tap to delete"). 4. **State:** For custom checkboxes or toggles, use `accessibilityState={{ checked: boolean }}`. +### Skeleton Loading Pattern + +**Date:** 2026-02-12 +**Context:** Implementing HomeScreen skeletons + +To create professional loading states in React Native: + +1. **Base Component:** Create a `Skeleton` component using `Animated` for pulsing opacity. + * Use `useTheme` to set background color (e.g., `theme.colors.surfaceVariant`). + * Use `useNativeDriver: true` for performance. +2. **Layout Mirroring:** Create a specific skeleton component (e.g., `GroupCardSkeleton`) that mirrors the actual component's layout using `Card`, `View`, and spacing. +3. **Integration:** + * Replace `ActivityIndicator` with a list of skeletons. + * Wrap in a `View` with `accessible={true}` and `accessibilityLabel="Loading..."`. + +```javascript +// Animation Logic +Animated.loop( + Animated.sequence([ + Animated.timing(opacity, { toValue: 0.7, duration: 800, useNativeDriver: true }), + Animated.timing(opacity, { toValue: 0.3, duration: 800, useNativeDriver: true }), + ]) +).start(); +``` + --- ## API Response Patterns diff --git a/.Jules/todo.md b/.Jules/todo.md index ebb0c7a..6e71fd5 100644 --- a/.Jules/todo.md +++ b/.Jules/todo.md @@ -57,11 +57,12 @@ - Impact: Native feel, users can easily refresh data - Size: ~150 lines -- [ ] **[ux]** Complete skeleton loading for HomeScreen groups - - File: `mobile/screens/HomeScreen.js` +- [x] **[ux]** Complete skeleton loading for HomeScreen groups + - Completed: 2026-02-12 + - Files: `mobile/screens/HomeScreen.js`, `mobile/components/ui/Skeleton.js`, `mobile/components/skeletons/GroupCardSkeleton.js` - Context: Replace ActivityIndicator with skeleton group cards - Impact: Better loading experience, less jarring - - Size: ~40 lines + - Size: ~80 lines - Added: 2026-01-01 - [x] **[a11y]** Complete accessibility labels for all screens diff --git a/mobile/components/skeletons/GroupCardSkeleton.js b/mobile/components/skeletons/GroupCardSkeleton.js new file mode 100644 index 0000000..ea1b0f9 --- /dev/null +++ b/mobile/components/skeletons/GroupCardSkeleton.js @@ -0,0 +1,25 @@ +import React from 'react'; +import { Card } from 'react-native-paper'; +import Skeleton from '../ui/Skeleton'; + +const GroupCardSkeleton = () => { + return ( + + } + left={(props) => ( + + )} + /> + + + + + ); +}; + +export default GroupCardSkeleton; diff --git a/mobile/components/ui/Skeleton.js b/mobile/components/ui/Skeleton.js new file mode 100644 index 0000000..01f6ae2 --- /dev/null +++ b/mobile/components/ui/Skeleton.js @@ -0,0 +1,44 @@ +import React, { useEffect, useRef } from 'react'; +import { Animated } from 'react-native'; +import { useTheme } from 'react-native-paper'; + +const Skeleton = ({ width, height, borderRadius = 4, style }) => { + const theme = useTheme(); + const opacity = useRef(new Animated.Value(0.3)).current; + + useEffect(() => { + const pulse = Animated.loop( + Animated.sequence([ + Animated.timing(opacity, { + toValue: 0.7, + duration: 800, + useNativeDriver: true, + }), + Animated.timing(opacity, { + toValue: 0.3, + duration: 800, + useNativeDriver: true, + }), + ]) + ); + pulse.start(); + return () => pulse.stop(); + }, [opacity]); + + return ( + + ); +}; + +export default Skeleton; diff --git a/mobile/screens/HomeScreen.js b/mobile/screens/HomeScreen.js index d2f3c38..a37a4e6 100644 --- a/mobile/screens/HomeScreen.js +++ b/mobile/screens/HomeScreen.js @@ -1,7 +1,6 @@ import { useContext, useEffect, useState } from "react"; import { Alert, FlatList, RefreshControl, StyleSheet, View } from "react-native"; import { - ActivityIndicator, Appbar, Avatar, Modal, @@ -12,6 +11,7 @@ import { } from "react-native-paper"; import HapticButton from '../components/ui/HapticButton'; import HapticCard from '../components/ui/HapticCard'; +import GroupCardSkeleton from '../components/skeletons/GroupCardSkeleton'; import { HapticAppbarAction } from '../components/ui/HapticAppbar'; import * as Haptics from "expo-haptics"; import { createGroup, getGroups, getOptimizedSettlements } from "../api/groups"; @@ -257,8 +257,14 @@ const HomeScreen = ({ navigation }) => { {isLoading ? ( - - + + {[1, 2, 3, 4, 5].map((key) => ( + + ))} ) : (