Browse Source

friend profile update

master
osman160128 2 weeks ago
parent
commit
fbd1f81c9a
  1. 119
      app/src/main/java/com/example/dattingapp/FriendProfile.kt
  2. 29
      app/src/main/java/com/example/dattingapp/LikeANdMatchActivity.kt
  3. 5
      app/src/main/java/com/example/dattingapp/Login/LoginActivity.kt
  4. 59
      app/src/main/java/com/example/dattingapp/ShowPostingActivity.kt
  5. 4
      app/src/main/java/com/example/dattingapp/dataclass/DataClass.kt
  6. 3
      app/src/main/java/com/example/dattingapp/homes/ExploreScreen.kt
  7. 18
      app/src/main/java/com/example/dattingapp/homes/MeetScreen.kt
  8. 28
      app/src/main/java/com/example/dattingapp/homes/MyProfile.kt
  9. 11
      app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeApiService.kt
  10. 48
      app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeViewModelClass.kt
  11. 4
      app/src/main/java/com/example/dattingapp/homes/networkandapi/TokenManager.kt
  12. 4
      app/src/main/java/com/example/dattingapp/registration/RegisterRequest.kt
  13. 2
      app/src/main/java/com/example/dattingapp/ui/theme/Color.kt

119
app/src/main/java/com/example/dattingapp/FriendProfile.kt

@ -51,6 +51,7 @@ import com.example.dattingapp.homes.profile.FrienPostMedia
import com.example.dattingapp.homes.profile.FriendAuthor import com.example.dattingapp.homes.profile.FriendAuthor
import com.example.dattingapp.homes.profile.FriendPost import com.example.dattingapp.homes.profile.FriendPost
import com.example.dattingapp.homes.profile.FriendProfileResponse import com.example.dattingapp.homes.profile.FriendProfileResponse
import com.example.dattingapp.homes.profile.LikedUserResponse
import com.example.dattingapp.ui.theme.* import com.example.dattingapp.ui.theme.*
import java.time.LocalDate import java.time.LocalDate
import java.time.Period import java.time.Period
@ -82,6 +83,9 @@ class FriendProfile : ComponentActivity() {
} }
} }
when (val state = profileUiState) { when (val state = profileUiState) {
is FriendProfileUiState.Loading -> { is FriendProfileUiState.Loading -> {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
@ -120,6 +124,36 @@ fun FriendProfileScreen(
onStarClick: () -> Unit = {}, onStarClick: () -> Unit = {},
onCancelClick: () -> Unit = {} onCancelClick: () -> Unit = {}
) { ) {
val likedViewModel: LikedViewModel = viewModel()
// ✅ collect each state separately
val likesState by likedViewModel.likesUiState.collectAsState() // who liked me
val likedState by likedViewModel.likedUiState.collectAsState() // I liked
val matchesState by likedViewModel.matchesUiState.collectAsState() // matches
// ✅ load all three lists once
LaunchedEffect(Unit) {
likedViewModel.likedResponse(context, "Likes")
likedViewModel.likedResponse(context, "Liked")
likedViewModel.likedResponse(context, "Matches")
}
// ✅ turn each into a set of ids
val likedMeIds = (likesState as? LikedUiState.Success)
?.LikeData?.map { it.id }?.toSet() ?: emptySet()
val iLikedIds = (likedState as? LikedUiState.Success)
?.LikeData?.map { it.id }?.toSet() ?: emptySet()
val matchIds = (matchesState as? LikedUiState.Success)
?.LikeData?.map { it.id }?.toSet() ?: emptySet()
// ✅ the three booleans for the button row
val isMatch = friendProfileData.id in matchIds
val heLikedMe = friendProfileData.id in likedMeIds
val iLikedHim = friendProfileData.id in iLikedIds
val age = remember(friendProfileData.birth_date) { val age = remember(friendProfileData.birth_date) {
calculateAge(friendProfileData.birth_date) calculateAge(friendProfileData.birth_date)
} }
@ -269,6 +303,7 @@ fun FriendProfileScreen(
) )
} }
// Action Buttons Row // Action Buttons Row
Row( Row(
modifier = Modifier modifier = Modifier
@ -277,85 +312,109 @@ fun FriendProfileScreen(
horizontalArrangement = Arrangement.spacedBy(10.dp), horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Button( // Message — inside a card
if (isMatch) {
Card(
onClick = onMessageClick, onClick = onMessageClick,
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.height(48.dp) .height(48.dp),
.background( shape = RoundedCornerShape(10.dp),
brush = gradientBrush, colors = CardDefaults.cardColors(containerColor = Color.White),
shape = RoundedCornerShape(10.dp) elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
), ) {
colors = ButtonDefaults.buttonColors( Row(
containerColor = Color.Transparent, modifier = Modifier.fillMaxSize(),
contentColor = Color.White horizontalArrangement = Arrangement.Center,
), verticalAlignment = Alignment.CenterVertically
shape = RoundedCornerShape(10.dp)
) { ) {
Icon( Icon(
imageVector = Icons.Default.ChatBubble, imageVector = Icons.Default.ChatBubble,
contentDescription = "Message", contentDescription = "Message",
tint = textDark,
modifier = Modifier.size(18.dp) modifier = Modifier.size(18.dp)
) )
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
Text( Text(
text = "Message", text = "Message",
fontSize = 16.sp, fontSize = 16.sp,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold,
color = textDark
) )
} }
}
}
IconButton( // ❤️ Like — red ICON when the user liked ME, hidden when matched
if (!isMatch) {
Card(
onClick = { onClick = {
onLikeClick() onLikeClick()
handleLikeAndDislikeAction(context, "right", friendProfileData, swipeViewModel) handleLikeAndDislikeAction(context, "right", friendProfileData, swipeViewModel)
}, },
modifier = Modifier modifier = Modifier.size(48.dp),
.size(48.dp) shape = RoundedCornerShape(10.dp),
.background(color = purpleButtonColor, shape = RoundedCornerShape(10.dp)) colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) { ) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Icon( Icon(
imageVector = Icons.Default.Favorite, imageVector = Icons.Default.Favorite,
contentDescription = "Like", contentDescription = "Like",
tint = Color(0xFFE2C4FF), tint = when {
modifier = Modifier.size(20.dp) heLikedMe -> Color(0xFFE53935) // 🔴 user liked me
iLikedHim -> Color(0xFF1E88E5) // 🔵 I liked the user
else -> textDark // ⚫ no relation yet
},
modifier = Modifier.size(22.dp)
) )
} }
}
IconButton( // Super Like — inside a card
Card(
onClick = { onClick = {
onStarClick() onStarClick()
handleLikeAndDislikeAction(context, "right", friendProfileData, swipeViewModel) handleLikeAndDislikeAction(context, "right", friendProfileData, swipeViewModel)
}, },
modifier = Modifier modifier = Modifier.size(48.dp),
.size(48.dp) shape = RoundedCornerShape(10.dp),
.background(color = purpleButtonColor, shape = RoundedCornerShape(10.dp)) colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) { ) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Icon( Icon(
imageVector = Icons.Default.Star, imageVector = Icons.Default.Star,
contentDescription = "Favorite", contentDescription = "Favorite",
tint = Color(0xFFE2C4FF), tint = textDark,
modifier = Modifier.size(20.dp) modifier = Modifier.size(22.dp)
) )
} }
}
}
IconButton( // Cancel — inside a card, always visible
Card(
onClick = { onClick = {
onCancelClick() onCancelClick()
handleLikeAndDislikeAction(context, "left", friendProfileData, swipeViewModel) handleLikeAndDislikeAction(context, "left", friendProfileData, swipeViewModel)
}, },
modifier = Modifier modifier = Modifier.size(48.dp),
.size(48.dp) shape = RoundedCornerShape(10.dp),
.background(color = purpleButtonColor, shape = RoundedCornerShape(10.dp)) colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) { ) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Icon( Icon(
imageVector = Icons.Default.Close, imageVector = Icons.Default.Close,
contentDescription = "Dislike", contentDescription = "Dislike",
tint = Color(0xFFE2C4FF), tint = textDark,
modifier = Modifier.size(20.dp) modifier = Modifier.size(22.dp)
) )
} }
} }
}
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))

