Skip to content
Closed
Show file tree
Hide file tree
Changes from 8 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
40 changes: 40 additions & 0 deletions frontend/components/SkeletonLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, { useEffect } from "react";
import { View, StyleSheet } from "react-native";
import Animated, {
useSharedValue,
useAnimatedStyle,
withRepeat,
withTiming,
interpolateColor,
} from "react-native-reanimated";
Comment on lines +3 to +9
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add animation cleanup and satisfy hooks deps

Cleanup prevents lingering animations after unmount and fixes the missing dependency warning.

Apply:

 import Animated, {
   useSharedValue,
   useAnimatedStyle,
   withRepeat,
   withTiming,
   interpolateColor,
+  cancelAnimation,
 } from "react-native-reanimated";
@@
-  useEffect(() => {
-    progress.value = withRepeat(withTiming(1, { duration: 1000 }), -1, true);
-  }, []);
+  useEffect(() => {
+    progress.value = withRepeat(withTiming(1, { duration: 1000 }), -1, true);
+    return () => cancelAnimation(progress);
+  }, [progress]);

Also applies to: 15-18

🤖 Prompt for AI Agents
In frontend/components/SkeletonLoader.js around lines 3–9 (and also apply the
same to lines 15–18), the animated shared values and effects are not cleaned up
and some hook dependencies are missing; add a useEffect that returns a cleanup
function which cancels any running animations (use reanimated's cancelAnimation
or stop methods on the relevant shared values) when the component unmounts, and
ensure all hook dependency arrays (useEffect/useAnimatedStyle/useDerivedValue)
include the values/config they reference (or memoize them) to satisfy the
missing-deps warnings.

import { colors } from "../styles/theme";

const SkeletonLoader = ({ style }) => {
const progress = useSharedValue(0);

useEffect(() => {
progress.value = withRepeat(withTiming(1, { duration: 1000 }), -1, true);
}, []);

const animatedStyle = useAnimatedStyle(() => {
const backgroundColor = interpolateColor(
progress.value,
[0, 1],
[colors.secondary, colors.white]
);
return {
backgroundColor,
};
});

return <Animated.View style={[styles.skeleton, animatedStyle, style]} />;
};

const styles = StyleSheet.create({
skeleton: {
backgroundColor: colors.secondary,
borderRadius: 4,
},
});

export default SkeletonLoader;
4 changes: 2 additions & 2 deletions frontend/navigation/GroupsStackNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ const GroupsStackNavigator = () => {
<Stack.Navigator>
<Stack.Screen name="GroupsList" component={HomeScreen} options={{ headerShown: false }}/>
<Stack.Screen name="GroupDetails" component={GroupDetailsScreen} />
<Stack.Screen name="AddExpense" component={AddExpenseScreen} options={{ title: 'Add Expense' }} />
<Stack.Screen name="AddExpense" component={AddExpenseScreen} options={{ headerShown: false }} />
<Stack.Screen name="JoinGroup" component={JoinGroupScreen} options={{ headerShown: false }} />
<Stack.Screen name="GroupSettings" component={GroupSettingsScreen} options={{ title: 'Group Settings' }} />
<Stack.Screen name="GroupSettings" component={GroupSettingsScreen} options={{ headerShown: false }} />
</Stack.Navigator>
);
};
Expand Down
51 changes: 51 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"react-native-paper": "^5.14.5",
"react-native-safe-area-context": "^5.5.2",
"react-native-screens": "^4.13.1",
"react-native-web": "^0.20.0"
"react-native-web": "^0.20.0",
"react-native-reanimated": "~3.17.4"
},
Comment on lines +27 to 29
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Ensure Reanimated is fully configured (Babel plugin + install checks)

Adding react-native-reanimated requires the Babel plugin and correct plugin ordering to avoid runtime errors and to enable worklets. Verify the project’s babel.config.js includes the plugin last, and that web support is considered if you target web.

Run this script to confirm the plugin is present and last in the plugins array:

