Skip to content

M3 Changes#2

Open
webianks wants to merge 1 commit into
mainfrom
webianks/phase3
Open

M3 Changes#2
webianks wants to merge 1 commit into
mainfrom
webianks/phase3

Conversation

@webianks

Copy link
Copy Markdown
Owner

This pull request introduces user authentication integration and improves cart management by associating carts with user accounts using Firebase Auth. It also refactors order-related data and repositories, and restructures sample data files. The most significant changes are grouped below.

Authentication and Cart Management Integration:

  • Added Firebase Auth as a dependency and integrated it into the app, including passing an AuthViewModel to screens and navigation. (app/build.gradle.kts, AppNavigation.kt, GenericCoreModule.kt) [1] [2] [3] [4] [5] [6] [7] [8]
  • Updated DataStoreCartRepository to store cart data separately for authenticated users and guests, using Firebase Auth state. Added methods to transfer guest carts to user accounts and clear user carts. (DataStoreCartRepository.kt) [1] [2] [3] [4] [5]

Order Data and Repository Refactor:

  • Introduced new Order and OrderItem data classes to represent order details. (Order.kt)
  • Refactored OrdersRepository interface and its implementations to use the new Order data class and updated method names. (OrdersRepository.kt, PlaceholderOrdersRepository.kt, FakeOrdersRepository.kt) [1] [2] [3]
  • Added a ViewModelFactory for injecting a fake orders repository into HistoryViewModel for testing or preview purposes. (ViewModelFactory.kt)

Sample Data and Utility Additions:

  • Moved and refactored sample data from ui/screens/SampleData.kt to data/SampleData.kt for better separation of concerns. (SampleData.kt) [1] [2]
  • Added a set of country calling codes as a utility for use in authentication or user profile features. (CountryCallingCodes.kt)

UI Component Fixes:

  • Fixed a parameter name in AddToCartBottomBar to correctly pass the button modifier. (BottomBar.kt)
  • Updated button styling imports and parameters in Buttons.kt for UI consistency. (Buttons.kt)

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @webianks, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the application by introducing robust user authentication via Firebase, allowing users to sign in and manage their carts and order history more personally. It also refactors core data structures related to orders and improves the overall UI consistency and code organization, laying the groundwork for a more feature-rich and user-centric experience.

Highlights

  • User Authentication Integration: Implemented Firebase Authentication to allow users to sign in with their phone numbers. This includes adding the Firebase Auth dependency, creating a dedicated AuthScreen and AuthViewModel, and integrating them into the app's navigation flow. Users can now sign in, receive OTPs, and log out.
  • Enhanced Cart Management: Refactored DataStoreCartRepository to associate shopping carts with authenticated user accounts. Guest carts are now stored separately and can be seamlessly transferred to a user's account upon successful login. New methods were added to clear user-specific carts.
  • Order Data Refactoring: Introduced new Order and OrderItem data classes to provide a more structured representation of order details. The OrdersRepository interface and its implementations (PlaceholderOrdersRepository) were updated to utilize these new data classes, improving data consistency and maintainability. A FakeOrdersRepository was also added for testing purposes.
  • Improved Order History Display: The HistoryScreen was updated to dynamically display order history based on the user's login status. It now shows actual order data for logged-in users (via HistoryViewModel and FakeOrdersRepository) and prompts guests to sign in. A new OrderHistoryCard component was created for a consistent UI.
  • Codebase Restructuring and Utilities: Moved sample data from ui/screens/SampleData.kt to data/SampleData.kt for better separation of concerns. A new utility file CountryCallingCodes.kt was added, containing a comprehensive list of country calling codes, likely for use in the authentication flow.
  • UI Component Enhancements: Made several UI adjustments, including fixing a parameter name in AddToCartBottomBar and updating button styling in Buttons.kt for better consistency and responsiveness. New drawable assets for logout and user icons were also added.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a significant and well-structured user authentication feature using Firebase Auth. The integration into the existing app, including the separation of guest and user carts, is handled effectively. The refactoring of order-related data and UI components to reflect authentication state is also a welcome improvement. I've identified one high-severity issue regarding potential cart data loss during login, along with a few medium-severity suggestions to improve the design, reduce code duplication, and fix a minor bug in the timer logic. Overall, this is a strong implementation of a complex feature.

