Browse Source

feed

master
osman160128 2 weeks ago
parent
commit
58b14651e8
  1. 184
      app/src/main/java/com/example/dattingapp/FriendProfile.kt
  2. 47
      app/src/main/java/com/example/dattingapp/LikeANdMatchActivity.kt
  3. 12
      app/src/main/java/com/example/dattingapp/Login/LoginActivity.kt
  4. 113
      app/src/main/java/com/example/dattingapp/homes/ExploreScreen.kt
  5. 684
      app/src/main/java/com/example/dattingapp/homes/MyProfile.kt
  6. 8
      app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeViewModelClass.kt
  7. 23
      app/src/main/java/com/example/dattingapp/homes/networkandapi/TokenManager.kt
  8. 8
      app/src/main/java/com/example/dattingapp/registration/RegistrationScreen.kt

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

@ -31,6 +31,7 @@ import androidx.compose.material.icons.filled.TextSnippet
import androidx.compose.material.icons.outlined.LocationOn import androidx.compose.material.icons.outlined.LocationOn
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
@ -140,10 +141,10 @@ fun FriendProfileScreen(
} }
// ✅ turn each into a set of ids // ✅ turn each into a set of ids
val likedMeIds = (likesState as? LikedUiState.Success) val iLikedIds = (likesState as? LikedUiState.Success)
?.LikeData?.map { it.id }?.toSet() ?: emptySet() ?.LikeData?.map { it.id }?.toSet() ?: emptySet()
val iLikedIds = (likedState as? LikedUiState.Success) val likedMeIds = (likedState as? LikedUiState.Success)
?.LikeData?.map { it.id }?.toSet() ?: emptySet() ?.LikeData?.map { it.id }?.toSet() ?: emptySet()
val matchIds = (matchesState as? LikedUiState.Success) val matchIds = (matchesState as? LikedUiState.Success)
@ -154,6 +155,12 @@ fun FriendProfileScreen(
val heLikedMe = friendProfileData.id in likedMeIds val heLikedMe = friendProfileData.id in likedMeIds
val iLikedHim = friendProfileData.id in iLikedIds val iLikedHim = friendProfileData.id in iLikedIds
// ✅ add this at the top of FriendProfileScreen, next to your other states
var localLiked by rememberSaveable { mutableStateOf(false) }
// Like is disabled if: I liked them before (server) OR I just clicked Like now
val likeDisabled = iLikedHim || localLiked
val age = remember(friendProfileData.birth_date) { val age = remember(friendProfileData.birth_date) {
calculateAge(friendProfileData.birth_date) calculateAge(friendProfileData.birth_date)
} }
@ -303,8 +310,7 @@ fun FriendProfileScreen(
) )
} }
//action button
// Action Buttons Row
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -312,110 +318,124 @@ fun FriendProfileScreen(
horizontalArrangement = Arrangement.spacedBy(10.dp), horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
// Message — inside a card // Message — card style, no background color
if (isMatch) { // Message — enabled only when matched, grayed out otherwise
Card( Card(
onClick = onMessageClick, onClick = onMessageClick,
modifier = Modifier enabled = isMatch, // ✅ disabled when not matched
.weight(1f) modifier = Modifier
.height(48.dp), .weight(1f)
shape = RoundedCornerShape(10.dp), .height(48.dp),
colors = CardDefaults.cardColors(containerColor = Color.White), shape = RoundedCornerShape(10.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) colors = CardDefaults.cardColors(
containerColor = if (isMatch) Color.White else Color(0xFFF0F0F0),
disabledContainerColor = Color(0xFFF0F0F0)
),
elevation = CardDefaults.cardElevation(
defaultElevation = if (isMatch) 4.dp else 0.dp
)
) {
Row(
modifier = Modifier.fillMaxSize(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) { ) {
Row( Icon(
modifier = Modifier.fillMaxSize(), imageVector = Icons.Default.ChatBubble,
horizontalArrangement = Arrangement.Center, contentDescription = "Message",
verticalAlignment = Alignment.CenterVertically tint = if (isMatch) textDark else Color(0xFFBDBDBD), // gray when disabled
) { modifier = Modifier.size(18.dp)
Icon( )
imageVector = Icons.Default.ChatBubble, Spacer(modifier = Modifier.width(8.dp))
contentDescription = "Message", Text(
tint = textDark, text = "Message",
modifier = Modifier.size(18.dp) fontSize = 16.sp,
) fontWeight = FontWeight.Bold,
Spacer(modifier = Modifier.width(8.dp)) color = if (isMatch) textDark else Color(0xFFBDBDBD) // gray when disabled
Text( )
text = "Message",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = textDark
)
}
} }
} }
// ❤️ Like — red ICON when the user liked ME, hidden when matched
if (!isMatch) {
Card(
onClick = {
onLikeClick()
handleLikeAndDislikeAction(context, "right", friendProfileData, swipeViewModel)
},
modifier = Modifier.size(48.dp),
shape = RoundedCornerShape(10.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Icon(
imageVector = Icons.Default.Favorite,
contentDescription = "Like",
tint = when {
heLikedMe -> Color(0xFFE53935) // 🔴 user liked me
iLikedHim -> Color(0xFF1E88E5) // 🔵 I liked the user
else -> textDark // ⚫ no relation yet
},
modifier = Modifier.size(22.dp)
)
}
}
// Super Like — inside a card // ❤️ Like — red ICON when the user liked ME, normal otherwise
Card( // hidden when matched
onClick = {
onStarClick() // ❤️ Like — RED background if user liked me, DISABLED if I liked them
handleLikeAndDislikeAction(context, "right", friendProfileData, swipeViewModel) Card(
}, onClick = {
modifier = Modifier.size(48.dp), onLikeClick()
shape = RoundedCornerShape(10.dp), handleLikeAndDislikeAction(context, "right", friendProfileData, swipeViewModel)
colors = CardDefaults.cardColors(containerColor = Color.White), localLiked = true // ✅ disable after first click
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) },
) { enabled = !likeDisabled,
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { modifier = Modifier.size(48.dp),
Icon( shape = RoundedCornerShape(10.dp),
imageVector = Icons.Default.Star, colors = CardDefaults.cardColors(
contentDescription = "Favorite", containerColor = if (heLikedMe) Color(0xFFE53935) else Color.White,
tint = textDark, disabledContainerColor = if (heLikedMe) Color(0xFFE53935) else Color(0xFFF0F0F0)
modifier = Modifier.size(22.dp) ),
) elevation = CardDefaults.cardElevation(
} defaultElevation = if (likeDisabled) 0.dp else 2.dp
)
) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Icon(
imageVector = Icons.Default.Favorite,
contentDescription = "Like",
tint = when {
heLikedMe -> Color.White // white heart on red background
likeDisabled -> Color(0xFFBDBDBD) // gray when disabled
else -> textDark // normal
},
modifier = Modifier.size(20.dp)
)
} }
} }
// Cancel — inside a card, always visible // Super Like — card style
Card(
onClick = {
onStarClick()
handleLikeAndDislikeAction(context, "right", friendProfileData, swipeViewModel)
},
modifier = Modifier.size(48.dp),
shape = RoundedCornerShape(10.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Icon(
imageVector = Icons.Default.Star,
contentDescription = "Favorite",
tint = textDark,
modifier = Modifier.size(20.dp)
)
}
}
// Cancel — card style, always visible
// Cancel — clicking it re-enables the Like button
Card( Card(
onClick = { onClick = {
onCancelClick() onCancelClick()
handleLikeAndDislikeAction(context, "left", friendProfileData, swipeViewModel) handleLikeAndDislikeAction(context, "left", friendProfileData, swipeViewModel)
localLiked = false // ✅ Like button works again
}, },
modifier = Modifier.size(48.dp), modifier = Modifier.size(48.dp),
shape = RoundedCornerShape(10.dp), shape = RoundedCornerShape(10.dp),
colors = CardDefaults.cardColors(containerColor = Color.White), colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
) { ) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Icon( Icon(
imageVector = Icons.Default.Close, imageVector = Icons.Default.Close,
contentDescription = "Dislike", contentDescription = "Dislike",
tint = textDark, tint = textDark,
modifier = Modifier.size(22.dp) modifier = Modifier.size(20.dp)
) )
} }
} }
} }
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
// User Posts Grid Section below Action Row // User Posts Grid Section below Action Row

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

