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
8 changes: 8 additions & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ jobs:
run: npm install
working-directory: ./frontend

- name: Create .env file
working-directory: ./frontend
run: |
echo "EXPO_PUBLIC_GOOGLE_EXPO_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_EXPO_CLIENT_ID }}" >> .env
echo "EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID }}" >> .env
echo "EXPO_PUBLIC_GOOGLE_ANDROID_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_ANDROID_CLIENT_ID }}" >> .env
echo "EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID }}" >> .env
Comment on lines +32 to +38
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

Prevent duplicate entries in .env file

The current implementation uses >> which appends to the file. If the file already exists (from a previous run or cache), this could create duplicate entries. Consider clearing the file first:

 - name: Create .env file
   working-directory: ./frontend
   run: |
+    > .env  # Clear the file first
     echo "EXPO_PUBLIC_GOOGLE_EXPO_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_EXPO_CLIENT_ID }}" >> .env
     echo "EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID }}" >> .env
     echo "EXPO_PUBLIC_GOOGLE_ANDROID_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_ANDROID_CLIENT_ID }}" >> .env
     echo "EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID }}" >> .env
📝 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
- name: Create .env file
working-directory: ./frontend
run: |
echo "EXPO_PUBLIC_GOOGLE_EXPO_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_EXPO_CLIENT_ID }}" >> .env
echo "EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID }}" >> .env
echo "EXPO_PUBLIC_GOOGLE_ANDROID_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_ANDROID_CLIENT_ID }}" >> .env
echo "EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID }}" >> .env
- name: Create .env file
working-directory: ./frontend
run: |
> .env # Clear the file first
echo "EXPO_PUBLIC_GOOGLE_EXPO_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_EXPO_CLIENT_ID }}" >> .env
echo "EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID }}" >> .env
echo "EXPO_PUBLIC_GOOGLE_ANDROID_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_ANDROID_CLIENT_ID }}" >> .env
echo "EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID=${{ secrets.EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID }}" >> .env
🤖 Prompt for AI Agents
.github/workflows/preview.yml around lines 32 to 38: the workflow currently
appends environment variables to ./frontend/.env using >> which can create
duplicate entries if the file already exists; instead ensure the file is either
truncated or rebuilt before writing (for example, write the first variable with
> to overwrite/truncate then append the rest with >>, or explicitly
remove/truncate the .env file first, or use a here-doc to atomically create the
file), so update the run step to clear or overwrite .env before adding the
variables.


- name: Create preview
uses: expo/expo-github-action/preview@v8
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ yarn-error.*

# local env files
.env*.local
frontend/.env

# typescript
*.tsbuildinfo
Expand Down
4 changes: 4 additions & 0 deletions frontend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
EXPO_PUBLIC_GOOGLE_EXPO_CLIENT_ID=
EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID=
EXPO_PUBLIC_GOOGLE_ANDROID_CLIENT_ID=
EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID=
4 changes: 4 additions & 0 deletions frontend/api/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export const signup = (name, email, password) => {
return apiClient.post("/auth/signup/email", { name, email, password });
};

export const loginWithGoogle = (id_token) => {
return apiClient.post("/auth/login/google", { id_token });
};

export const updateUser = (userData) => apiClient.patch("/users/me", userData);

