diff --git a/app/src/main/java/com/example/dattingapp/FriendProfile.kt b/app/src/main/java/com/example/dattingapp/FriendProfile.kt index 3e65795..5d2d709 100644 --- a/app/src/main/java/com/example/dattingapp/FriendProfile.kt +++ b/app/src/main/java/com/example/dattingapp/FriendProfile.kt @@ -1,5 +1,6 @@ package com.example.dattingapp +import android.content.Context import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -17,46 +18,107 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.ChatBubble +import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.MoreHoriz +import androidx.compose.material.icons.filled.Star import androidx.compose.material.icons.outlined.LocationOn +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel +import coil.compose.AsyncImage +import com.example.dattingapp.homes.networkandapi.FriendProfileMedia +import com.example.dattingapp.homes.networkandapi.FriendProfileResponse +import com.example.dattingapp.homes.networkandapi.FriendProfileUiState +import com.example.dattingapp.homes.networkandapi.FriendProfileViewModel +import com.example.dattingapp.homes.networkandapi.MeetUserProfile +import com.example.dattingapp.homes.networkandapi.SwipeRequest +import com.example.dattingapp.homes.networkandapi.SwipeViewModel import com.example.dattingapp.ui.theme.AccentPurple import com.example.dattingapp.ui.theme.DattingAPPTheme import com.example.dattingapp.ui.theme.textDark import com.example.dattingapp.ui.theme.BadgePink +import com.example.dattingapp.ui.theme.gradientBrush +import com.example.dattingapp.ui.theme.purpleButtonColor import java.nio.file.WatchEvent +import java.time.LocalDate +import java.time.Period +import java.time.format.DateTimeFormatter + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Text +import androidx.compose.ui.unit.dp class FriendProfile : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() + + // Read extras passed from the Intent + val userId = intent.getIntExtra("USER_ID", -1) + setContent { DattingAPPTheme { + val context = LocalContext.current + val viewModel: FriendProfileViewModel = viewModel() + + // 2. Collect state from ViewModel + val uiState by viewModel.friendProfileUiState.collectAsState() + + // 3. Trigger network call once when screen loads + LaunchedEffect(Unit) { + viewModel.fetchUserProfile(context,userId) + } + when (val state = uiState) { + is FriendProfileUiState.Loading -> { + // Show loading indicator + } + is FriendProfileUiState.Success -> { + val friendProfileData = state.friendProfile + FriendProfileScreen(friendProfileData,context) - FriendProfileScreen() + } + is FriendProfileUiState.Error -> { + // Show error message + } + FriendProfileUiState.Idle -> { + // Idle state + } + } } } @@ -65,13 +127,24 @@ class FriendProfile : ComponentActivity() { @Composable fun FriendProfileScreen( + friendProfileData: FriendProfileResponse, + context: Context, + swipeViewModel: SwipeViewModel= viewModel() , onBackClick:()->Unit={}, onMoreClick:()->Unit={}, onMessageClick:()->Unit={}, onLikeClick:()->Unit={}, onStarClick:()->Unit={}, + onCancelClick:()->Unit={}, ){ + + val age = remember(friendProfileData.birthDate) { + calculateAge(friendProfileData.birthDate) + } + + + Column( modifier = Modifier .fillMaxSize() @@ -119,11 +192,14 @@ fun FriendProfileScreen( } } - Image( - painter = painterResource(id = R.drawable.locationpermissionbg), + // Assuming 'profileImageUrl' is passed into your composable from FriendProfileResponse + AsyncImage( + model = friendProfileData?.profileImageUrl, // Pass the String URL from FriendProfileResponse contentDescription = "Profile Avatar", + placeholder = painterResource(id = R.drawable.locationpermissionbg), // Placeholder while loading + error = painterResource(id = R.drawable.locationpermissionbg), // Fallback if URL fails or is null modifier = Modifier - .padding(start = 20.dp) // Move padding to the VERY TOP of modifier chain + .padding(start = 20.dp) .align(Alignment.BottomStart) .size(100.dp) .clip(CircleShape) @@ -139,12 +215,16 @@ fun FriendProfileScreen( .padding(horizontal = 20.dp) ){ + Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { + + val displayName = friendProfileData.fullName ?: "User" + val nameAndAge = if (age != null) "$displayName, $age" else displayName Text( - text = "Elena", + text = nameAndAge, fontSize = 24.sp, fontWeight = FontWeight.Bold, color = textDark @@ -213,6 +293,8 @@ fun FriendProfileScreen( color = Color.Gray ) } + + Surface( onClick = { // Handle bio click action here (e.g., expand bio or navigate) @@ -222,25 +304,207 @@ fun FriendProfileScreen( color = Color(0xFFF9F9F9), shadowElevation = 1.dp ) { + val displayBio= friendProfileData.bio ?: "" Text( - text = "\"Always looking for the best flat white in the city. Amateur photographer, professional dog petter. Let...\"", + text = displayBio, fontSize = 13.sp, color = Color(0xFF4A4A4A), modifier = Modifier.padding(12.dp), + maxLines = 2, lineHeight = 18.sp ) } + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + horizontalArrangement = Arrangement.spacedBy(10.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // Message Button (Gradient Background) + Button( + onClick = onMessageClick, + modifier = Modifier + .weight(1f) + .height(48.dp) + .background( + brush = gradientBrush, + shape = RoundedCornerShape(10.dp) + ), + colors = ButtonDefaults.buttonColors( + containerColor = Color.Transparent, + contentColor = Color.White + ), + shape = RoundedCornerShape(10.dp) + ) { + Icon( + imageVector = Icons.Default.ChatBubble, + contentDescription = "Message", + modifier = Modifier.size(18.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = "Message", + fontSize = 16.sp, + fontWeight = FontWeight.Bold + ) + } + + // Heart Button + IconButton( + onClick = { + // Option 1: Invoke callback directly if handleSwipeAction is inside the parent screen + onLikeClick() + + // Option 2: Call handleSwipeAction directly here + handleLikeAndDislikeAction( + context = context, + direction = "right", // Or "like", matching your API endpoint expected value + profile = friendProfileData, // Your MeetUserProfile instance + swipeViewModel = swipeViewModel + ) + }, + modifier = Modifier + .size(48.dp) + .background( + color = purpleButtonColor, + shape = RoundedCornerShape(10.dp) + ) + ) { + Icon( + imageVector = Icons.Default.Favorite, + contentDescription = "Like", + tint = Color(0xFFE2C4FF), // Soft light purple icon color + modifier = Modifier.size(20.dp) + ) + } + + // Star Button + IconButton( + onClick = { + onStarClick() + handleLikeAndDislikeAction( + context = context, + direction = "right", // Or "like", matching your API endpoint expected value + profile = friendProfileData, // Your MeetUserProfile instance + swipeViewModel = swipeViewModel + ) + }, + modifier = Modifier + .size(48.dp) + .background( + color = purpleButtonColor, + shape = RoundedCornerShape(10.dp) + ) + ) { + Icon( + imageVector = Icons.Default.Star, + contentDescription = "Favorite", + tint = Color(0xFFE2C4FF), // Soft light purple icon color + modifier = Modifier.size(20.dp) + ) + } + IconButton( + onClick = { + onStarClick() + handleLikeAndDislikeAction( + context = context, + direction = "right", // Or "like", matching your API endpoint expected value + profile = friendProfileData, // Your MeetUserProfile instance + swipeViewModel = swipeViewModel + ) + }, + modifier = Modifier + .size(48.dp) + .background( + color = purpleButtonColor, + shape = RoundedCornerShape(10.dp) + ) + ) { + Icon( + imageVector = Icons.Default.Close, + contentDescription = "Favorite", + tint = Color(0xFFE2C4FF), // Soft light purple icon color + modifier = Modifier.size(20.dp) + ) + } + + } } +// +// LazyVerticalGrid( +// columns = GridCells.Fixed(3), +// contentPadding = PaddingValues(12.dp), +// horizontalArrangement = Arrangement.spacedBy(8.dp), +// verticalArrangement = Arrangement.spacedBy(8.dp), +// modifier = Modifier.fillMaxSize() +// ) { +// items( +// items = uiState.items, +// key = { post -> post.id } +// ) { item -> +// PostGridItem(item = item) +// } +// } } } +private fun handleLikeAndDislikeAction( + context: Context, + direction:String, + profile: FriendProfileResponse, + swipeViewModel: SwipeViewModel +) { + val targetUserId = profile.id + val swipeRequest = SwipeRequest(targetUserId, direction) + swipeViewModel.sendSwipeResponse(context, swipeRequest) +} + + +//calculte age +private fun calculateAge(birthDateStr: String?): Int? { + if (birthDateStr.isNullOrBlank()) return null + return try { + val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") + val birthDate = LocalDate.parse(birthDateStr.take(10), formatter) + Period.between(birthDate, LocalDate.now()).years + } catch (e: Exception) { + null + } +} + + @Preview(showBackground = true, showSystemUi = true) @Composable fun FriendProfileScreenPreview() { DattingAPPTheme { - FriendProfileScreen() + val mockProfileResponse = FriendProfileResponse( + id = 101, + fullName = "Elena Rostova", + gender = "Female", + birthDate = "1999-05-14", + city = "San Francisco", + country = "USA", + relationshipGoal = "Long-term relationship", + bio = "Always looking for the best flat white in the city. Amateur photographer, professional dog petter.", + profileImageUrl = "https://via.placeholder.com/150", + videoUrl = null, + compatibilityPercentage = 98, + media = null + ) + + FriendProfileScreen( + friendProfileData = mockProfileResponse, + context = LocalContext.current, + onBackClick = {}, + onMoreClick = {}, + onMessageClick = {}, + onLikeClick = {}, + onStarClick = {} + ) } -} \ No newline at end of file +} + diff --git a/app/src/main/java/com/example/dattingapp/homes/MeetScreen.kt b/app/src/main/java/com/example/dattingapp/homes/MeetScreen.kt index 9ae11f5..83c4c21 100644 --- a/app/src/main/java/com/example/dattingapp/homes/MeetScreen.kt +++ b/app/src/main/java/com/example/dattingapp/homes/MeetScreen.kt @@ -1,9 +1,11 @@ package com.example.dattingapp.homes import android.content.Context +import android.content.Intent import android.util.Log import androidx.compose.animation.core.Animatable import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape @@ -20,6 +22,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight @@ -29,6 +32,7 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import coil.compose.AsyncImage import coil.request.ImageRequest +import com.example.dattingapp.FriendProfile import com.example.dattingapp.homes.networkandapi.MeetUserProfile import com.example.dattingapp.homes.networkandapi.SwipeRequest import com.example.dattingapp.homes.networkandapi.SwipeViewModel @@ -199,10 +203,11 @@ private fun ProfileCardItem( val token = remember(context) { TokenManager.getInstance(context).getAccessToken() } + val defaultPainter = rememberVectorPainter(Icons.Default.Person) AsyncImage( model = ImageRequest.Builder(context) - .data(photoUrl) + .data(photoUrl.takeIf { !it.isNullOrBlank() }) .apply { if (!token.isNullOrBlank()) { val authHeader = if (token.startsWith("Bearer ", ignoreCase = true)) token else "Bearer $token" @@ -211,6 +216,9 @@ private fun ProfileCardItem( } .crossfade(true) .build(), + placeholder = defaultPainter, + error = defaultPainter, + fallback = defaultPainter, // Shows Icons.Default.Person immediately when photoUrl is null/blank contentDescription = "Profile Image", modifier = Modifier.fillMaxSize(), contentScale = ContentScale.Crop, @@ -299,6 +307,16 @@ private fun ProfileCardItem( .fillMaxWidth(0.70f) ) { Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + val intent = Intent(context, FriendProfile::class.java).apply { + putExtra("USER_ID",profile.id ) + + } + context.startActivity(intent) + }, + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { @@ -397,3 +415,54 @@ private fun handleSwipeAction( swipeViewModel.sendSwipeResponse(context, swipeRequest) } + + +// ========================================== +// PREVIEWS & MOCK DATA +// ========================================== + +private val mockProfiles = listOf( + MeetUserProfile( + id = 1, + fullName = "Sophia Chen", + birthDate = "1998-06-15", + city = "San Francisco", + country = "USA", + distanceKm = 4.2, + bio = "Coffee enthusiast, UI designer, and full-time dog lover 🐶✨ Looking for someone to explore new coffee shops with!", + compatibilityPercentage = 92, + media = null, // Triggers fallback default Person icon + gender = "Male", + videoUrl = null + ), + MeetUserProfile( + id = 2, + fullName = "Alex Rivera", + birthDate = "1995-11-03", + city = "Los Angeles", + country = "USA", + distanceKm = 12.0, + bio = "Software engineer by day, musician by night 🎸 Let's jam or talk about tech!", + compatibilityPercentage = 85, + media = null, + gender = "Male", + videoUrl = null + ) +) + +@Preview(name = "Discovery Screen - Loaded State", showBackground = true) +@Composable +fun MeetDiscoveryScreenPreview() { + MaterialTheme { + MeetDiscoveryScreen( + profiles = mockProfiles, + isLoading = false, + errorMessage = null, + onAddClick = {}, + onSearchClick = {}, + onSuperLikeClick = {}, + onRejectClick = {}, + onLikeClick = {} + ) + } +} diff --git a/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeApiService.kt b/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeApiService.kt index fa16577..e8bdbb5 100644 --- a/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeApiService.kt +++ b/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeApiService.kt @@ -4,6 +4,7 @@ import retrofit2.Response import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST +import retrofit2.http.Path import retrofit2.http.Query interface HomeApiService { @@ -17,8 +18,14 @@ interface HomeApiService { suspend fun getCurrentUserProfile(): Response @POST("api/v1/meet/swipes") - suspend fun recordSwipe( + suspend fun sendSwipe( @Body request: SwipeRequest ): Response + @GET("api/v1/users/{user_id}") + suspend fun getFriendProfile( + @Path("user_id") userId: Int + ): Response + + } \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeDataClass.kt b/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeDataClass.kt index c09b7a4..4b66daf 100644 --- a/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeDataClass.kt +++ b/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeDataClass.kt @@ -92,27 +92,6 @@ data class MediaItem( get() = resolveMediaUrl(url) } - - -//swife data - -// Request payload -data class SwipeRequest( - @SerializedName("target_user_id") - val targetUserId: Int, - @SerializedName("direction") - val direction: String // "like", "dislike", "super_like" -) - -data class SwipeResponse( - @SerializedName("direction") - val direction: String?, - @SerializedName("matched") - val matched: Boolean = false, - @SerializedName("match_id") - val matchId: Int? = null -) - //feeling data for create post data class VibeFeeling( val name: String, @@ -144,4 +123,57 @@ val vibeFeelingsList = listOf( VibeFeeling("Working Out", "🏋️‍♂️", "Activities"), VibeFeeling("Coding", "💻", "Activities"), VibeFeeling("Gaming", "🎮", "Activities") +) + + +// Request Model for Swipe +data class SwipeRequest( + @SerializedName("target_user_id") + val targetUserId: Int, + + @SerializedName("direction") + val direction: String +) + +// Response Model +data class SwipeResponse( + @SerializedName("direction") + val direction: String?, + + @SerializedName("matched") + val matched: Boolean?, + + @SerializedName("match_id") + val matchId: Long? +) + + + +// data class friend profile + +data class FriendProfileMedia( + val id: Int, + @SerializedName("media_type") + val mediaType: String?, + val url: String? +) +data class FriendProfileResponse( + val id: Int, + @SerializedName("full_name") + val fullName: String?, + val gender: String?, + @SerializedName("birth_date") + val birthDate: String?, + val city: String?, + val country: String?, + @SerializedName("relationship_goal") + val relationshipGoal: String?, + val bio: String?, + @SerializedName("profile_image_url") + val profileImageUrl: String?, + @SerializedName("video_url") + val videoUrl: String?, + @SerializedName("compatibility_percentage") + val compatibilityPercentage: Int?, + val media: List? ) \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeViewModelClass.kt b/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeViewModelClass.kt index 0278e39..5ff3dde 100644 --- a/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeViewModelClass.kt +++ b/app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeViewModelClass.kt @@ -159,7 +159,7 @@ class MyProfileViewModel : ViewModel() { -// Swife ViewModel--------------------------------------------------------------------------------------- +//for ViewModel swife -------------------------------------------------------------------------------------- sealed interface SwipeUiState { object Idle : SwipeUiState @@ -189,7 +189,7 @@ class SwipeViewModel : ViewModel() { try { val response = HomeNetworkModule .getApiService(appContext) - .recordSwipe(swipeRequest) + .sendSwipe(swipeRequest) val statusCode = response.code() Log.d("swipe code", "Response Code: $statusCode") @@ -224,3 +224,68 @@ class SwipeViewModel : ViewModel() { } } + +//for friend profile -------------------------------------------------------------------------------------- + +sealed interface FriendProfileUiState { + object Idle : FriendProfileUiState + object Loading : FriendProfileUiState + data class Success(val friendProfile: FriendProfileResponse) : FriendProfileUiState + data class Error(val message: String) : FriendProfileUiState +} + +class FriendProfileViewModel : ViewModel() { + + private val _friendProfileUiState = MutableStateFlow(FriendProfileUiState.Idle) + val friendProfileUiState: StateFlow = _friendProfileUiState.asStateFlow() + + fun fetchUserProfile(context: Context, userId: Int) { + if (_friendProfileUiState.value is FriendProfileUiState.Loading) return + + val appContext = context.applicationContext + + viewModelScope.launch { + _friendProfileUiState.value = FriendProfileUiState.Loading + + if (TokenManager.getInstance(appContext).getAccessToken().isNullOrBlank()) { + Log.e("MeetApi", "No access token in user_tokens preferences") + _friendProfileUiState.value = FriendProfileUiState.Error("No access token found. Please log in again.") + return@launch + } + + try { + val response = HomeNetworkModule + .getApiService(appContext) + .getFriendProfile(userId) + + val statusCode = response.code() + Log.d("userProfile code", "Response Code: $statusCode") + + if (!response.isSuccessful) { + response.errorBody()?.close() + val message = when (response.code()) { + 401 -> "Authentication failed. Please log in again." + 403 -> "The server refused access to this profile." + 404 -> "User profile was not found." + else -> "Request failed: HTTP ${response.code()}." + } + _friendProfileUiState.value = FriendProfileUiState.Error(message) + return@launch + } + + val profileData = response.body() + + if (profileData != null) { + _friendProfileUiState.value = FriendProfileUiState.Success(profileData) + } else { + _friendProfileUiState.value = FriendProfileUiState.Error("Profile data is empty.") + } + + } catch (e: Exception) { + Log.e("MeetApi", "Unexpected request failure", e) + _friendProfileUiState.value = FriendProfileUiState.Error("Request failed: ${e.javaClass.simpleName}") + } + } + } +} + diff --git a/app/src/main/java/com/example/dattingapp/permission/PermissionHelper.kt b/app/src/main/java/com/example/dattingapp/permission/PermissionHelper.kt index 0e50ebc..3b05e2f 100644 --- a/app/src/main/java/com/example/dattingapp/permission/PermissionHelper.kt +++ b/app/src/main/java/com/example/dattingapp/permission/PermissionHelper.kt @@ -28,7 +28,7 @@ class PermissionHelper(private val activity: ComponentActivity) { permissionLauncher = activity.registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { permissions -> - // Check if all permissions requested are granted + // Check if at least one requested location permission (or all requested) is granted val allGranted = permissions.entries.all { it.value } onResult?.invoke(allGranted) } @@ -57,6 +57,23 @@ class PermissionHelper(private val activity: ComponentActivity) { } } + /** + * Helper to check location permission specifically (either FINE or COARSE) + */ + fun hasLocationPermission(): Boolean { + val fineGranted = ContextCompat.checkSelfPermission( + activity, + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + + val coarseGranted = ContextCompat.checkSelfPermission( + activity, + Manifest.permission.ACCESS_COARSE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + + return fineGranted || coarseGranted + } + /** * Request standard runtime permissions: NOTIFICATION, GALLERY, AUDIO, or LOCATION */ @@ -71,6 +88,17 @@ class PermissionHelper(private val activity: ComponentActivity) { } } + /** + * Direct method to request Location Permission + */ + fun requestLocationPermission(callback: (Boolean) -> Unit) { + if (hasLocationPermission()) { + callback(true) + } else { + requestPermissions(PermissionType.LOCATION, callback) + } + } + /** * Request Special Permission: Draw Over Other Apps */ diff --git a/app/src/main/java/com/example/dattingapp/registration/RegisterRequest.kt b/app/src/main/java/com/example/dattingapp/registration/RegisterRequest.kt index d9b14fd..703499e 100644 --- a/app/src/main/java/com/example/dattingapp/registration/RegisterRequest.kt +++ b/app/src/main/java/com/example/dattingapp/registration/RegisterRequest.kt @@ -7,6 +7,7 @@ data class RegisterRequest( @SerializedName("email_or_phone") val emailOrPhone: String, @SerializedName("password") val password: String, @SerializedName("confirm_password") val confirmPassword: String, + @SerializedName("relationship_goal") val relationshipGoal: String, @SerializedName("full_name") val fullName: String, @SerializedName("gender") val gender: String, @SerializedName("birth_date") val birthDate: String, diff --git a/app/src/main/java/com/example/dattingapp/registration/RegistrationScreen.kt b/app/src/main/java/com/example/dattingapp/registration/RegistrationScreen.kt index 0d49743..3e1bb06 100644 --- a/app/src/main/java/com/example/dattingapp/registration/RegistrationScreen.kt +++ b/app/src/main/java/com/example/dattingapp/registration/RegistrationScreen.kt @@ -122,7 +122,7 @@ fun RegistrationFlowContainer( var selectedBirthDateMillis by rememberSaveable { mutableStateOf(null) } var addressInput by rememberSaveable { mutableStateOf("") } var selectedGender by rememberSaveable { mutableStateOf("") } - var selectedRealationStatus by remember { mutableStateOf(null) } + var selectedRealationStatus by remember { mutableStateOf("") } val scrollState = rememberScrollState() @@ -404,19 +404,20 @@ fun RegistrationFlowContainer( val contact = if (selectedTab == 0) emailInput.trim() else "$countryCode${phoneInput.trim()}" - val payload = RegisterRequest( - emailOrPhone = contact, - password = passwordInput, - confirmPassword = confirmPasswordInput, - fullName = nameInput.trim(), - gender = selectedGender.lowercase(Locale.ROOT), - birthDate = formattedDate, - city = addressInput.trim(), - bio = null, - videoIntro = null, - profileImage = null, - interests = null - ) + val payload = RegisterRequest( + emailOrPhone = contact, + password = passwordInput, + confirmPassword = confirmPasswordInput, + relationshipGoal = selectedRealationStatus.lowercase(Locale.ROOT).replace(" ", "_"), + fullName = nameInput.trim(), + gender = selectedGender.lowercase(Locale.ROOT), + birthDate = formattedDate, + city = addressInput.trim(), + bio = null, + videoIntro = null, + profileImage = null, + interests = null + ) registrationViewModel.registerUser(context, payload) } @@ -1496,6 +1497,7 @@ fun UniquePulseLoader( data class VibeOption( val title: String, + val value: String, // Value sent to backend API val description: String, val icon: ImageVector, val iconBgColor: Color, @@ -1511,6 +1513,7 @@ fun VibeCheckScreen( listOf( VibeOption( title = "Serious Relationship", + value = "serious_relationship", description = "Looking for the one", icon = Icons.Default.FavoriteBorder, iconBgColor = Color(0xFFFFE3E3), @@ -1518,6 +1521,7 @@ fun VibeCheckScreen( ), VibeOption( title = "Casual Dates", + value = "casual_dates", description = "Drinks & good times", icon = Icons.Default.LocalBar, iconBgColor = Color(0xFFEDE7F6), @@ -1525,6 +1529,7 @@ fun VibeCheckScreen( ), VibeOption( title = "New Friends", + value = "new_friends", description = "Expand my circle", icon = Icons.Default.People, iconBgColor = Color(0xFFFFE0B2), @@ -1532,6 +1537,7 @@ fun VibeCheckScreen( ), VibeOption( title = "Marriage", + value = "marriage", description = "Seeing where things go", icon = Icons.Default.CompassCalibration, iconBgColor = Color(0xFFE0E0E0), @@ -1540,72 +1546,26 @@ fun VibeCheckScreen( ) } - val backgroundBrush = Brush.verticalGradient( - colors = listOf( - Color(0xFFFFF0F3), - Color(0xFFFFFFFF), - Color(0xFFFFF0F5) - ) - ) + // ... rest of background and header layout ... - Box( - modifier = Modifier - .fillMaxSize() - .background(backgroundBrush) - .padding(horizontal = 24.dp, vertical = 32.dp) + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.fillMaxWidth() ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth() - ) { - Text( - text = "VibeCheck", - fontSize = 24.sp, - fontWeight = FontWeight.Bold, - color = Color(0xFFB0003A) - ) - - Spacer(modifier = Modifier.height(32.dp)) - - Text( - text = "What's your vibe?", - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - color = Color(0xFF1E293B) - ) - - Spacer(modifier = Modifier.height(8.dp)) - - Text( - text = "Select your main goal right now to help us find theright connections.", - fontSize = 13.sp, - color = Color(0xFF64748B), - textAlign = TextAlign.Center, - lineHeight = 18.sp - ) - - Spacer(modifier = Modifier.height(48.dp)) - - Column( - verticalArrangement = Arrangement.spacedBy(16.dp), - modifier = Modifier.fillMaxWidth() - ) { - vibeOptions.forEach { option -> - val isSelected = option.title == selectedRealationStatus - - VibeCard( - option = option, - isSelected = isSelected, - onClick = { - onRelationSelected(option.title) - } - ) + vibeOptions.forEach { option -> + // Check selection against option.value + val isSelected = option.value == selectedRealationStatus + VibeCard( + option = option, + isSelected = isSelected, + onClick = { + // Send backend value instead of UI title + onRelationSelected(option.value) } - } + ) } } } - @Composable fun VibeCard( option: VibeOption, diff --git a/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt b/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt index 6c0d11d..f21aae6 100644 --- a/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt +++ b/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt @@ -110,4 +110,12 @@ val ScreenBackground = Color(0xFFFAF9FD) val SectionHeaderColor = Color(0xFF757575) val CardBackgroundColor = Color.White val LinkTextColor = Color(0xFF7B1FA2) -val UnreadDotColor = Color(0xFFD32F2F) \ No newline at end of file +val UnreadDotColor = Color(0xFFD32F2F) + +val gradientBrush = Brush.horizontalGradient( +colors = listOf( + Color(0xFFB00040), // Dark Pink/Magenta + Color(0xFF6B00B0) // Deep Purple + ) +) +val purpleButtonColor = Color(0xFF6B00F5) \ No newline at end of file