@ -15,6 +15,8 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.LocationOn import androidx.compose.material.icons.filled.LocationOn
import androidx.compose.material3.* import androidx.compose.material3.*
@ -54,18 +56,50 @@ 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" val type = intent.getStringExtra("TYPE") ?: "Likes"
// Collect state from ViewModel // Dynamically collect the correct StateFlow based on the 'type'
val uiState by likedViewModel.likesUiState.collectAsState() val uiState by when (type) {
"Likes" -> likedViewModel.likesUiState.collectAsState()
"Liked" -> likedViewModel.likedUiState.collectAsState()
else -> likedViewModel.matchesUiState.collectAsState()
}
// Trigger network call once when screen loads // Trigger network call once when screen loads
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
likedViewModel.likedResponse(context,type) likedViewModel.likedResponse(context, type)
} }
Scaffold( Scaffold(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
topBar = {
@OptIn(ExperimentalMaterial3Api::class)
TopAppBar(
title = {
// Display dynamic title based on the page type
Text(
text = when (type) {
"Likes" -> "My Likes"
"Liked" -> "Liked Me"
else -> "My Matches"
},
fontWeight = FontWeight.Bold,
fontSize = 18.sp
)
},
navigationIcon = {
IconButton(onClick = { finish() }) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "Back"
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface
)
)
}
) { innerPadding -> ) { innerPadding ->
Box( Box(
modifier = Modifier modifier = Modifier
@ -79,7 +113,7 @@ class LikeANdMatchActivity : ComponentActivity() {
} }
is LikedUiState.Success -> { is LikedUiState.Success -> {
ProfileGrid( ProfileGrid(
context =context, context = context,
likedResponse = state.LikeData, likedResponse = state.LikeData,
isLoading = false, isLoading = false,
errorMessage = null errorMessage = null
@ -96,7 +130,7 @@ class LikeANdMatchActivity : ComponentActivity() {
color = MaterialTheme.colorScheme.error, color = MaterialTheme.colorScheme.error,
fontWeight = FontWeight.Medium fontWeight = FontWeight.Medium
) )
Button(onClick = { likedViewModel.likedResponse(context,"like") }) { Button(onClick = { likedViewModel.likedResponse(context, type) }) {
Text("Retry") Text("Retry")
} }
} }
@ -108,7 +142,6 @@ class LikeANdMatchActivity : ComponentActivity() {
} }
} }
} }
// 1. Define ProfileCard FIRST so ProfileGrid can reference it without issue // 1. Define ProfileCard FIRST so ProfileGrid can reference it without issue
@Composable @Composable
fun ProfileCard(context: Context, likedProfile: LikedUserResponse, modifier: Modifier = Modifier, fun ProfileCard(context: Context, likedProfile: LikedUserResponse, modifier: Modifier = Modifier,

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

@ -274,12 +274,16 @@ fun LoginScreenContent(
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,
currenUserName = loginData.user?.profile?.full_name,
profile_image_url=loginData.user?.profile?.profile_image_url
) )
tokenManager.saveProfile(
userId = loginData.user?.id ?: -1,
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()
onLoginSuccess() onLoginSuccess()
} else { } else {

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

@ -30,6 +30,7 @@ import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.graphics.luminance import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
@ -42,12 +43,14 @@ import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView import androidx.media3.ui.PlayerView
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.example.dattingapp.R
import com.example.dattingapp.ShowPostingActivity import com.example.dattingapp.ShowPostingActivity
import com.example.dattingapp.dataclass.Author import com.example.dattingapp.dataclass.Author
import com.example.dattingapp.dataclass.Location import com.example.dattingapp.dataclass.Location
import com.example.dattingapp.dataclass.MediaItem import com.example.dattingapp.dataclass.MediaItem
import com.example.dattingapp.dataclass.ShowPostResponse import com.example.dattingapp.dataclass.ShowPostResponse
import com.example.dattingapp.homes.networkandapi.PostResponse import com.example.dattingapp.homes.networkandapi.PostResponse
import com.example.dattingapp.homes.networkandapi.TokenManager
import com.example.dattingapp.homes.profile.UserResponse import com.example.dattingapp.homes.profile.UserResponse
import com.example.dattingapp.ui.theme.DattingAPPTheme import com.example.dattingapp.ui.theme.DattingAPPTheme
import com.example.dattingapp.ui.theme.OverlayShadow import com.example.dattingapp.ui.theme.OverlayShadow
@ -64,15 +67,6 @@ data class VibeCategory(
val isSelected: Boolean = false val isSelected: Boolean = false
) )
data class EventCardData(
val id: String,
val title: String,
val description: String,
val imageUrl: String,
val tagLabel: String,
val isTrending: Boolean = false,
val tags: List<String> = emptyList()
)
data class UserMatch( data class UserMatch(
val id: String, val id: String,
@ -132,6 +126,10 @@ fun VibeCheckHomeScreen(
private fun VibeCheckTopAppBar( private fun VibeCheckTopAppBar(
primaryColor: Color primaryColor: Color
) { ) {
val context = LocalContext.current
val tokenManager = remember { TokenManager.getInstance(context) }
val currentUserName = remember { tokenManager.currentUserName() }
val currentProfileImage = remember { tokenManager.currentProfileImage() }
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -147,17 +145,22 @@ private fun VibeCheckTopAppBar(
} }
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Box(
AsyncImage(
model = currentProfileImage,
contentDescription = "Post author image",
placeholder = painterResource(id = com.example.dattingapp.R.drawable.locationpermissionbg), // your existing placeholder
error = painterResource(id = R.drawable.locationpermissionbg),
modifier = Modifier modifier = Modifier
.size(32.dp) .size(40.dp)
.clip(CircleShape) .clip(CircleShape),
.background(Color.Gray) contentScale = ContentScale.Crop
) )
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(2.dp))
Text( Text(
text = "VibeCheck", text = currentUserName?:"",
color = primaryColor, color = primaryColor,
fontSize = 20.sp, fontSize = 20.sp,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold
@ -177,47 +180,53 @@ private fun VibeCheckTopAppBar(
private fun SearchBarSection() { private fun SearchBarSection() {
var searchText by remember { mutableStateOf("") } var searchText by remember { mutableStateOf("") }
OutlinedTextField( OutlinedTextField(
value = searchText, value = searchText,
onValueChange = { searchText = it }, onValueChange = { searchText = it },
placeholder = { placeholder = {
Text(text = "Search", color = Color.Gray) Text(text = "Search", color = Color.Gray, fontSize = 13.sp)
}, },
leadingIcon = { leadingIcon = {
Icon(
imageVector = Icons.Default.Search,
contentDescription = null,
tint = Color.Gray
)
},
trailingIcon = {
Row(modifier = Modifier.padding(end = 8.dp)) {
Icon(
imageVector = Icons.Outlined.Image,
contentDescription = null,
tint = Color.Gray
)
Spacer(modifier = Modifier.width(8.dp))
Icon( Icon(
imageVector = Icons.Outlined.LocationOn, imageVector = Icons.Default.Search,
contentDescription = null, contentDescription = null,
tint = Color.Gray tint = Color.Gray,
modifier = Modifier.size(18.dp)
) )
} },
}, trailingIcon = {
shape = RoundedCornerShape(24.dp), Row(
colors = OutlinedTextFieldDefaults.colors( verticalAlignment = Alignment.CenterVertically
unfocusedContainerColor = Color(0xFFF5F5F5), ) {
focusedContainerColor = Color(0xFFF5F5F5), Icon(
unfocusedBorderColor = Color.Transparent, imageVector = Icons.Outlined.Image,
focusedBorderColor = Color.Transparent contentDescription = null,
), tint = Color.Gray,
modifier = Modifier modifier = Modifier.size(18.dp)
.fillMaxWidth() )
.height(50.dp) Spacer(modifier = Modifier.width(6.dp))
) Icon(
} imageVector = Icons.Outlined.LocationOn,
contentDescription = null,
tint = Color.Gray,
modifier = Modifier.size(18.dp)
)
}
},
shape = RoundedCornerShape(24.dp),
colors = OutlinedTextFieldDefaults.colors(
unfocusedContainerColor = Color(0xFFF5F5F5),
focusedContainerColor = Color(0xFFF5F5F5),
unfocusedBorderColor = Color.Transparent,
focusedBorderColor = Color.Transparent
),
singleLine = true,
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
)
}
@Composable @Composable
private fun VibeFilterChipsSection( private fun VibeFilterChipsSection(
selectedColor: Color, selectedColor: Color,

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

@ -1,15 +1,21 @@
package com.example.dattingapp.homes package com.example.dattingapp.homes
import android.content.Intent
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.clickable
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.TextSnippet
import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.MusicNote
import androidx.compose.material.icons.filled.TextSnippet
import androidx.compose.material.icons.outlined.Edit import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material.icons.outlined.Settings import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.* import androidx.compose.material3.*
@ -20,105 +26,114 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.example.dattingapp.LikeANdMatchActivity
import com.example.dattingapp.MasonryPostItem
import com.example.dattingapp.R import com.example.dattingapp.R
import com.example.dattingapp.ShowPostingActivity
import com.example.dattingapp.StaggeredFriendPostsGrid
import com.example.dattingapp.homes.profile.Author
import com.example.dattingapp.homes.profile.Media
import com.example.dattingapp.homes.profile.UserResponse import com.example.dattingapp.homes.profile.UserResponse
import com.example.dattingapp.homes.profile.Post
import com.example.dattingapp.homes.profile.ProfileData
import com.example.dattingapp.ui.theme.BrandMagenta import com.example.dattingapp.ui.theme.BrandMagenta
import com.example.dattingapp.ui.theme.BrandPurple import com.example.dattingapp.ui.theme.BrandPurple
import com.example.dattingapp.ui.theme.DattingAPPTheme import com.example.dattingapp.ui.theme.DattingAPPTheme
import com.example.dattingapp.ui.theme.textDark
import com.example.dattingapp.ui.theme.TextMuted import com.example.dattingapp.ui.theme.TextMuted
import com.example.dattingapp.homes.profile.ProfileData import com.example.dattingapp.ui.theme.textDark
import com.example.dattingapp.LikeANdMatchActivity import java.time.LocalDate
import android.content.Intent import java.time.Period
import android.util.Log import java.time.format.DateTimeFormatter
import androidx.compose.foundation.clickable
import androidx.compose.ui.platform.LocalContext // Helper function to calculate age from birth_date string (e.g., "1995-05-15")
import androidx.core.content.ContextCompat.startActivity private fun calculateAge(birthDateStr: String?): Int? {
if (birthDateStr.isNullOrBlank()) return null
// ============================================================ return try {
// MY PROFILE SCREEN val formatter = DateTimeFormatter.ISO_LOCAL_DATE
// ============================================================ val birthDate = LocalDate.parse(birthDateStr.take(10), formatter)
Period.between(birthDate, LocalDate.now()).years
} catch (e: Exception) {
null
}
}
@Composable @Composable
fun MyProfileScreen( fun MyProfileScreen(
profileResponse : UserResponse?, // Change from MyProfileData? to MyProfileResponse? profileResponse: UserResponse?,
isLoading: Boolean, isLoading: Boolean,
errorMessage: String?, errorMessage: String?,
onRetry: () -> Unit = {}, onRetry: () -> Unit = {},
onEditClick: () -> Unit = {}, onEditClick: () -> Unit = {},
onUpgradeClick: () -> Unit = {}, onUpgradeClick: () -> Unit = {},
onSettingsClick: () -> Unit = {}, onSettingsClick: () -> Unit = {},
onLikesClick: () -> Unit={}, onLikesClick: () -> Unit = {},
onLikedClick: () -> Unit={}, onLikedClick: () -> Unit = {},
onMatchClick: () -> Unit={} onMatchClick: () -> Unit = {}
) { ) {
// ========================================================
// FULL SCREEN SCROLL
// ========================================================
val context = LocalContext.current val context = LocalContext.current
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
val profile = profileResponse?.profile
val age = calculateAge(profile?.birth_date)
Column( Column(
modifier = Modifier.fillMaxSize().background(Color.White).verticalScroll(scrollState)) { modifier = Modifier
.fillMaxSize()
.background(Color.White)
.verticalScroll(scrollState)
) {
// ==================================================== // ====================================================
// HEADER // HEADER & COVER IMAGE
// ==================================================== // ====================================================
Box( Box(
modifier = Modifier.fillMaxWidth().height(260.dp) modifier = Modifier
.fillMaxWidth()
.height(260.dp)
) { ) {
// =================================================
// COVER IMAGE
// =================================================
Image( Image(
painter = painterResource(id = R.drawable.locationpermissionbg), painter = painterResource(id = R.drawable.locationpermissionbg),
contentDescription = "Cover Banner", contentDescription = "Cover Banner",
modifier = Modifier.fillMaxWidth().height(200.dp), modifier = Modifier
.fillMaxWidth()
.height(200.dp),
contentScale = ContentScale.Crop contentScale = ContentScale.Crop
) )
// =================================================
// SETTINGS BUTTON
// =================================================
// SETTINGS BUTTON
IconButton( IconButton(
onClick = onSettingsClick, onClick = onSettingsClick,
modifier = Modifier.align(Alignment.TopEnd) modifier = Modifier
.align(Alignment.TopEnd)
.padding(top = 16.dp, end = 16.dp) .padding(top = 16.dp, end = 16.dp)
.size(36.dp) .size(36.dp)
.clip(CircleShape) .clip(CircleShape)
.background(Color.Black.copy(alpha = 0.3f) .background(Color.Black.copy(alpha = 0.3f))
)
) { ) {
Icon( Icon(
imageVector = Icons.Outlined.Settings, imageVector = Icons.Outlined.Settings,
contentDescription = "Settings", contentDescription = "Settings",
tint = Color.White, tint = Color.White,
modifier = Modifier.size(20.dp) modifier = Modifier.size(20.dp)
) )
} }
// =================================================
// PROFILE IMAGE // PROFILE IMAGE & VERIFIED BADGE
// =================================================
Box( Box(
modifier = Modifier.align(Alignment.BottomStart) modifier = Modifier
.align(Alignment.BottomStart)
.padding(start = 24.dp) .padding(start = 24.dp)
.size(110.dp) .size(110.dp)
) { ) {
AsyncImage( AsyncImage(
// Access nested inner 'profile' first, then 'profileImageUrl' model = profile?.profile_image_url,
model = profileResponse?.profile?.profile_image_url,
contentDescription = "Profile Picture", contentDescription = "Profile Picture",
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
placeholder = painterResource(id = R.drawable.locationpermissionbg), placeholder = painterResource(id = R.drawable.locationpermissionbg),
@ -129,15 +144,13 @@ fun MyProfileScreen(
.border(width = 4.dp, color = Color.White, shape = CircleShape) .border(width = 4.dp, color = Color.White, shape = CircleShape)
) )
// =================================================
// VERIFIED BADGE
// =================================================
Box( Box(
modifier = Modifier.align(Alignment.BottomEnd).size(28.dp).clip(CircleShape) modifier = Modifier
.align(Alignment.BottomEnd)
.size(28.dp)
.clip(CircleShape)
.background(BrandMagenta) .background(BrandMagenta)
.border(width = 2.dp, color = Color.White, shape = CircleShape .border(width = 2.dp, color = Color.White, shape = CircleShape),
),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Icon( Icon(
@ -149,90 +162,103 @@ fun MyProfileScreen(
} }
} }
} }
// ==================================================== // ====================================================
// USER INFORMATION // USER INFORMATION SECTION
// ==================================================== // ====================================================
Column( Column(
modifier = Modifier
modifier = Modifier.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 8.dp) .padding(horizontal = 24.dp, vertical = 8.dp)
) { ) {
// ================================================= // NAME, AGE & EDIT BUTTON
// NAME + EDIT
// =================================================
Row( Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.SpaceBetween
) { ) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = if (age != null) "${profile?.full_name ?: ""}, $age" else (profile?.full_name ?: ""),
fontSize = 22.sp,
fontWeight = FontWeight.Bold,
color = textDark
)
}
Text( }
text = profileResponse?.profile?.full_name?:"",
fontSize = 22.sp,
fontWeight = FontWeight.Bold,
color = BrandMagenta
)
Spacer(modifier = Modifier.height(4.dp))
// EDIT BUTTON // LOCATION & STATUS
val locationText = listOfNotNull(profile?.city, profile?.country).joinToString(", ")
if (locationText.isNotBlank()) {
Text(
text = "📍 $locationText",
fontSize = 13.sp,
color = TextMuted
)
Spacer(modifier = Modifier.height(6.dp))
}
OutlinedButton( // BIO CONTAINER
onClick = onEditClick, if (!profile?.bio.isNullOrBlank()) {
modifier = Modifier.height(30.dp), Card(
contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp), modifier = Modifier.fillMaxWidth(),
border = ButtonDefaults shape = RoundedCornerShape(12.dp),
.outlinedButtonBorder colors = CardDefaults.cardColors(containerColor = Color(0xFFF9F9F9)),
.copy(width = 1.dp) border = CardDefaults.outlinedCardBorder().copy(width = 0.5.dp)
) { ) {
Icon(
imageVector = Icons.Outlined.Edit,
contentDescription = null,
tint = BrandMagenta,
modifier = Modifier.size(14.dp)
)
Spacer(modifier = Modifier.width(4.dp))
Text( Text(
text = "Edit", text = profile.bio!!,
fontSize = 12.sp, fontSize = 13.sp,
color = BrandMagenta, color = textDark,
fontWeight = FontWeight.Bold lineHeight = 18.sp,
modifier = Modifier.padding(12.dp)
) )
} }
Spacer(modifier = Modifier.height(10.dp))
} }
Spacer(modifier = Modifier.height(4.dp)) // INTERESTS CHIPS
// ================================================= if (!profile?.interests.isNullOrEmpty()) {
// BIO FlowRow(
// ================================================= modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(6.dp),
Log.d("my id","${profileResponse?.id}" ) verticalArrangement = Arrangement.spacedBy(6.dp)
Text( ) {
profile.interests.forEach { interest ->
text = profileResponse?.profile?.bio?:"", Surface(
fontSize = 13.sp, shape = RoundedCornerShape(16.dp),
color = TextMuted, color = BrandMagenta.copy(alpha = 0.1f)
lineHeight = 18.sp ) {
) Text(
text = interest,
color = BrandMagenta,
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp)
)
}
}
}
}
} }
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
// ==================================================== // ====================================================
// PREMIUM BANNER // PREMIUM BANNER
// ==================================================== // ====================================================
Box( Box(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp) modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.clip(RoundedCornerShape(16.dp)) .clip(RoundedCornerShape(16.dp))
.background( .background(brush = Brush.horizontalGradient(colors = listOf(BrandMagenta, BrandPurple)))
brush = Brush.horizontalGradient(
colors = listOf(BrandMagenta, BrandPurple)
)
)
.padding(16.dp) .padding(16.dp)
) { ) {
Row( Row(
@ -241,42 +267,26 @@ fun MyProfileScreen(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Column { Column {
Text( Text(
text = "VibeCheck Premium", text = "VibeCheck Premium",
color = Color.White, color = Color.White,
fontSize = 16.sp, fontSize = 16.sp,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold
) )
Spacer(modifier = Modifier.height(2.dp))
Spacer(
modifier = Modifier.height(2.dp)
)
Text( Text(
text = "Unlock unlimited likes & more", text = "Unlock unlimited likes & more",
color = Color.White.copy(alpha = 0.8f), color = Color.White.copy(alpha = 0.8f),
fontSize = 12.sp fontSize = 12.sp
) )
} }
// UPGRADE BUTTON
Button( Button(
onClick = onUpgradeClick, onClick = onUpgradeClick,
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(containerColor = Color.White),
containerColor = Color.White
),
shape = RoundedCornerShape(20.dp), shape = RoundedCornerShape(20.dp),
contentPadding = PaddingValues( contentPadding = PaddingValues(horizontal = 14.dp, vertical = 4.dp)
horizontal = 14.dp,
vertical = 4.dp
)
) { ) {
Text( Text(
text = "Upgrade", text = "Upgrade",
color = BrandMagenta, color = BrandMagenta,
fontSize = 12.sp, fontSize = 12.sp,
@ -286,155 +296,98 @@ fun MyProfileScreen(
} }
} }
Spacer(modifier = Modifier.height(16.dp))
Spacer(
modifier = Modifier.height(16.dp)
)
// ==================================================== // ====================================================
// STATISTICS // STATISTICS CARDS
// ==================================================== // ====================================================
Row( Row(
modifier = Modifier
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), .fillMaxWidth()
.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
StatCard( StatCard(
number = profileResponse?.liked_users_count?.toString() ?: "0", number = profileResponse?.liked_users_count?.toString() ?: "0",
label = "Likes", label = "Likes",
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
onClick = { onClick = {
val intent = Intent(context, LikeANdMatchActivity::class.java).apply { val intent = Intent(context, LikeANdMatchActivity::class.java).apply {
putExtra("TYPE", "Likes") // ← e.g. "like", "liked", or "matches" putExtra("TYPE", "Likes")
// putExtra("USER_ID", userId.toString()) // ← if you also need a user id
} }
context.startActivity(intent) context.startActivity(intent)
onLikesClick() onLikesClick()
} }
) )
StatCard( StatCard(
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 = { onClick = {
val intent = Intent(context, LikeANdMatchActivity::class.java).apply { val intent = Intent(context, LikeANdMatchActivity::class.java).apply {
putExtra("TYPE", "Liked") // ← e.g. "like", "liked", or "matches" putExtra("TYPE", "Liked")
// putExtra("USER_ID", userId.toString()) // ← if you also need a user id
} }
context.startActivity(intent) context.startActivity(intent)
onLikesClick() onLikedClick()
} }
) )
// StatCard(
// number = "Top",
// label = "Match",
// numberColor = BrandMagenta,
// modifier = Modifier.weight(1f)
// )
StatCard( StatCard(
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 = { onClick = {
val intent = Intent(context, LikeANdMatchActivity::class.java).apply { val intent = Intent(context, LikeANdMatchActivity::class.java).apply {
putExtra("TYPE", "Matches") // ← e.g. "like", "liked", or "matches" putExtra("TYPE", "Matches")
// putExtra("USER_ID", userId.toString()) // ← if you also need a user id
} }
context.startActivity(intent) context.startActivity(intent)
onLikesClick() onMatchClick()
} }
) )
} }
Spacer(
modifier = Modifier.height(16.dp)
)
ProfileMediaGrid() Spacer(modifier = Modifier.height(20.dp))
Spacer( // ====================================================
modifier = Modifier.height(30.dp) // ALREADY POSTED / DYNAMIC POSTS SECTION
// ====================================================
Text(
text = "Posted",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = textDark,
modifier = Modifier.padding(horizontal = 16.dp)
) )
} Spacer(modifier = Modifier.height(10.dp))
}
ProfilePostsGrid(posts = profileResponse?.posts ?: emptyList())
// ============================================================ Spacer(modifier = Modifier.height(30.dp))
// PROFILE MEDIA GRID
// ============================================================
@Composable
private fun ProfileMediaGrid() {
Column(
modifier = Modifier.fillMaxWidth().padding(2.dp)
) {
// 6 rows × 3 columns = 18 images
repeat(6) { rowIndex ->
Row(
modifier = Modifier.fillMaxWidth()
) {
repeat(3) { columnIndex ->
val imageIndex = rowIndex * 3 + columnIndex
Image(
painter = painterResource(
id = R.drawable.locationpermissionbg
),
contentDescription = "Profile Image $imageIndex",
modifier = Modifier.weight(1f).aspectRatio(1f).padding(1.dp),
contentScale = ContentScale.Crop
)
}
}
}
} }
} }
// ============================================================ // ============================================================
// STAT CARD // STAT CARD
// ============================================================ // ============================================================
@Composable @Composable
private fun StatCard( private fun StatCard(
number: String, number: String,
label: String, label: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
numberColor: Color = textDark, numberColor: Color = textDark,
onClick:()->Unit onClick: () -> Unit
) { ) {
Card( Card(
modifier = modifier.clickable { onClick() }, modifier = modifier.clickable { onClick() },
shape = RoundedCornerShape(12.dp), shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = Color(0xFFFAFAFA)),
colors = CardDefaults.cardColors( border = CardDefaults.outlinedCardBorder().copy(width = 0.5.dp)
containerColor = Color(0xFFFAFAFA)
),
border =
CardDefaults.outlinedCardBorder()
.copy(width = 0.5.dp)
) { ) {
Column( Column(
modifier = Modifier.fillMaxWidth().padding(vertical = 5.dp), modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Text( Text(
@ -443,10 +396,7 @@ private fun StatCard(
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
color = numberColor color = numberColor
) )
Spacer( Spacer(modifier = Modifier.height(2.dp))
modifier = Modifier.height(2.dp)
)
Text( Text(
text = label, text = label,
fontSize = 11.sp, fontSize = 11.sp,
@ -458,39 +408,299 @@ private fun StatCard(
// ============================================================ // ============================================================
// PREVIEW // STAGGERED PROFILE POSTS GRID (Matches your custom Masonry layout)
// ============================================================
@Composable
private fun ProfilePostsGrid(posts: List<Post>) {
if (posts.isEmpty()) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(120.dp),
contentAlignment = Alignment.Center
) {
Text(text = "No posts yet", color = TextMuted, fontSize = 14.sp)
}
return
}
// Distribute posts into two columns
val columns = remember(posts) {
val col1 = mutableListOf<Post>()
val col2 = mutableListOf<Post>()
posts.forEachIndexed { index, post ->
if (index % 2 == 0) {
col1.add(post)
} else {
col2.add(post)
}
}
Pair(col1, col2)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(6.dp)
) {
// Column 1
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
columns.first.forEachIndexed { idx, post ->
val aspectRatio = if (idx % 2 == 0) 0.8f else 1.2f
MasonryProfilePostItem(post = post, aspectRatio = aspectRatio)
}
}
// Column 2
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
columns.second.forEachIndexed { idx, post ->
val aspectRatio = if (idx % 2 == 0) 1.2f else 0.8f
MasonryProfilePostItem(post = post, aspectRatio = aspectRatio)
}
}
}
}
@Composable
private fun MasonryProfilePostItem(post: Post, aspectRatio: Float) {
val context = LocalContext.current
val primaryMedia = post.media.firstOrNull()?.url
val isImage = post.post_type.equals("image", ignoreCase = true) || !primaryMedia.isNullOrEmpty()
Card(
shape = RoundedCornerShape(12.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp),
modifier = Modifier
.fillMaxWidth()
.aspectRatio(aspectRatio)
.clickable {
val intent = Intent(context, ShowPostingActivity::class.java).apply {
putExtra("Post_ID", post.id)
}
context.startActivity(intent)
}
) {
Box(
modifier = Modifier
.fillMaxSize()
) {
if (isImage && !primaryMedia.isNullOrEmpty()) {
AsyncImage(
model = primaryMedia,
contentDescription = "Post Image",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
// Dark Gradient Scrim for text readability over images
Box(
modifier = Modifier
.fillMaxSize()
.background(
Brush.verticalGradient(
colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.65f)),
startY = 100f
)
)
)
// Image metadata overlay
Column(
modifier = Modifier
.align(Alignment.BottomStart)
.padding(8.dp),
verticalArrangement = Arrangement.spacedBy(2.dp)
) {
if (!post.feeling.isNullOrBlank()) {
Text(
text = post.feeling,
color = Color.White.copy(alpha = 0.9f),
fontSize = 9.sp,
fontWeight = FontWeight.Bold
)
}
if (!post.content.isNullOrBlank()) {
Text(
text = post.content,
color = Color.White,
fontSize = 11.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
} else {
// Text / Status Card Design
Column(
modifier = Modifier
.fillMaxSize()
.padding(10.dp),
verticalArrangement = Arrangement.SpaceBetween
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(3.dp)
) {
Icon(
imageVector = when (post.post_type.lowercase()) {
"music" -> Icons.Default.MusicNote
else -> Icons.Default.TextSnippet
},
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(10.dp)
)
Text(
text = post.post_type.uppercase(),
color = Color.White.copy(alpha = 0.9f),
fontSize = 7.sp,
fontWeight = FontWeight.ExtraBold
)
}
}
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
if (!post.feeling.isNullOrBlank()) {
Surface(
shape = RoundedCornerShape(6.dp),
color = Color.White.copy(alpha = 0.2f)
) {
Text(
text = "feeling ${post.feeling}",
color = Color.White,
fontSize = 7.sp,
modifier = Modifier.padding(horizontal = 4.dp, vertical = 2.dp),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
Text(
text = post.content ?: "",
color = Color.White,
fontSize = 11.sp,
fontWeight = FontWeight.SemiBold,
maxLines = 3,
overflow = TextOverflow.Ellipsis,
lineHeight = 14.sp
)
}
// Footer Likes
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(2.dp)
) {
Icon(
imageVector = Icons.Default.Favorite,
contentDescription = null,
tint = Color.White.copy(alpha = 0.8f),
modifier = Modifier.size(9.dp)
)
Text(
text = "${post.like_count}",
color = Color.White.copy(alpha = 0.8f),
fontSize = 8.sp
)
}
}
}
}
}
}
// ============================================================
// PREVIEWS
// ============================================================ // ============================================================
@Preview(showBackground = true, showSystemUi = true) @Preview(showBackground = true, showSystemUi = true)
@Composable @Composable
fun MyProfileScreenSuccessPreview() { fun MyProfileScreenSuccessPreview() {
DattingAPPTheme { DattingAPPTheme {
// Mock UserResponse matching your backend model hierarchy // Mock UserResponse matching your backend model structure
val mockUserResponse = UserResponse( val mockUserResponse = UserResponse(
id = 1, id = 1,
email_or_phone = "alex.dev@example.com", email_or_phone = "elena@example.com",
status = "active", status = "active",
profile = ProfileData( profile = ProfileData(
full_name = "Alex Morgan", full_name = "Elena",
gender = "Male", gender = "Female",
birth_date = "1995-05-15", birth_date = "2002-04-12", // 24 years old
city = "New York", city = "New York",
county = "Manhattan", county = null,
country = "USA", country = "USA",
relationship_goal = "Long-term partnership", relationship_goal = "Long-term partnership",
bio = "Adventure seeker, dog lover, and amateur chef. Let's find a vibe!", bio = "\"Always looking for the best flat white in the city. Amateur photographer, professional dog petter...\"",
profile_image_url = "https://picsum.photos/300/300", profile_image_url = "https://picsum.photos/300/300",
video_url = null, video_url = null,
interests = listOf("Hiking", "Cooking", "Photography") interests = listOf("Photography", "Dogs", "Coffee Shop Lover", "Art Galleries")
), ),
created_at = "2026-01-01", created_at = "2026-01-01",
posts = emptyList(), posts = listOf(
Post(
id = 101,
content = "Enjoying the art exhibition!",
post_type = "image",
content_format = "standard",
feeling = "Happy",
location = null,
author = Author(1, "Elena", "New York", "USA", 98),
like_count = 12,
comment_count = 3,
liked_by_me = false,
media = listOf(
Media(
1,
"image",
"image/jpeg",
1024,
1,
"https://picsum.photos/400/400?random=1"
),
Media(2, "image", "image/jpeg", 1024, 2, "https://picsum.photos/400/400?random=2"),
Media(3, "image", "image/jpeg", 1024, 3, "https://picsum.photos/400/400?random=3")
),
created_at = "2026-02-10"
),
Post(
id = 102,
content = "Weekend coffee vibes ☕",
post_type = "image",
content_format = "standard",
feeling = "Relaxed",
location = null,
author = Author(1, "Elena", "New York", "USA", 98),
like_count = 25,
comment_count = 5,
liked_by_me = false,
media = listOf(
Media(4, "image", "image/jpeg", 1024, 1, "https://picsum.photos/400/400?random=4"),
Media(5, "image", "image/jpeg", 1024, 2, "https://picsum.photos/400/400?random=5"),
Media(6, "image", "image/jpeg", 1024, 3, "https://picsum.photos/400/400?random=6")
),
created_at = "2026-02-12"
)
),
liked_users = emptyList(), liked_users = emptyList(),
liked_by_users = emptyList(), liked_by_users = emptyList(),
matches = emptyList(), matches = emptyList(),
liked_users_count = 24, liked_users_count = 42,
liked_by_users_count = 12, liked_by_users_count = 128,
matches_count = 8 matches_count = 18
) )
MyProfileScreen( MyProfileScreen(
@ -499,4 +709,4 @@ fun MyProfileScreenSuccessPreview() {
errorMessage = null errorMessage = null
) )
} }
} }

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

@ -535,12 +535,12 @@ sealed interface LikedUiState {
class LikedViewModel : ViewModel() { class LikedViewModel : ViewModel() {
// ✅ three separate states, one per list type // ✅ three separate states, one per list type
private val _likesUiState = MutableStateFlow<LikedUiState>(LikedUiState.Idle) // users who liked ME private val _likedUiState = MutableStateFlow<LikedUiState>(LikedUiState.Idle) // users who liked ME
val likesUiState: StateFlow<LikedUiState> = _likesUiState.asStateFlow()
private val _likedUiState = MutableStateFlow<LikedUiState>(LikedUiState.Idle) // users I liked
val likedUiState: StateFlow<LikedUiState> = _likedUiState.asStateFlow() val likedUiState: StateFlow<LikedUiState> = _likedUiState.asStateFlow()
private val _likesUiState = MutableStateFlow<LikedUiState>(LikedUiState.Idle) // users I liked
val likesUiState: StateFlow<LikedUiState> = _likesUiState.asStateFlow()
private val _matchesUiState = MutableStateFlow<LikedUiState>(LikedUiState.Idle) // matches private val _matchesUiState = MutableStateFlow<LikedUiState>(LikedUiState.Idle) // matches
val matchesUiState: StateFlow<LikedUiState> = _matchesUiState.asStateFlow() val matchesUiState: StateFlow<LikedUiState> = _matchesUiState.asStateFlow()

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

@ -11,7 +11,13 @@ class TokenManager private constructor(context: Context) {
Context.MODE_PRIVATE Context.MODE_PRIVATE
) )
fun saveTokens(accessToken: String, refreshToken: String?,userId: Int,currenUserName:String?,profile_image_url:String?) { private val prefProfile =
context.applicationContext.getSharedPreferences(
PREF_NAME,
Context.MODE_PRIVATE
)
fun saveTokens(accessToken: String, refreshToken: String?) {
val cleanToken = accessToken.trim() val cleanToken = accessToken.trim()
.replace( .replace(
Regex("^Bearer\\s+", RegexOption.IGNORE_CASE), Regex("^Bearer\\s+", RegexOption.IGNORE_CASE),
@ -22,6 +28,14 @@ class TokenManager private constructor(context: Context) {
val saved = preferences.edit() val saved = preferences.edit()
.putString(KEY_ACCESS_TOKEN, cleanToken) .putString(KEY_ACCESS_TOKEN, cleanToken)
.putString(KEY_REFRESH_TOKEN, refreshToken) .putString(KEY_REFRESH_TOKEN, refreshToken)
.commit()
// Do not log the token itself.
Log.d("TokenManager", "Tokens saved: $saved")
}
fun saveProfile(userId: Int,currenUserName:String?,profile_image_url:String?) {
val saved = preferences.edit()
.putInt("user_id", userId) .putInt("user_id", userId)
.putString("currenUserName", currenUserName) .putString("currenUserName", currenUserName)
.putString("profile_image_url", profile_image_url) .putString("profile_image_url", profile_image_url)
@ -33,12 +47,13 @@ class TokenManager private constructor(context: Context) {
fun getUserId(): Int { fun getUserId(): Int {
return preferences.getInt("user_id", -1) return prefProfile.getInt("user_id", -1)
} }
fun currentUserName(): String? = preferences.getString("full_name", null) fun currentUserName(): String? = prefProfile.getString("currenUserName", null)
fun currentProfileImage(): String? = preferences.getString("profile_image_url", null) fun currentProfileImage(): String? = prefProfile.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)

8
app/src/main/java/com/example/dattingapp/registration/RegistrationScreen.kt

@ -80,6 +80,7 @@ import android.content.pm.PackageManager
import android.location.LocationManager import android.location.LocationManager
import android.os.Looper import android.os.Looper
import android.Manifest import android.Manifest
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -100,6 +101,8 @@ import androidx.compose.material.icons.filled.LocationOn
// Permission check // Permission check
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.example.dattingapp.homes.networkandapi.TokenManager
class RegistrationScreen : ComponentActivity() { class RegistrationScreen : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge() enableEdgeToEdge()
@ -156,7 +159,12 @@ fun RegistrationFlowContainer(
when (val state = uiState) { when (val state = uiState) {
is RegistrationUiState.Success -> { is RegistrationUiState.Success -> {
Toast.makeText(context, "Registration Successful!", Toast.LENGTH_SHORT).show() Toast.makeText(context, "Registration Successful!", Toast.LENGTH_SHORT).show()
val tokenManager = TokenManager.getInstance(context)
tokenManager.saveTokens(
accessToken = state.response.accessToken,
refreshToken = state.response.refreshToken
)
// Navigate to UploadProfileImageActivity and finish flow // Navigate to UploadProfileImageActivity and finish flow
val intent = Intent(context, UploadProfileImageActivity::class.java).apply { val intent = Intent(context, UploadProfileImageActivity::class.java).apply {
putExtra("ACCESS_TOKEN", state.response.accessToken) putExtra("ACCESS_TOKEN", state.response.accessToken)

Loading…
Cancel
Save