If missing, add this to babel.config.js (ensure it’s the last plugin):

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ["babel-preset-expo"],
    plugins: [
      // other plugins...
      "react-native-reanimated/plugin", // must be last
    ],
  };
};

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate babel config(s)
fd -a '^babel\.config\.js$' | while read -r f; do
  echo "Checking $f"
  # Show plugins line(s)
  rg -n "plugins" -A 5 -n "$f"
  # Check if reanimated plugin is present
  if rg -q "react-native-reanimated/plugin" "$f"; then
    echo "✓ Found reanimated plugin in $f"
  else
    echo "✗ Missing reanimated plugin in $f"
  fi
done

Length of output: 61


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Locate babel config files (common names):"
find . -maxdepth 4 -type f \( -iname "babel.config.js" -o -iname "babel.config.cjs" -o -iname "babel.config.mjs" -o -iname ".babelrc" -o -iname ".babelrc.js" -o -iname ".babelrc.json" \) -print || true

echo
echo "2) Check package.json for a 'babel' field:"
if [ -f package.json ]; then
  rg -n '"babel"\s*:' package.json || echo "No 'babel' key in package.json"
else
  echo "No package.json at repository root"
fi

echo
echo "3) Search entire repo for reanimated plugin string:"
rg -n --hidden --no-ignore 'react-native-reanimated/plugin' --glob '!node_modules' || echo "No occurrences of reanimated plugin string found"

echo
echo "4) Search for any mention of react-native-reanimated:"
rg -n --hidden --no-ignore 'react-native-reanimated' --glob '!node_modules' || echo "No occurrences of react-native-reanimated found"

echo
echo "5) If any babel config files found above, print their contents (best-effort):"
for f in $(find . -maxdepth 4 -type f \( -iname "babel.config.js" -o -iname "babel.config.cjs" -o -iname ".babelrc" -o -iname ".babelrc.js" -o -iname ".babelrc.json" \) -print); do
  echo "---- $f ----"
  sed -n '1,240p' "$f"
  echo
done

Length of output: 1918


Add react-native-reanimated Babel plugin (must be last) and verify setup

react-native-reanimated is present (frontend/package.json) and imported (frontend/components/SkeletonLoader.js), but no Babel config (.babelrc / babel.config.*) was found — the Reanimated Babel plugin appears to be missing.

Files to fix / check:

  • frontend/package.json — dependency added
  • frontend/components/SkeletonLoader.js — imports reanimated
  • Missing: no babel.config.js or .babelrc detected in the repo root or common locations — add one in the frontend package

Suggested babel config (ensure the plugin is the last entry in plugins):

// frontend/babel.config.js
module.exports = function (api) {
  api.cache(true);
  return {
    presets: ["babel-preset-expo"], // or your project's presets
    plugins: [
      // other plugins...
      "react-native-reanimated/plugin" // MUST be last
    ],
  };
};

After adding the plugin: clear Metro cache / rebuild the app and verify worklets/runtime errors are gone. If you target web, also verify Reanimated's web setup for your project.

🤖 Prompt for AI Agents
In frontend/package.json around lines 27 to 29, react-native-reanimated is added
but there is no Babel config in the frontend package; create
frontend/babel.config.js exporting a function that returns your project presets
(e.g. babel-preset-expo or app presets) and a plugins array where
"react-native-reanimated/plugin" is the last entry; ensure the file is placed in
the frontend package root, then clear Metro cache and rebuild (and verify
web-specific Reanimated setup if targeting web).

"devDependencies": {
"@babel/core": "^7.20.0"
Expand Down
146 changes: 104 additions & 42 deletions frontend/screens/AccountScreen.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Ionicons } from "@expo/vector-icons";
import { useContext } from "react";
import { Alert, StyleSheet, View } from "react-native";
import { Appbar, Avatar, Divider, List, Text } from "react-native-paper";
import { Alert, StyleSheet, TouchableOpacity, View } from "react-native";
import { Appbar, Avatar, Divider, Text } from "react-native-paper";
import { AuthContext } from "../context/AuthContext";
import { colors, spacing, typography } from "../styles/theme";