Comment on lines +111 to +122
suspend fun transferGuestCartToUser(uid: String) {
val guestCartJson = dataStore.data.first()[stringPreferencesKey(GUEST_CART_KEY)]
if (!guestCartJson.isNullOrEmpty()) {
val guestCart = json.decodeFromString<List<CartLine>>(guestCartJson)
if (guestCart.isNotEmpty()) {
dataStore.edit {
it[stringPreferencesKey(uid)] = json.encodeToString(guestCart)
it.remove(stringPreferencesKey(GUEST_CART_KEY))
}
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The current implementation of transferGuestCartToUser overwrites the user's existing cart with the guest cart. If a user logs in and already has items in their cart from a previous session, those items will be lost. The guest cart should be merged with the user's existing cart instead of replacing it to prevent data loss.

    suspend fun transferGuestCartToUser(uid: String) {
        val guestCartJson = dataStore.data.first()[stringPreferencesKey(GUEST_CART_KEY)]
        if (guestCartJson.isNullOrEmpty()) return

        val guestCart = json.decodeFromString<List<CartLine>>(guestCartJson)
        if (guestCart.isEmpty()) return

        dataStore.edit { preferences ->
            val userCartKey = stringPreferencesKey(uid)
            val userCartJson = preferences[userCartKey]
            val userCart = if (userCartJson.isNullOrEmpty()) {
                emptyList()
            } else {
                json.decodeFromString<List<CartLine>>(userCartJson)
            }

            // Merge guest cart into user cart
            val mergedCart = userCart.toMutableList()
            guestCart.forEach { guestLine ->
                val existingLineIndex = mergedCart.indexOfFirst { it.identityKey() == guestLine.identityKey() }
                if (existingLineIndex != -1) {
                    val existingLine = mergedCart[existingLineIndex]
                    mergedCart[existingLineIndex] = existingLine.copy(quantity = existingLine.quantity + guestLine.quantity)
                } else {
                    mergedCart.add(guestLine)
                }
            }

            preferences[userCartKey] = json.encodeToString(mergedCart)
            preferences.remove(stringPreferencesKey(GUEST_CART_KEY))
        }
    }

Comment on lines +57 to +58
val cartRepository = cartRepo(context) as DataStoreCartRepository
return AuthViewModel(FirebaseAuth.getInstance(), cartRepository) as T

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Casting cartRepo(context) to the concrete class DataStoreCartRepository breaks the Dependency Inversion Principle, as AuthViewModel now depends on a concrete implementation instead of an abstraction. This makes the code less flexible and harder to test.

To improve this, you could:

  1. Add the required methods (transferGuestCartToUser, clearUserCart) to the CartRepository interface, possibly with empty default implementations.
  2. Update AuthViewModel to depend on the CartRepository interface.
  3. Remove the cast in this factory.

This will lead to a more robust and maintainable architecture.

Comment on lines +249 to +291
val isPhoneNumberPotentiallyValid = { number: String ->
if (!number.startsWith("+")) {
false
} else {
val digits = number.substring(1)
val countryCode = (3 downTo 1).asSequence()
.mapNotNull { len ->
digits.takeIf { it.length >= len }?.substring(0, len)?.toIntOrNull()
}
.firstOrNull { it in countryCallingCodes }

if (countryCode == null) {
false
} else {
val nationalNumber = digits.substring(countryCode.toString().length)
nationalNumber.length in 7..12
}
}
}

val validatePhoneNumber = { number: String ->
if (isPhoneNumberPotentiallyValid(number)) {
isError = false
errorMessage = ""
} else {
isError = true
if (!number.startsWith("+")) {
errorMessage = "Phone number must start with '+' (country code)."
} else {
val digits = number.substring(1)
val countryCode = (3 downTo 1).asSequence()
.mapNotNull { len ->
digits.takeIf { it.length >= len }?.substring(0, len)?.toIntOrNull()
}
.firstOrNull { it in countryCallingCodes }
if (countryCode == null) {
errorMessage = "Invalid country code."
} else {
errorMessage = "Please enter a valid number of digits (7-12)."
}
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The logic for validating a phone number is duplicated within the isPhoneNumberPotentiallyValid and validatePhoneNumber lambdas. This makes the code harder to maintain, as any change to the validation rules needs to be applied in multiple places. This logic is also similar to what's in PhoneNumberVisualTransformation.

Consider extracting the core validation logic into a single, reusable top-level function. This function could be used by both lambdas to reduce duplication and improve clarity.

timerJob?.cancel()
timerJob = viewModelScope.launch {
flow {
for (i in 60 downTo 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The timer loop for (i in 60 downTo 0) runs for 61 seconds (from 60 to 0 inclusive). Standard countdown timers are usually exclusive of the top value (e.g., a 60-second timer counts from 59 down to 0). This should probably be for (i in 59 downTo 0) to implement a correct 60-second timer.

Suggested change
for (i in 60 downTo 0) {
for (i in 59 downTo 0) {

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request introduces user authentication via Firebase Phone Auth and refactors cart management to support user-specific carts with seamless guest-to-user cart migration. The changes also include order history functionality, UI component improvements, and data model restructuring.

Key Changes:

  • Implemented Firebase Phone Authentication with OTP verification, including phone number validation and visual formatting
  • Refactored cart repository to store separate carts for authenticated users and guests, with cart transfer capability upon sign-in
  • Introduced new Order data models and refactored OrdersRepository interface with updated implementations for order history display

Reviewed Changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 19 comments.

Show a summary per file
File Description
app/build.gradle.kts Added Firebase Auth dependency
gradle/libs.versions.toml Added Firebase Auth library reference
AppNavigation.kt Integrated AuthViewModel and added auth route to navigation
GenericCoreModule.kt Added AuthViewModelFactory and updated cart repository with Firebase Auth instance
AuthViewModel.kt New ViewModel managing phone authentication state, OTP verification, and cart transfer
AuthScreen.kt New authentication UI with phone input, OTP verification, and custom phone number formatting
HomeScreen.kt Added authentication state handling, logout dialog, and account click actions
HistoryScreen.kt Refactored to show order history for authenticated users with responsive grid layout
HistoryViewModel.kt Converted to proper ViewModel with StateFlow-based UI state management
DataStoreCartRepository.kt Refactored to support user-specific carts with Firebase Auth integration and cart transfer logic
Order.kt New data classes for Order and OrderItem
OrdersRepository.kt Updated interface method name from history() to getOrderHistory()
FakeOrdersRepository.kt New implementation providing sample order data for testing/preview
PlaceholderOrdersRepository.kt Updated to match new repository interface
OrderHistoryCard.kt New composable for displaying order information with status badges
NavBars.kt Added account/logout icon button with authentication state awareness
Dialogs.kt New logout confirmation dialog component
Buttons.kt Refactored PrimaryGradientButton with improved sizing control and disabled state support
BottomBar.kt Fixed parameter name to use buttonModifier instead of modifier
Cards.kt Updated import paths for sample data
Type.kt Added Label3Medium text style for smaller labels
Color.kt Added new color values for warnings, success states, and transparency variants
SampleData.kt Moved from ui/screens package to data package
CountryCallingCodes.kt New utility file containing valid country calling codes for phone validation
ViewModelFactory.kt New factory for creating HistoryViewModel with FakeOrdersRepository
ic_user.xml New user icon drawable
ic_logout.xml New logout icon drawable

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +249 to +290
val isPhoneNumberPotentiallyValid = { number: String ->
if (!number.startsWith("+")) {
false
} else {
val digits = number.substring(1)
val countryCode = (3 downTo 1).asSequence()
.mapNotNull { len ->
digits.takeIf { it.length >= len }?.substring(0, len)?.toIntOrNull()
}
.firstOrNull { it in countryCallingCodes }

if (countryCode == null) {
false
} else {
val nationalNumber = digits.substring(countryCode.toString().length)
nationalNumber.length in 7..12
}
}
}

val validatePhoneNumber = { number: String ->
if (isPhoneNumberPotentiallyValid(number)) {
isError = false
errorMessage = ""
} else {
isError = true
if (!number.startsWith("+")) {
errorMessage = "Phone number must start with '+' (country code)."
} else {
val digits = number.substring(1)
val countryCode = (3 downTo 1).asSequence()
.mapNotNull { len ->
digits.takeIf { it.length >= len }?.substring(0, len)?.toIntOrNull()
}
.firstOrNull { it in countryCallingCodes }
if (countryCode == null) {
errorMessage = "Invalid country code."
} else {
errorMessage = "Please enter a valid number of digits (7-12)."
}
}
}

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

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

The phone number validation logic is duplicated between the validation lambda at line 249-267 and the validatePhoneNumber lambda at line 269-291. This code duplication makes the implementation harder to maintain.

Consider extracting this into a single reusable function to avoid inconsistencies if the validation logic needs to be updated.

Suggested change
val isPhoneNumberPotentiallyValid = { number: String ->
if (!number.startsWith("+")) {
false
} else {
val digits = number.substring(1)
val countryCode = (3 downTo 1).asSequence()
.mapNotNull { len ->
digits.takeIf { it.length >= len }?.substring(0, len)?.toIntOrNull()
}
.firstOrNull { it in countryCallingCodes }
if (countryCode == null) {
false
} else {
val nationalNumber = digits.substring(countryCode.toString().length)
nationalNumber.length in 7..12
}
}
}
val validatePhoneNumber = { number: String ->
if (isPhoneNumberPotentiallyValid(number)) {
isError = false
errorMessage = ""
} else {
isError = true
if (!number.startsWith("+")) {
errorMessage = "Phone number must start with '+' (country code)."
} else {
val digits = number.substring(1)
val countryCode = (3 downTo 1).asSequence()
.mapNotNull { len ->
digits.takeIf { it.length >= len }?.substring(0, len)?.toIntOrNull()
}
.firstOrNull { it in countryCallingCodes }
if (countryCode == null) {
errorMessage = "Invalid country code."
} else {
errorMessage = "Please enter a valid number of digits (7-12)."
}
}
}
data class PhoneNumberValidationResult(val isValid: Boolean, val errorMessage: String = "")
fun getPhoneNumberValidationResult(number: String): PhoneNumberValidationResult {
if (!number.startsWith("+")) {
return PhoneNumberValidationResult(
isValid = false,
errorMessage = "Phone number must start with '+' (country code)."
)
}
val digits = number.substring(1)
val countryCode = (3 downTo 1).asSequence()
.mapNotNull { len ->
digits.takeIf { it.length >= len }?.substring(0, len)?.toIntOrNull()
}
.firstOrNull { it in countryCallingCodes }
if (countryCode == null) {
return PhoneNumberValidationResult(
isValid = false,
errorMessage = "Invalid country code."
)
}
val nationalNumber = digits.substring(countryCode.toString().length)
if (nationalNumber.length in 7..12) {
return PhoneNumberValidationResult(isValid = true)
} else {
return PhoneNumberValidationResult(
isValid = false,
errorMessage = "Please enter a valid number of digits (7-12)."
)
}
}
val isPhoneNumberPotentiallyValid = { number: String ->
getPhoneNumberValidationResult(number).isValid
}
val validatePhoneNumber = { number: String ->
val result = getPhoneNumberValidationResult(number)
isError = !result.isValid
errorMessage = result.errorMessage

Copilot uses AI. Check for mistakes.

private fun FirebaseAuth.authStateFlow(): Flow<com.google.firebase.auth.FirebaseUser?> = kotlinx.coroutines.flow.callbackFlow {
val listener = FirebaseAuth.AuthStateListener { auth ->
trySend(auth.currentUser).isSuccess

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

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

The authStateFlow() extension function uses trySend() but doesn't handle the case when the send fails. The result of trySend() returns a ChannelResult, and while you're checking .isSuccess, the boolean result is not used.

Consider logging failed sends or handling them appropriately:

val listener = FirebaseAuth.AuthStateListener { auth ->
    val result = trySend(auth.currentUser)
    if (!result.isSuccess) {
        // Log or handle the failure
    }
}

Copilot uses AI. Check for mistakes.
Comment on lines +156 to +165
private fun startTimer() {
timerJob?.cancel()
timerJob = viewModelScope.launch {
flow {
for (i in 60 downTo 0) {
emit(i)
delay(1000)
}
}.collect {
_timerValue.value = it

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

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

The timer implementation uses a countdown from 60 to 0, but the timeout in PhoneAuthOptions is also set to 60 seconds. There's a potential race condition where the timer might reach 0 slightly before or after the actual Firebase timeout expires, leading to inconsistent user experience.

Consider synchronizing the timer with Firebase's actual timeout callback or adding a small buffer.

Suggested change
private fun startTimer() {
timerJob?.cancel()
timerJob = viewModelScope.launch {
flow {
for (i in 60 downTo 0) {
emit(i)
delay(1000)
}
}.collect {
_timerValue.value = it
private fun startTimer(timeoutSeconds: Int = 60) {
timerJob?.cancel()
val startTime = System.currentTimeMillis()
val endTime = startTime + timeoutSeconds * 1000L
timerJob = viewModelScope.launch {
while (true) {
val currentTime = System.currentTimeMillis()
val remaining = ((endTime - currentTime) / 1000).toInt()
if (remaining >= 0) {
_timerValue.value = remaining
delay(1000)
} else {
_timerValue.value = 0
break
}

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +49
/*Order(
orderId = "12344",
date = "September 24, 10:05",
items = listOf(
OrderItem(name = "Margherita", quantity = 1),
),
status = "Canceled",
totalAmount = 8.99
)*/

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

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

The commented-out code for a canceled order should either be removed or properly documented with a TODO/FIXME comment explaining why it's commented out and when it should be uncommented.

Leaving commented code without explanation makes the codebase harder to maintain.

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +10
data class Order(
val orderId: String,
val date: String,
val items: List<OrderItem>,
val status: String,
val totalAmount: Double
)

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

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

The status field in the Order data class is a plain String, which allows any arbitrary value and makes the code prone to errors. The same issue exists with the date field being a formatted string rather than a structured type.

Consider using more appropriate types:

  • status: Use an enum or sealed class
  • date: Use a proper date/time type (e.g., LocalDateTime or Instant) and format it only for display
Suggested change
data class Order(
val orderId: String,
val date: String,
val items: List<OrderItem>,
val status: String,
val totalAmount: Double
)
import java.time.LocalDateTime
data class Order(
val orderId: String,
val date: LocalDateTime,
val items: List<OrderItem>,
val status: OrderStatus,
val totalAmount: Double
)
enum class OrderStatus {
PENDING,
CONFIRMED,
DELIVERED,
CANCELLED
}

Copilot uses AI. Check for mistakes.
Comment on lines +105 to +122
@Composable
fun OrderStatus(status: String) {
val backgroundColor = when (status) {
"In Progress" -> Warning
"Completed" -> Success
"Canceled" -> Color.Red
else -> Color.Gray
}
Text(
text = status,
color = MaterialTheme.colorScheme.onPrimary,
modifier = Modifier
.clip(RoundedCornerShape(100.dp))
.background(backgroundColor)
.padding(horizontal = 8.dp, vertical = 4.dp),
style = AppTextStyles.Label3Medium
)
}

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

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

The OrderStatus composable uses string matching to determine the background color, which is fragile and not type-safe. If the status string doesn't match exactly (e.g., case sensitivity, typos), it falls back to Color.Gray.

Consider using an enum or sealed class for order status:

sealed class OrderStatus(val displayName: String, val color: Color) {
    object InProgress : OrderStatus("In Progress", Warning)
    object Completed : OrderStatus("Completed", Success)
    object Canceled : OrderStatus("Canceled", Color.Red)
}

Copilot uses AI. Check for mistakes.
verifyPhoneNumber(activity, phoneNumber, token)
} ?: run {
_authState.value =
AuthState.AuthError("Can't resend code, please try again from the beginning.")

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

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

The error message "Can't resend code, please try again from the beginning." could be clearer. Users might not understand what "from the beginning" means in this context.

Consider a more user-friendly message:

"Unable to resend code. Please go back and request a new verification code."
Suggested change
AuthState.AuthError("Can't resend code, please try again from the beginning.")
AuthState.AuthError("Unable to resend code. Please go back and request a new verification code.")

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +87
/* CHANGED: Box no longer forces any size (no fillMaxWidth/height here).
It wraps its content so the Button decides the size. */
Box(
modifier = modifier
.dropShadow(
shape = RoundedCornerShape(100.dp),
shadow = Shadow(
radius = 6.dp,
spread = 0.dp,
offset = DpOffset(0.dp, (4).dp),
alpha = 0.25f,
color = PrimaryGradientEnd
)
)
.clip(RoundedCornerShape(100.dp))
.then(shadowModifier) // shadow outside the pill
.clip(CircleShape)
.background(if (enabled) gradient else SolidColor(TextPrimary8))
.padding(outerPadding)
// NOTE: Do NOT set .fillMaxWidth() or .height(...) on the Box here.
// The Button inside will determine sizing.
) {
Button(
modifier = modifier.background(gradient),
colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent),
onClick = { onClick() },
onClick = onClick,
enabled = enabled,

/* CHANGED: Button modifier uses buttonModifier provided by caller and ensures sensible defaults
with defaultMinSize(minWidth, minHeight). This makes the Button decide size; Box wraps to it. */
modifier = buttonModifier.defaultMinSize(minWidth = minWidth, minHeight = minHeight),

shape = CircleShape,
colors = ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
disabledContainerColor = Color.Transparent
),

/* CHANGED: reasonable horizontal padding so the pill looks correct */
contentPadding = PaddingValues(horizontal = 24.dp, vertical = 0.dp),

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

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

Multiple inline comments starting with "CHANGED:" are present throughout the file (lines 40, 61-62, 69, 76-77, 86-87). These appear to be code review or development notes that should be removed before merging.

Consider removing all these "CHANGED:" comments to maintain clean production code.

Copilot uses AI. Check for mistakes.
Comment on lines +194 to +210
onPhoneNumberEntered = { phoneNumber ->
activity?.let {
viewModel.sendOtp(
phoneNumber,
it
)
}
},
onOtpEntered = { otp -> viewModel.verifyOtp(otp) },
onResendCode = { phoneNumber ->
activity?.let {
viewModel.resendCode(
phoneNumber,
it
)
}
},

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

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

Missing null safety check for activity. While the current code uses activity?.let, if the activity is null, the OTP send/resend operations will silently fail without providing any user feedback.

Consider showing an error message to the user when activity is null:

activity?.let {
    viewModel.sendOtp(phoneNumber, it)
} ?: run {
    // Show error: "Unable to send OTP, please try again"
}

Copilot uses AI. Check for mistakes.
false
} else {
val nationalNumber = digits.substring(countryCode.toString().length)
nationalNumber.length in 7..12

Copilot AI Nov 13, 2025

Copy link

Choose a reason for hiding this comment

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

The phone number length validation accepts 7-12 digits, but this range may not be accurate for all countries. Some countries have phone numbers outside this range (e.g., shorter or longer). This could prevent valid phone numbers from being entered.

Consider researching and using more accurate length ranges per country code, or widening the acceptable range to be more permissive.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants