From 9f0791a62579881563e2c55f7f4aad99fbb78477 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 19:55:59 +0000 Subject: [PATCH] [jules] ux: Complete skeleton loading for HomeScreen groups - Created reusable `Skeleton` component with pulsing animation in `mobile/components/ui/Skeleton.js`. - Created `GroupListSkeleton` component in `mobile/components/skeletons/GroupListSkeleton.js` to mimic the home screen layout. - Replaced `ActivityIndicator` in `mobile/screens/HomeScreen.js` with the new skeleton loader. - Updated tracking files (`todo.md`, `changelog.md`, `knowledge.md`). Co-authored-by: Devasy23 <110348311+Devasy23@users.noreply.github.com> --- .Jules/changelog.md | 7 +++ .Jules/knowledge.md | 11 +++++ .Jules/todo.md | 12 ++--- .../components/skeletons/GroupListSkeleton.js | 38 +++++++++++++++ mobile/components/ui/Skeleton.js | 47 +++++++++++++++++++ mobile/screens/HomeScreen.js | 11 +---- 6 files changed, 111 insertions(+), 15 deletions(-) create mode 100644 mobile/components/skeletons/GroupListSkeleton.js create mode 100644 mobile/components/ui/Skeleton.js diff --git a/.Jules/changelog.md b/.Jules/changelog.md index 11fc864..f018c42 100644 --- a/.Jules/changelog.md +++ b/.Jules/changelog.md @@ -7,6 +7,13 @@ ## [Unreleased] ### Added +- **Mobile Skeleton Loading:** Implemented skeleton loading state for HomeScreen. + - **Features:** + - Created reusable `Skeleton` component with pulsing animation and theme support. + - Created `GroupListSkeleton` to mimic the layout of group cards. + - Replaced `ActivityIndicator` in `HomeScreen` with the skeleton loader. + - **Technical:** Created `mobile/components/ui/Skeleton.js` and `mobile/components/skeletons/GroupListSkeleton.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..09267d9 100644 --- a/.Jules/knowledge.md +++ b/.Jules/knowledge.md @@ -306,6 +306,17 @@ Commonly used components: Most screens use `` - consider wrapping in `SafeAreaView` for notched devices. +### Skeleton Loading Pattern + +**Date:** 2026-02-09 +**Context:** Improving mobile loading states + +To create a skeleton loader that matches the content layout: + +1. **Base Component:** Create a `Skeleton` component using `Animated` for pulsing opacity and `useTheme` for colors (`surfaceVariant`). +2. **Layout Component:** Create a composite skeleton (e.g., `GroupListSkeleton`) that uses `Card` (or other containers) to mimic the exact structure of the loaded content (including padding/margins). +3. **Integration:** Replace `ActivityIndicator` with the skeleton component in the `isLoading` state. + ### Accessibility Patterns **Date:** 2026-01-29 diff --git a/.Jules/todo.md b/.Jules/todo.md index ebb0c7a..789ff72 100644 --- a/.Jules/todo.md +++ b/.Jules/todo.md @@ -57,12 +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` - - Context: Replace ActivityIndicator with skeleton group cards - - Impact: Better loading experience, less jarring - - Size: ~40 lines - - Added: 2026-01-01 +- [x] **[ux]** Complete skeleton loading for HomeScreen groups + - Completed: 2026-02-09 + - Files modified: `mobile/screens/HomeScreen.js`, `mobile/components/ui/Skeleton.js`, `mobile/components/skeletons/GroupListSkeleton.js` + - Context: Replaced ActivityIndicator with skeleton group cards + - Impact: Better loading experience, less jarring, matches list layout + - Size: ~80 lines - [x] **[a11y]** Complete accessibility labels for all screens - Completed: 2026-01-29 diff --git a/mobile/components/skeletons/GroupListSkeleton.js b/mobile/components/skeletons/GroupListSkeleton.js new file mode 100644 index 0000000..ca1ee83 --- /dev/null +++ b/mobile/components/skeletons/GroupListSkeleton.js @@ -0,0 +1,38 @@ +import React from 'react'; +import { View, StyleSheet } from 'react-native'; +import { Card } from 'react-native-paper'; +import Skeleton from '../ui/Skeleton'; + +const GroupListSkeleton = () => { + // Render 5 items to fill the screen + const items = Array.from({ length: 5 }, (_, i) => i); + + return ( + + {items.map((key) => ( + + } + left={(props) => ( + + )} + /> + + + + + ))} + + ); +}; + +const styles = StyleSheet.create({ + container: { + padding: 16, + }, + card: { + marginBottom: 16, + }, +}); + +export default GroupListSkeleton; diff --git a/mobile/components/ui/Skeleton.js b/mobile/components/ui/Skeleton.js new file mode 100644 index 0000000..afd9b5f --- /dev/null +++ b/mobile/components/ui/Skeleton.js @@ -0,0 +1,47 @@ +import React, { useEffect, useRef } from 'react'; +import { Animated, View, StyleSheet } 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 animation = Animated.loop( + Animated.sequence([ + Animated.timing(opacity, { + toValue: 1, + duration: 1000, + useNativeDriver: true, + }), + Animated.timing(opacity, { + toValue: 0.3, + duration: 1000, + useNativeDriver: true, + }), + ]) + ); + animation.start(); + + return () => animation.stop(); + }, [opacity]); + + return ( + + ); +}; + +export default Skeleton; diff --git a/mobile/screens/HomeScreen.js b/mobile/screens/HomeScreen.js index d2f3c38..9be0c17 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, @@ -15,6 +14,7 @@ import HapticCard from '../components/ui/HapticCard'; import { HapticAppbarAction } from '../components/ui/HapticAppbar'; import * as Haptics from "expo-haptics"; import { createGroup, getGroups, getOptimizedSettlements } from "../api/groups"; +import GroupListSkeleton from '../components/skeletons/GroupListSkeleton'; import { AuthContext } from "../context/AuthContext"; import { formatCurrency, getCurrencySymbol } from "../utils/currency"; @@ -257,9 +257,7 @@ const HomeScreen = ({ navigation }) => { {isLoading ? ( - - - + ) : (