const AccountScreen = ({ navigation }) => {
const { user, logout } = useContext(AuthContext);
Expand All @@ -14,51 +16,84 @@ const AccountScreen = ({ navigation }) => {
Alert.alert("Coming Soon", "This feature is not yet implemented.");
};

const menuItems = [
{
title: "Edit Profile",
icon: "person-outline",
onPress: () => navigation.navigate("EditProfile"),
},
{
title: "Email Settings",
icon: "mail-outline",
onPress: handleComingSoon,
},
{
title: "Send Feedback",
icon: "chatbubble-ellipses-outline",
onPress: handleComingSoon,
},
{
title: "Logout",
icon: "log-out-outline",
onPress: handleLogout,
color: colors.error,
},
];

return (
<View style={styles.container}>
<Appbar.Header>
<Appbar.Content title="Account" />
<Appbar.Header style={{ backgroundColor: colors.primary }}>
<Appbar.Content
title="Account"
color={colors.white}
titleStyle={{ ...typography.h2 }}
/>
</Appbar.Header>
<View style={styles.content}>
<View style={styles.profileSection}>
{user?.imageUrl && /^(https?:|data:image)/.test(user.imageUrl) ? (
<Avatar.Image size={80} source={{ uri: user.imageUrl }} />
<Avatar.Image
size={100}
source={{ uri: user.imageUrl }}
style={styles.avatar}
/>
) : (
<Avatar.Text size={80} label={user?.name?.charAt(0) || "A"} />
<Avatar.Text
size={100}
label={user?.name?.charAt(0) || "A"}
style={styles.avatar}
labelStyle={{ ...typography.h1, color: colors.white }}
/>
)}
<Text variant="headlineSmall" style={styles.name}>
{user?.name}
</Text>
<Text variant="bodyLarge" style={styles.email}>
{user?.email}
</Text>
<Text style={styles.name}>{user?.name}</Text>
<Text style={styles.email}>{user?.email}</Text>
</View>

<List.Section>
<List.Item
title="Edit Profile"
left={() => <List.Icon icon="account-edit" />}
onPress={() => navigation.navigate("EditProfile")}
/>
<Divider />
<List.Item
title="Email Settings"
left={() => <List.Icon icon="email-edit-outline" />}
onPress={handleComingSoon}
/>
<Divider />
<List.Item
title="Send Feedback"
left={() => <List.Icon icon="message-alert-outline" />}
onPress={handleComingSoon}
/>
<Divider />
<List.Item
title="Logout"
left={() => <List.Icon icon="logout" />}
onPress={handleLogout}
/>
</List.Section>
<View style={styles.menuContainer}>
{menuItems.map((item, index) => (
<View key={item.title}>
<TouchableOpacity style={styles.menuItem} onPress={item.onPress}>
<Ionicons
name={item.icon}
size={24}
color={item.color || colors.primary}
style={styles.menuIcon}
/>
<Text
style={[styles.menuItemText, { color: item.color || colors.text }]}
>
{item.title}
</Text>
<Ionicons
name="chevron-forward-outline"
size={24}
color={colors.textSecondary}
/>
</TouchableOpacity>
{index < menuItems.length - 1 && <Divider />}
</View>
))}
</View>
</View>
</View>
);
Expand All @@ -67,20 +102,47 @@ const AccountScreen = ({ navigation }) => {
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.secondary,
},
content: {
padding: 16,
padding: spacing.md,
},
profileSection: {
alignItems: "center",
marginBottom: 24,
marginBottom: spacing.xl,
backgroundColor: colors.white,
padding: spacing.lg,
borderRadius: spacing.sm,
},
avatar: {
backgroundColor: colors.primary,
},
name: {
marginTop: 16,
...typography.h2,
marginTop: spacing.md,
color: colors.text,
},
email: {
marginTop: 4,
color: "gray",
...typography.body,
marginTop: spacing.xs,
color: colors.textSecondary,
},
menuContainer: {
backgroundColor: colors.white,
borderRadius: spacing.sm,
},
menuItem: {
flexDirection: "row",
alignItems: "center",
paddingVertical: spacing.md,
paddingHorizontal: spacing.md,
},
menuIcon: {
marginRight: spacing.md,
},
menuItemText: {
...typography.body,
flex: 1,
},
});

Expand Down
Loading
Loading