29
app/src/main/java/com/example/dattingapp/LikeANdMatchActivity.kt

@ -54,13 +54,14 @@ class LikeANdMatchActivity : ComponentActivity() {
DattingAPPTheme { DattingAPPTheme {
val likedViewModel: LikedViewModel = viewModel() val likedViewModel: LikedViewModel = viewModel()
val context = LocalContext.current val context = LocalContext.current
val type = intent.getStringExtra("TYPE") ?: "liked"
// Collect state from ViewModel // Collect state from ViewModel
val uiState by likedViewModel.likeUiState.collectAsState() val uiState by likedViewModel.likesUiState.collectAsState()
// Trigger network call once when screen loads // Trigger network call once when screen loads
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
likedViewModel.likedResponse(context) likedViewModel.likedResponse(context,type)
} }
Scaffold( Scaffold(
@ -95,7 +96,7 @@ class LikeANdMatchActivity : ComponentActivity() {
color = MaterialTheme.colorScheme.error, color = MaterialTheme.colorScheme.error,
fontWeight = FontWeight.Medium fontWeight = FontWeight.Medium
) )
Button(onClick = { likedViewModel.likedResponse(context) }) { Button(onClick = { likedViewModel.likedResponse(context,"like") }) {
Text("Retry") Text("Retry")
} }
} }
@ -120,7 +121,6 @@ fun ProfileCard(context: Context, likedProfile: LikedUserResponse, modifier: Mod
modifier = modifier.fillMaxWidth() modifier = modifier.fillMaxWidth()
) { ) {
Column( Column(
modifier = Modifier.padding(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
Box( Box(
@ -128,6 +128,14 @@ fun ProfileCard(context: Context, likedProfile: LikedUserResponse, modifier: Mod
.fillMaxWidth() .fillMaxWidth()
.height(180.dp) .height(180.dp)
.clip(RoundedCornerShape(12.dp)) .clip(RoundedCornerShape(12.dp))
.clickable {
// Create an intent to launch your target activity (e.g., FriendProfileActivity)
val intent = Intent(context, FriendProfile::class.java).apply {
putExtra("USER_ID", likedProfile.id) // Pass the user ID here
}
context.startActivity(intent)
onNameClick() }
) { ) {
// Use profile_image_url or fallback to the first media item // Use profile_image_url or fallback to the first media item
AsyncImage( AsyncImage(
@ -182,17 +190,10 @@ fun ProfileCard(context: Context, likedProfile: LikedUserResponse, modifier: Mod
Column( Column(
modifier = Modifier modifier = Modifier
.align(Alignment.BottomStart) .align(Alignment.BottomStart)
.clickable { // Makes just this text section clickable
// Create an intent to launch your target activity (e.g., FriendProfileActivity)
val intent = Intent(context, FriendProfile::class.java).apply {
putExtra("USER_ID", likedProfile.id) // Pass the user ID here
}
context.startActivity(intent)
onNameClick() } // Makes just this text section clickable
.padding(8.dp) .padding(8.dp)
){ ){
val displayName = likedProfile.full_name.ifBlank { "User" } val displayName = likedProfile.full_name?.ifBlank { "User" }
val age = remember(likedProfile.birth_date) { val age = remember(likedProfile.birth_date) {
calculateAge(likedProfile.birth_date) calculateAge(likedProfile.birth_date)
} }
@ -201,7 +202,7 @@ fun ProfileCard(context: Context, likedProfile: LikedUserResponse, modifier: Mod
Log.d("user","$likedProfile.id") Log.d("user","$likedProfile.id")
Text( Text(
text = nameAndAge, text = nameAndAge?:"",
color = Color.White, color = Color.White,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
fontSize = 14.sp fontSize = 14.sp

5
app/src/main/java/com/example/dattingapp/Login/LoginActivity.kt

@ -270,13 +270,14 @@ fun LoginScreenContent(
// Save retrieved tokens via TokenManager // Save retrieved tokens via TokenManager
val tokenManager = TokenManager.getInstance(context) val tokenManager = TokenManager.getInstance(context)
val id=loginData.user?.profile?.full_name val id=loginData.user?.id
Log.d("useridlogin","$id") Log.d("useridlogin","$id")
tokenManager.saveTokens( tokenManager.saveTokens(
accessToken = loginData.accessToken ?: "", accessToken = loginData.accessToken ?: "",
refreshToken = loginData.refreshToken ?: "", refreshToken = loginData.refreshToken ?: "",
userId = loginData.user?.id ?: -1, userId = loginData.user?.id ?: -1,
currenUserName = loginData.user?.profile?.full_name currenUserName = loginData.user?.profile?.full_name,
profile_image_url=loginData.user?.profile?.profile_image_url
) )
Toast.makeText(context, "Login successful!", Toast.LENGTH_SHORT).show() Toast.makeText(context, "Login successful!", Toast.LENGTH_SHORT).show()

59
app/src/main/java/com/example/dattingapp/ShowPostingActivity.kt

@ -1,4 +1,5 @@
package com.example.dattingapp package com.example.dattingapp
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
@ -44,6 +45,7 @@ import com.example.dattingapp.homes.networkandapi.ShowPostUiState
import com.example.dattingapp.homes.networkandapi.ShowPostViewModel import com.example.dattingapp.homes.networkandapi.ShowPostViewModel
import androidx.compose.material.icons.filled.Send import androidx.compose.material.icons.filled.Send
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
@ -135,38 +137,52 @@ fun PostCard(
.padding(horizontal = 12.dp, vertical = 8.dp), .padding(horizontal = 12.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Box( AsyncImage(
model = post.author?.profile_image_url,
contentDescription = "Post author image",
placeholder = painterResource(id = R.drawable.locationpermissionbg), // your existing placeholder
error = painterResource(id = R.drawable.locationpermissionbg),
modifier = Modifier modifier = Modifier
.size(40.dp) .size(40.dp)
.clip(CircleShape) .clip(CircleShape),
.background(Color.LightGray), contentScale = ContentScale.Crop
contentAlignment = Alignment.Center )
) {
Text(text = post.author?.full_name?.take(1) ?: "U")
}
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
Column { Column {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
val authorName = post.author?.full_name ?: "Anonymous" val authorName = post.author?.full_name ?: "Anonymous"
val feelingText = post.feeling ?: "" val feelingText = post.feeling ?: ""
val context = LocalContext.current
// ✅ Clickable author name
Text( Text(
text = buildAnnotatedString { text = authorName,
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { fontSize = 16.sp,
append("$authorName ") fontWeight = FontWeight.Bold,
modifier = Modifier.clickable {
val intent = Intent(context, FriendProfile::class.java).apply {
putExtra("USER_ID", post.author?.id) // ← send the user id
}
context.startActivity(intent)
} }
)
// Rest of the line, unchanged
if (feelingText.isNotEmpty()) { if (feelingText.isNotEmpty()) {
Text(
text = buildAnnotatedString {
withStyle(style = SpanStyle(fontWeight = FontWeight.Normal)) { withStyle(style = SpanStyle(fontWeight = FontWeight.Normal)) {
append(" — feeling ") append(" — feeling ")
} }
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
append(feelingText) append(feelingText)
} }
}
}, },
fontSize = 16.sp fontSize = 16.sp
) )
} }
}
if (!post.location?.name.isNullOrEmpty()) { if (!post.location?.name.isNullOrEmpty()) {
Text( Text(
@ -295,6 +311,7 @@ fun CommentSection(
val tokenManager = remember { TokenManager.getInstance(context) } val tokenManager = remember { TokenManager.getInstance(context) }
val currentUserName = remember { tokenManager.currentUserName() } val currentUserName = remember { tokenManager.currentUserName() }
val currentProfileImage = remember { tokenManager.currentProfileImage() }
// ✅ get the logged-in user's id once // ✅ get the logged-in user's id once
val currentUserId = remember { tokenManager.getUserId() } val currentUserId = remember { tokenManager.getUserId() }
@ -383,19 +400,16 @@ fun CommentSection(
.padding(vertical = 6.dp), .padding(vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Box( AsyncImage(
model = currentProfileImage,
contentDescription = "Post author image",
placeholder = painterResource(id = R.drawable.locationpermissionbg), // your existing placeholder
error = painterResource(id = R.drawable.locationpermissionbg),
modifier = Modifier modifier = Modifier
.size(32.dp) .size(32.dp)
.clip(CircleShape) .clip(CircleShape),
.background(Color.LightGray), contentScale = ContentScale.Crop
contentAlignment = Alignment.Center
) {
Text(
text = comment.author?.full_name?.take(1) ?: "U",
style = MaterialTheme.typography.bodySmall,
fontWeight = FontWeight.Bold
) )
}
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
@ -513,6 +527,7 @@ fun ShowPostScreenPreview() {
full_name = "Alex Rivera", full_name = "Alex Rivera",
city = "Shanghai", city = "Shanghai",
country = "China", country = "China",
profile_image_url=null,
compatibilityPercentage = 95 compatibilityPercentage = 95
), ),
like_count = 12, like_count = 12,
@ -542,6 +557,7 @@ fun ShowPostScreenPreview() {
full_name = "Sarah", full_name = "Sarah",
city = "Shanghai", city = "Shanghai",
country = "China", country = "China",
profile_image_url=null,
compatibilityPercentage = 100 compatibilityPercentage = 100
), ),
createdAt = "2026-10-13T07:45:15" createdAt = "2026-10-13T07:45:15"
@ -555,6 +571,7 @@ fun ShowPostScreenPreview() {
full_name = "David", full_name = "David",
city = "Beijing", city = "Beijing",
country = "China", country = "China",
profile_image_url=null,
compatibilityPercentage = 90 compatibilityPercentage = 90
), ),
createdAt = "2026-10-13T08:04:48" createdAt = "2026-10-13T08:04:48"

4
app/src/main/java/com/example/dattingapp/dataclass/DataClass.kt

@ -32,6 +32,10 @@ data class Author(
val full_name: String?, val full_name: String?,
val city: String?, val city: String?,
val country: String?, val country: String?,
@SerialName("profile_image_url")
val profile_image_url:String?,
@SerialName("compatibility_percentage") val compatibilityPercentage: Int? = 0 @SerialName("compatibility_percentage") val compatibilityPercentage: Int? = 0
) )

3
app/src/main/java/com/example/dattingapp/homes/ExploreScreen.kt

@ -54,6 +54,7 @@ import com.example.dattingapp.ui.theme.OverlayShadow
import com.example.dattingapp.ui.theme.TextPostTextColor import com.example.dattingapp.ui.theme.TextPostTextColor
import com.example.dattingapp.ui.theme.lightChipBg import com.example.dattingapp.ui.theme.lightChipBg
import com.example.dattingapp.ui.theme.primaryColor import com.example.dattingapp.ui.theme.primaryColor
import kotlinx.serialization.SerialName
// Data Models // Data Models
@ -628,6 +629,7 @@ private val mockFeed = listOf(
full_name = "Alex Rivera", full_name = "Alex Rivera",
city = "Shanghai", city = "Shanghai",
country = "China", country = "China",
profile_image_url = null,
compatibilityPercentage = 95 compatibilityPercentage = 95
), ),
like_count = 12, like_count = 12,
@ -657,6 +659,7 @@ private val mockFeed = listOf(
full_name = "Sarah", full_name = "Sarah",
city = "Beijing", city = "Beijing",
country = "China", country = "China",
profile_image_url = null,
compatibilityPercentage = 88 compatibilityPercentage = 88
), ),
like_count = 0, like_count = 0,

18
app/src/main/java/com/example/dattingapp/homes/MeetScreen.kt

@ -4,6 +4,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.util.Log import android.util.Log
import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
@ -205,12 +206,15 @@ private fun ProfileCardItem(
} }
val defaultPainter = rememberVectorPainter(Icons.Default.Person) val defaultPainter = rememberVectorPainter(Icons.Default.Person)
if (!photoUrl.isNullOrBlank()) {
AsyncImage( AsyncImage(
model = ImageRequest.Builder(context) model = ImageRequest.Builder(context)
.data(photoUrl.takeIf { !it.isNullOrBlank() }) .data(photoUrl) // ← guaranteed non-null here
.apply { .apply {
if (!token.isNullOrBlank()) { if (!token.isNullOrBlank()) {
val authHeader = if (token.startsWith("Bearer ", ignoreCase = true)) token else "Bearer $token" val authHeader =
if (token.startsWith("Bearer ", ignoreCase = true)) token
else "Bearer $token"
addHeader("Authorization", authHeader) addHeader("Authorization", authHeader)
} }
} }
@ -218,7 +222,6 @@ private fun ProfileCardItem(
.build(), .build(),
placeholder = defaultPainter, placeholder = defaultPainter,
error = defaultPainter, error = defaultPainter,
fallback = defaultPainter, // Shows Icons.Default.Person immediately when photoUrl is null/blank
contentDescription = "Profile Image", contentDescription = "Profile Image",
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
@ -226,6 +229,15 @@ private fun ProfileCardItem(
Log.e("CoilStatus", "Failed to load image", errorState.result.throwable) Log.e("CoilStatus", "Failed to load image", errorState.result.throwable)
} }
) )
} else {
// No URL at all → show the default directly, Coil never runs
Image(
painter = defaultPainter,
contentDescription = "Profile Image",
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
}
// DARK GRADIENT OVERLAY // DARK GRADIENT OVERLAY
Box( Box(

28
app/src/main/java/com/example/dattingapp/homes/MyProfile.kt

@ -45,6 +45,7 @@ import androidx.core.content.ContextCompat.startActivity
// MY PROFILE SCREEN // MY PROFILE SCREEN
// ============================================================ // ============================================================
@Composable @Composable
fun MyProfileScreen( fun MyProfileScreen(
profileResponse : UserResponse?, // Change from MyProfileData? to MyProfileResponse? profileResponse : UserResponse?, // Change from MyProfileData? to MyProfileResponse?
@ -305,7 +306,10 @@ fun MyProfileScreen(
label = "Likes", label = "Likes",
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
onClick = { onClick = {
val intent = Intent(context, LikeANdMatchActivity::class.java) val intent = Intent(context, LikeANdMatchActivity::class.java).apply {
putExtra("TYPE", "Likes") // ← e.g. "like", "liked", or "matches"
// putExtra("USER_ID", userId.toString()) // ← if you also need a user id
}
context.startActivity(intent) context.startActivity(intent)
onLikesClick() onLikesClick()
} }
@ -316,7 +320,14 @@ fun MyProfileScreen(
number = profileResponse?.liked_by_users_count?.toString() ?: "0", number = profileResponse?.liked_by_users_count?.toString() ?: "0",
label = "Liked", label = "Liked",
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
onClick = { /* Optional: Handle Matches click */ } onClick = {
val intent = Intent(context, LikeANdMatchActivity::class.java).apply {
putExtra("TYPE", "Liked") // ← e.g. "like", "liked", or "matches"
// putExtra("USER_ID", userId.toString()) // ← if you also need a user id
}
context.startActivity(intent)
onLikesClick()
}
) )
@ -330,7 +341,14 @@ fun MyProfileScreen(
number = profileResponse?.matches_count?.toString() ?: "0", number = profileResponse?.matches_count?.toString() ?: "0",
label = "Matches", label = "Matches",
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
onClick = { /* Optional: Handle Matches click */ } onClick = {
val intent = Intent(context, LikeANdMatchActivity::class.java).apply {
putExtra("TYPE", "Matches") // ← e.g. "like", "liked", or "matches"
// putExtra("USER_ID", userId.toString()) // ← if you also need a user id
}
context.startActivity(intent)
onLikesClick()
}
) )
} }
Spacer( Spacer(
@ -346,6 +364,8 @@ fun MyProfileScreen(
} }
// ============================================================ // ============================================================
// PROFILE MEDIA GRID // PROFILE MEDIA GRID
// ============================================================ // ============================================================
@ -414,7 +434,7 @@ private fun StatCard(
) { ) {
Column( Column(
modifier = Modifier.fillMaxWidth().padding(vertical = 12.dp), modifier = Modifier.fillMaxWidth().padding(vertical = 5.dp),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Text( Text(

11
app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeApiService.kt

@ -69,9 +69,20 @@ interface HomeApiService {
): Response<UserResponse> ): Response<UserResponse>
@GET("api/v1/users/me/liked") @GET("api/v1/users/me/liked")
suspend fun getLikeUsers(
): Response<List<LikedUserResponse>>
@GET("/api/v1/users/me/likes")
suspend fun getLikedUsers( suspend fun getLikedUsers(
): Response<List<LikedUserResponse>> ): Response<List<LikedUserResponse>>
@GET("/api/v1/matches")
suspend fun getMatches(
@Query("limit") limit: Int = 20,
@Query("offset") offset: Int = 0
): Response<List<LikedUserResponse>>
@GET("api/v1/explore/posts/{post_id}") @GET("api/v1/explore/posts/{post_id}")
suspend fun getPostById( suspend fun getPostById(
@Path("post_id") postId: Int @Path("post_id") postId: Int

48
app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeViewModelClass.kt

@ -534,49 +534,62 @@ sealed interface LikedUiState {
class LikedViewModel : ViewModel() { class LikedViewModel : ViewModel() {
private val _likeUiState = MutableStateFlow<LikedUiState>(LikedUiState.Idle) // ✅ three separate states, one per list type
val likeUiState: StateFlow<LikedUiState> = _likeUiState.asStateFlow() private val _likesUiState = MutableStateFlow<LikedUiState>(LikedUiState.Idle) // users who liked ME
val likesUiState: StateFlow<LikedUiState> = _likesUiState.asStateFlow()
fun likedResponse(context: Context) { private val _likedUiState = MutableStateFlow<LikedUiState>(LikedUiState.Idle) // users I liked
if (_likeUiState.value is LikedUiState.Loading) return val likedUiState: StateFlow<LikedUiState> = _likedUiState.asStateFlow()
private val _matchesUiState = MutableStateFlow<LikedUiState>(LikedUiState.Idle) // matches
val matchesUiState: StateFlow<LikedUiState> = _matchesUiState.asStateFlow()
fun likedResponse(context: Context, type: String) {
val appContext = context.applicationContext val appContext = context.applicationContext
// ✅ pick which state this call writes to
val targetFlow = when (type) {
"Likes" -> _likesUiState
"Liked" -> _likedUiState
else -> _matchesUiState
}
if (targetFlow.value is LikedUiState.Loading) return
viewModelScope.launch { viewModelScope.launch {
_likeUiState.value = LikedUiState.Loading targetFlow.value = LikedUiState.Loading
if (TokenManager.getInstance(appContext).getAccessToken().isNullOrBlank()) { if (TokenManager.getInstance(appContext).getAccessToken().isNullOrBlank()) {
Log.e("MeetApi", "No access token in user_tokens preferences") Log.e("MeetApi", "No access token in user_tokens preferences")
_likeUiState.value = LikedUiState.Error("No access token found. Please log in again.") targetFlow.value = LikedUiState.Error("No access token found. Please log in again.")
return@launch return@launch
} }
try { try {
// Returns CreatePostResponse directly val createdPost = when (type) {
val createdPost = HomeNetworkModule "Likes" -> HomeNetworkModule.getApiService(appContext).getLikeUsers()
.getApiService(appContext) "Liked" -> HomeNetworkModule.getApiService(appContext).getLikedUsers()
.getLikedUsers() else -> HomeNetworkModule.getApiService(appContext).getMatches(20, 10)
}
Log.d("CreatePostDebug", "Response Code: ${createdPost.code()}") Log.d("CreatePostDebug", "$type Response Code: ${createdPost.code()}")
if (createdPost.isSuccessful && createdPost.body() != null) { if (createdPost.isSuccessful && createdPost.body() != null) {
_likeUiState.value = LikedUiState.Success(createdPost.body()!!) // Use .body() here! targetFlow.value = LikedUiState.Success(createdPost.body()!!)
} else { } else {
_likeUiState.value = LikedUiState.Error("No post data received from the server.") targetFlow.value = LikedUiState.Error("No data received from the server.")
} }
} catch (e: HttpException) { } catch (e: HttpException) {
// Handle HTTP errors via HttpException automatically thrown by Retrofit
val message = when (e.code()) { val message = when (e.code()) {
401 -> "Authentication failed. Please log in again." 401 -> "Authentication failed. Please log in again."
403 -> "The server refused access to this profile." 403 -> "The server refused access to this profile."
404 -> "User profile was not found." 404 -> "User profile was not found."
else -> "Request failed: HTTP ${e.code()}." else -> "Request failed: HTTP ${e.code()}."
} }
_likeUiState.value = LikedUiState.Error(message) targetFlow.value = LikedUiState.Error(message)
} catch (e: Exception) { } catch (e: Exception) {
Log.e("MeetApi", "Unexpected request failure", e) Log.e("MeetApi", "Unexpected request failure", e)
_likeUiState.value = LikedUiState.Error("Request failed: ${e.javaClass.simpleName}") targetFlow.value = LikedUiState.Error("Request failed: ${e.javaClass.simpleName}")
} }
} }
} }
@ -653,6 +666,7 @@ class ShowPostViewModel : ViewModel() {
full_name = currentUserName ?: "You", full_name = currentUserName ?: "You",
city = null, city = null,
country = null, country = null,
profile_image_url = null,
compatibilityPercentage = null compatibilityPercentage = null
), ),
createdAt = "Just now" createdAt = "Just now"

4
app/src/main/java/com/example/dattingapp/homes/networkandapi/TokenManager.kt

@ -11,7 +11,7 @@ class TokenManager private constructor(context: Context) {
Context.MODE_PRIVATE Context.MODE_PRIVATE
) )
fun saveTokens(accessToken: String, refreshToken: String?,userId: Int,currenUserName:String?) { fun saveTokens(accessToken: String, refreshToken: String?,userId: Int,currenUserName:String?,profile_image_url:String?) {
val cleanToken = accessToken.trim() val cleanToken = accessToken.trim()
.replace( .replace(
Regex("^Bearer\\s+", RegexOption.IGNORE_CASE), Regex("^Bearer\\s+", RegexOption.IGNORE_CASE),
@ -24,6 +24,7 @@ class TokenManager private constructor(context: Context) {
.putString(KEY_REFRESH_TOKEN, refreshToken) .putString(KEY_REFRESH_TOKEN, refreshToken)
.putInt("user_id", userId) .putInt("user_id", userId)
.putString("currenUserName", currenUserName) .putString("currenUserName", currenUserName)
.putString("profile_image_url", profile_image_url)
.commit() .commit()
// Do not log the token itself. // Do not log the token itself.
@ -37,6 +38,7 @@ class TokenManager private constructor(context: Context) {
} }
fun currentUserName(): String? = preferences.getString("full_name", null) fun currentUserName(): String? = preferences.getString("full_name", null)
fun currentProfileImage(): String? = preferences.getString("profile_image_url", null)
fun getAccessToken(): String? { fun getAccessToken(): String? {
return preferences.getString(KEY_ACCESS_TOKEN, null) return preferences.getString(KEY_ACCESS_TOKEN, null)

4
app/src/main/java/com/example/dattingapp/registration/RegisterRequest.kt

@ -74,5 +74,7 @@ data class LoginUser(
data class LoginProfile( data class LoginProfile(
@SerializedName("full_name") @SerializedName("full_name")
val full_name: String? = null val full_name: String? = null,
@SerializedName("profile_image_url")
val profile_image_url: String? = null
) )

2
app/src/main/java/com/example/dattingapp/ui/theme/Color.kt

@ -113,7 +113,7 @@ colors = listOf(
Color(0xFF6B00B0) // Deep Purple Color(0xFF6B00B0) // Deep Purple
) )
) )
val purpleButtonColor = Color(0xFF6B00F5) val purpleButtonColor = Color(0x236B00F5)
// Same post id always gives the same color // Same post id always gives the same color
val OverlayShadow = Shadow( val OverlayShadow = Shadow(

Loading…
Cancel
Save