export const refresh = (refresh_token) => {
Expand Down
3 changes: 3 additions & 0 deletions frontend/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"scheme": "splitwiser",
"newArchEnabled": true,
"splash": {
"image": "./assets/splash-icon.png",
Expand All @@ -14,11 +15,13 @@
},
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.devasy23.splitwiser",
"infoPlist": {
"NSPhotoLibraryUsageDescription": "Allow Splitwiser to select a group icon from your photo library."
}
},
"android": {
"package": "com.devasy23.splitwiser",
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
Expand Down
62 changes: 61 additions & 1 deletion frontend/context/AuthContext.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import AsyncStorage from "@react-native-async-storage/async-storage";
import { useAuthRequest } from "expo-auth-session/providers/google";
import * as WebBrowser from "expo-web-browser";
import { createContext, useEffect, useState } from "react";
import * as authApi from "../api/auth";
import {
Expand All @@ -7,6 +9,8 @@ import {
setTokenUpdateListener,
} from "../api/client";

WebBrowser.maybeCompleteAuthSession();

export const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
Expand All @@ -15,13 +19,64 @@ export const AuthProvider = ({ children }) => {
const [refresh, setRefresh] = useState(null);
const [isLoading, setIsLoading] = useState(true);

// For Expo Go, we need to use the web-based auth flow
// Force all platforms to use web client ID in Expo Go
const [request, response, promptAsync] = useAuthRequest({
expoClientId: process.env.EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID,
iosClientId: process.env.EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID, // Force web client for iOS in Expo Go
androidClientId: process.env.EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID, // Force web client for Android in Expo Go
webClientId: process.env.EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID,
redirectUri: 'https://auth.expo.io/@devasy23/frontend',
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

Avoid hardcoding the redirect URI.

The redirect URI is hardcoded as 'https://auth.expo.io/@devasy23/frontend' which will break in different environments or for different developers. Consider using an environment variable or dynamic configuration.

-    redirectUri: 'https://auth.expo.io/@devasy23/frontend',
+    redirectUri: process.env.EXPO_PUBLIC_REDIRECT_URI || makeRedirectUri({
+      useProxy: true,
+    }),

Also import makeRedirectUri from expo-auth-session:

import { makeRedirectUri } from 'expo-auth-session';
🤖 Prompt for AI Agents
In frontend/context/AuthContext.js around line 29, the redirectUri is hardcoded
which fails across environments and developers; update the file to import
makeRedirectUri from 'expo-auth-session' and replace the hardcoded URI with a
dynamically generated value (e.g., use makeRedirectUri with appropriate options
or read from an environment/config variable like process.env.EXPO_REDIRECT_URI
or an app config entry), and ensure any environment variable is documented and
used as fallback if makeRedirectUri cannot be used.

});

// Debug logging
useEffect(() => {
if (request) {
console.log("Auth request details:", {
url: request.url,
params: request.params
});
}
}, [request]);

useEffect(() => {
const handleGoogleSignIn = async () => {
if (response?.type === "success") {
const { id_token } = response.params;
try {
const res = await authApi.loginWithGoogle(id_token);
const { access_token, refresh_token, user: userData } = res.data;
setToken(access_token);
setRefresh(refresh_token);
await setAuthTokens({
newAccessToken: access_token,
newRefreshToken: refresh_token,
});
const normalizedUser = userData?._id
? userData
: userData?.id
? { ...userData, _id: userData.id }
: userData;
setUser(normalizedUser);
} catch (error) {
console.error(
"Google login failed:",
error.response?.data?.detail || error.message
);
}
}
};

handleGoogleSignIn();
}, [response]);

// Load token and user data from AsyncStorage on app start
useEffect(() => {
const loadStoredAuth = async () => {
try {
const storedToken = await AsyncStorage.getItem("auth_token");
const storedRefresh = await AsyncStorage.getItem("refresh_token");
const storedUser = await AsyncStorage.getItem("user_data");
const storedUser = await AsyncStorage.getItem("user_data");

if (storedToken && storedUser) {
setToken(storedToken);
Expand Down Expand Up @@ -146,6 +201,10 @@ export const AuthProvider = ({ children }) => {
}
};

const loginWithGoogle = async () => {
await promptAsync();
};
Comment on lines +204 to +206
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 error handling to loginWithGoogle

The function should handle potential errors from promptAsync and provide feedback.

 const loginWithGoogle = async () => {
-  await promptAsync();
+  try {
+    const result = await promptAsync();
+    return result;
+  } catch (error) {
+    console.error("Failed to initiate Google sign-in:", error);
+    throw error;
+  }
 };
📝 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
const loginWithGoogle = async () => {
await promptAsync();
};
const loginWithGoogle = async () => {
try {
const result = await promptAsync();
return result;
} catch (error) {
console.error("Failed to initiate Google sign-in:", error);
throw error;
}
};
🤖 Prompt for AI Agents
In frontend/context/AuthContext.js around lines 191 to 193, the loginWithGoogle
function currently awaits promptAsync without handling failures; wrap the
promptAsync call in a try/catch, handle errors by logging/reporting them (e.g.,
process/logging/toast) and optionally update relevant auth/error state or return
a failure indicator, and ensure any resources or loading flags are cleared in a
finally block so the UI gets proper feedback on success or failure.


const logout = async () => {
try {
// Clear stored authentication data
Expand Down Expand Up @@ -182,6 +241,7 @@ export const AuthProvider = ({ children }) => {
signup,
logout,
updateUserInContext,
loginWithGoogle,
}}
>
{children}
Expand Down
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
65 changes: 65 additions & 0 deletions frontend/package-lock.json

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

2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"@react-navigation/native-stack": "^7.3.23",
"axios": "^1.11.0",
"expo": "~53.0.20",
"expo-auth-session": "^6.2.1",
"expo-crypto": "^14.1.5",
"expo-image-picker": "~16.0.2",
Comment on lines +19 to 21
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

Verify SDK compatibility and avoid caret ranges for Expo-managed packages.

expo-auth-session and expo-crypto should match Expo SDK 53’s compatible versions. Using ^ can drift to incompatible minor versions; prefer versions installed via “npx expo install …”.

  • Run “npx expo install expo-auth-session expo-crypto” to pin compatible versions.
  • If you keep manual versions, confirm compatibility:

🌐 Web query:

What versions of expo-auth-session and expo-crypto are compatible with Expo SDK 53?

Error during web search


🏁 Script executed:

#!/bin/bash
# Retrieve the Expo SDK version from app.json (if present)
rg '"sdkVersion"' -n

# Retrieve the Expo package version from frontend/package.json
rg '"expo"' -n frontend/package.json | sed -n '1,20p'

Length of output: 103


Pin Expo-managed package versions with expo install
To ensure compatibility with SDK 53 (as specified by "expo": "~53.0.20" in frontend/package.json), replace the caret (^) ranges for expo-auth-session and expo-crypto with the exact versions that npx expo install selects. This prevents drifting into unsupported minor or patch releases.

• File: frontend/package.json (lines 19–21)
• Run:

npx expo install expo-auth-session expo-crypto

• Commit the updated dependency entries (no leading ^).

🤖 Prompt for AI Agents
In frontend/package.json around lines 19 to 21, the expo-auth-session and
expo-crypto dependencies are specified with caret ranges which can drift outside
SDK 53 compatibility; run `npx expo install expo-auth-session expo-crypto` to
let Expo pick the exact compatible versions, replace the current entries to the
exact versions returned by that command (remove the leading ^), save the file
and commit the updated package.json (and package-lock or yarn.lock if
applicable).

"expo-status-bar": "~2.2.3",
"react": "19.0.0",
Expand Down
Loading
Loading