From ae282a352fc9489f4be493bba6ceae4852594401 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 19:49:59 +0000 Subject: [PATCH 1/2] [jules] ux: Complete skeleton loading for HomeScreen groups Co-authored-by: Devasy23 <110348311+Devasy23@users.noreply.github.com> --- .Jules/changelog.md | 8 ++++ .Jules/todo.md | 8 ++-- .../components/skeletons/GroupCardSkeleton.js | 29 ++++++++++++ mobile/components/ui/Skeleton.js | 44 +++++++++++++++++++ mobile/screens/HomeScreen.js | 13 +++--- 5 files changed, 90 insertions(+), 12 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..a12ed1f 100644 --- a/.Jules/changelog.md +++ b/.Jules/changelog.md @@ -7,6 +7,14 @@ ## [Unreleased] ### Added +- **Mobile Skeleton Loading:** Implemented a skeleton loading state for the Home screen group list. + - **Features:** + - Created reusable `Skeleton` component with pulsing animation. + - Created `GroupCardSkeleton` mimicking the exact layout of group cards. + - Replaced `ActivityIndicator` with a list of skeletons for a smoother loading experience. + - Maintained theme consistency using `surfaceVariant` color. + - **Technical:** Created `mobile/components/ui/Skeleton.js`, `mobile/components/skeletons/GroupCardSkeleton.js`. Updated `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/todo.md b/.Jules/todo.md index ebb0c7a..565a444 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` +- [x] **[ux]** Complete skeleton loading for HomeScreen groups + - Completed: 2026-02-12 + - Files: `mobile/components/ui/Skeleton.js`, `mobile/components/skeletons/GroupCardSkeleton.js`, `mobile/screens/HomeScreen.js` - Context: Replace ActivityIndicator with skeleton group cards - Impact: Better loading experience, less jarring - - Size: ~40 lines - - Added: 2026-01-01 + - Size: ~80 lines - [x] **[a11y]** Complete accessibility labels for all screens - Completed: 2026-01-29 diff --git a/mobile/components/skeletons/GroupCardSkeleton.js b/mobile/components/skeletons/GroupCardSkeleton.js new file mode 100644 index 0000000..2ce2dc2 --- /dev/null +++ b/mobile/components/skeletons/GroupCardSkeleton.js @@ -0,0 +1,29 @@ +import React from 'react'; +import { StyleSheet } from 'react-native'; +import { Card } from 'react-native-paper'; +import Skeleton from '../ui/Skeleton'; + +const GroupCardSkeleton = () => { + return ( + + } + left={() => } + /> + + + + + ); +}; + +const styles = StyleSheet.create({ + card: { + marginBottom: 16, + }, + subtitle: { + marginTop: 4, + }, +}); + +export default GroupCardSkeleton; diff --git a/mobile/components/ui/Skeleton.js b/mobile/components/ui/Skeleton.js new file mode 100644 index 0000000..d603a72 --- /dev/null +++ b/mobile/components/ui/Skeleton.js @@ -0,0 +1,44 @@ +import { useEffect, useRef } from 'react'; +import { Animated, StyleSheet, View } 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: 0.7, + duration: 800, + useNativeDriver: true, + }), + Animated.timing(opacity, { + toValue: 0.3, + duration: 800, + 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..1e891f3 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, @@ -13,6 +12,7 @@ import { import HapticButton from '../components/ui/HapticButton'; import HapticCard from '../components/ui/HapticCard'; import { HapticAppbarAction } from '../components/ui/HapticAppbar'; +import GroupCardSkeleton from '../components/skeletons/GroupCardSkeleton'; import * as Haptics from "expo-haptics"; import { createGroup, getGroups, getOptimizedSettlements } from "../api/groups"; import { AuthContext } from "../context/AuthContext"; @@ -257,8 +257,10 @@ const HomeScreen = ({ navigation }) => { {isLoading ? ( - - + + {[1, 2, 3, 4, 5, 6].map((i) => ( + + ))} ) : ( Date: Mon, 16 Feb 2026 03:31:25 +0000 Subject: [PATCH 2/2] [jules] ux: Refine skeleton loading based on PR feedback Co-authored-by: Devasy23 <110348311+Devasy23@users.noreply.github.com> --- mobile/components/skeletons/GroupCardSkeleton.js | 10 +++++++--- mobile/components/ui/Skeleton.js | 2 +- mobile/screens/HomeScreen.js | 8 ++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/mobile/components/skeletons/GroupCardSkeleton.js b/mobile/components/skeletons/GroupCardSkeleton.js index 2ce2dc2..f8ec80d 100644 --- a/mobile/components/skeletons/GroupCardSkeleton.js +++ b/mobile/components/skeletons/GroupCardSkeleton.js @@ -3,15 +3,19 @@ import { StyleSheet } from 'react-native'; import { Card } from 'react-native-paper'; import Skeleton from '../ui/Skeleton'; -const GroupCardSkeleton = () => { +const GroupCardSkeleton = ({ index = 0 }) => { + // Deterministic random-looking widths based on index + const titleWidth = 100 + ((index * 37) % 60); // 100 - 159 + const subtitleWidth = 140 + ((index * 53) % 90); // 140 - 229 + return ( } + title={} left={() => } /> - + ); diff --git a/mobile/components/ui/Skeleton.js b/mobile/components/ui/Skeleton.js index d603a72..606f43d 100644 --- a/mobile/components/ui/Skeleton.js +++ b/mobile/components/ui/Skeleton.js @@ -1,5 +1,5 @@ import { useEffect, useRef } from 'react'; -import { Animated, StyleSheet, View } from 'react-native'; +import { Animated, StyleSheet } from 'react-native'; import { useTheme } from 'react-native-paper'; const Skeleton = ({ width, height, borderRadius = 4, style }) => { diff --git a/mobile/screens/HomeScreen.js b/mobile/screens/HomeScreen.js index 1e891f3..351921c 100644 --- a/mobile/screens/HomeScreen.js +++ b/mobile/screens/HomeScreen.js @@ -1,5 +1,5 @@ import { useContext, useEffect, useState } from "react"; -import { Alert, FlatList, RefreshControl, StyleSheet, View } from "react-native"; +import { Alert, FlatList, RefreshControl, ScrollView, StyleSheet, View } from "react-native"; import { Appbar, Avatar, @@ -257,11 +257,11 @@ const HomeScreen = ({ navigation }) => { {isLoading ? ( - + {[1, 2, 3, 4, 5, 6].map((i) => ( - + ))} - + ) : (