Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .Jules/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@
- Captures errors in `AppRoutes` and displays a user-friendly message instead of a white screen.
- **Technical:** Created `web/components/ErrorBoundary.tsx` using a hybrid Class+Functional approach to support Hooks in the fallback UI. Integrated into `web/App.tsx`.

- **Mobile Skeleton Loading:** Replaced generic ActivityIndicator with `GroupListSkeleton` for smoother and more professional loading state.
- **Features:**
- Custom animated `Skeleton` primitive utilizing `Animated.loop` to mimic content layout.
- Added comprehensive skeleton list container component mirroring actual data appearance.
- Ensured screen reader compatibility with appropriate progressbar ARIA equivalents (`accessible=true`, `accessibilityRole=progressbar`, `accessibilityLabel="Loading groups"`).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Tighten the accessibility wording in this changelog bullet.

Use clearer prose and quote the role token to avoid ambiguity in docs.

Suggested wording fix
-    - Ensured screen reader compatibility with appropriate progressbar ARIA equivalents (`accessible=true`, `accessibilityRole=progressbar`, `accessibilityLabel="Loading groups"`).
+    - Ensured screen reader compatibility with progress bar semantics (`accessible={true}`, `accessibilityRole="progressbar"`, `accessibilityLabel="Loading groups"`).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Ensured screen reader compatibility with appropriate progressbar ARIA equivalents (`accessible=true`, `accessibilityRole=progressbar`, `accessibilityLabel="Loading groups"`).
- Ensured screen reader compatibility with progress bar semantics (`accessible={true}`, `accessibilityRole="progressbar"`, `accessibilityLabel="Loading groups"`).
🧰 Tools
🪛 LanguageTool

[grammar] ~62-~62: Ensure spelling is correct
Context: ...n reader compatibility with appropriate progressbar ARIA equivalents (accessible=true, `a...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.Jules/changelog.md at line 62, Rewrite the changelog line to use clearer
prose and quote the role token exactly; replace the current bullet with
something like: "Improved screen reader support by enabling accessibility
(accessible=true) and setting accessibilityRole='progressbar' with
accessibilityLabel='Loading groups'." Ensure the role token progressbar is
single-quoted or double-quoted in the text and keep the attribute-like fragments
(accessible=true, accessibilityRole, accessibilityLabel) intact for clarity.

- **Technical:** Created `mobile/components/ui/Skeleton.js` and `mobile/components/skeletons/GroupListSkeleton.js`. Integrated into `mobile/screens/HomeScreen.js`.

- Inline form validation in Auth page with real-time feedback and proper ARIA accessibility support (`aria-invalid`, `aria-describedby`, `role="alert"`).
- Dashboard skeleton loading state (`DashboardSkeleton`) to improve perceived performance during data fetch.
- Comprehensive `EmptyState` component for Groups and Friends pages to better guide new users.
Expand Down
5 changes: 3 additions & 2 deletions .Jules/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@
- 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-14
- Files: `mobile/screens/HomeScreen.js`, `mobile/components/ui/Skeleton.js`, `mobile/components/skeletons/GroupListSkeleton.js`
- Context: Replace ActivityIndicator with skeleton group cards
- Impact: Better loading experience, less jarring
- Size: ~40 lines
Expand Down
47 changes: 47 additions & 0 deletions mobile/components/skeletons/GroupListSkeleton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import { View, StyleSheet } from 'react-native';
import { Card } from 'react-native-paper';
import Skeleton from '../ui/Skeleton';

const GroupListSkeletonItem = () => {
return (
<Card style={styles.card}>
<Card.Title
title={<Skeleton width={120} height={16} borderRadius={4} />}
left={(props) => <Skeleton width={40} height={40} borderRadius={20} />}
/>
<Card.Content>
<Skeleton width={150} height={14} borderRadius={4} style={styles.subtitle} />
</Card.Content>
</Card>
);
};

const GroupListSkeleton = ({ count = 3 }) => {
return (
<View
style={styles.container}
accessible={true}
accessibilityRole="progressbar"
accessibilityLabel="Loading groups"
>
{Array.from({ length: count }).map((_, index) => (
<GroupListSkeletonItem key={index} />
))}
</View>
);
};

const styles = StyleSheet.create({
container: {
padding: 16,
},
card: {
marginBottom: 16,
},
subtitle: {
marginTop: 4,
},
});

export default GroupListSkeleton;
51 changes: 51 additions & 0 deletions mobile/components/ui/Skeleton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
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 loop = Animated.loop(
Animated.sequence([
Animated.timing(opacity, {
toValue: 0.7,
duration: 800,
useNativeDriver: true,
}),
Animated.timing(opacity, {
toValue: 0.3,
duration: 800,
useNativeDriver: true,
}),
])
);
loop.start();
return () => loop.stop();
}, [opacity]);

return (
<Animated.View
style={[
styles.skeleton,
{
width,
height,
borderRadius,
backgroundColor: theme.colors.surfaceVariant,
opacity,
},
style,
]}
/>
);
};

const styles = StyleSheet.create({
skeleton: {
overflow: 'hidden',
},
});

export default Skeleton;
5 changes: 2 additions & 3 deletions mobile/screens/HomeScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import HapticButton from '../components/ui/HapticButton';
import HapticCard from '../components/ui/HapticCard';
import { HapticAppbarAction } from '../components/ui/HapticAppbar';
import GroupListSkeleton from '../components/skeletons/GroupListSkeleton';
import * as Haptics from "expo-haptics";
import { createGroup, getGroups, getOptimizedSettlements } from "../api/groups";
import { AuthContext } from "../context/AuthContext";
Expand Down Expand Up @@ -257,9 +258,7 @@ const HomeScreen = ({ navigation }) => {
</Appbar.Header>

{isLoading ? (
<View style={styles.loaderContainer}>
<ActivityIndicator size="large" />
</View>
<GroupListSkeleton />
) : (
<FlatList
data={groups}
Expand Down
Loading