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) => (
-
+
))}
-
+
) : (