You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
510 lines
19 KiB
510 lines
19 KiB
package com.example.dattingapp
|
|
|
|
import android.content.Context
|
|
import android.os.Bundle
|
|
import androidx.activity.ComponentActivity
|
|
import androidx.activity.compose.setContent
|
|
import androidx.activity.enableEdgeToEdge
|
|
import androidx.compose.foundation.Image
|
|
import androidx.compose.foundation.background
|
|
import androidx.compose.foundation.border
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
import androidx.compose.foundation.layout.Box
|
|
import androidx.compose.foundation.layout.Column
|
|
import androidx.compose.foundation.layout.Row
|
|
import androidx.compose.foundation.layout.Spacer
|
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
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)
|
|
|
|
}
|
|
is FriendProfileUiState.Error -> {
|
|
// Show error message
|
|
}
|
|
FriendProfileUiState.Idle -> {
|
|
// Idle state
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@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()
|
|
.background(Color.White)
|
|
.verticalScroll(rememberScrollState())
|
|
) {
|
|
|
|
Box(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.height(250.dp)
|
|
){
|
|
Image(
|
|
painter = painterResource(id=R.drawable.gallarypermissionbg),
|
|
contentDescription = "cover photo",
|
|
modifier = Modifier.fillMaxWidth().height(200.dp)
|
|
.clip(RoundedCornerShape(bottomStart = 10.dp, bottomEnd = 20.dp)),
|
|
contentScale = ContentScale.Crop
|
|
)
|
|
|
|
Row(
|
|
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 25.dp),
|
|
horizontalArrangement = Arrangement.SpaceBetween,
|
|
verticalAlignment = Alignment.CenterVertically
|
|
){
|
|
IconButton(
|
|
onClick=onBackClick,
|
|
modifier = Modifier.size(20.dp).clip(CircleShape)
|
|
|
|
) {
|
|
Icon(
|
|
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
|
contentDescription = "back button",
|
|
)
|
|
}
|
|
IconButton(
|
|
onClick=onBackClick,
|
|
modifier = Modifier.size(20.dp).clip(CircleShape)
|
|
|
|
) {
|
|
Icon(
|
|
imageVector = Icons.Default.MoreHoriz,
|
|
contentDescription = "back button",
|
|
)
|
|
}
|
|
|
|
}
|
|
// 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)
|
|
.align(Alignment.BottomStart)
|
|
.size(100.dp)
|
|
.clip(CircleShape)
|
|
.border(4.dp, Color.White, CircleShape),
|
|
contentScale = ContentScale.Crop
|
|
)
|
|
}
|
|
|
|
Spacer(modifier = Modifier.height(12.dp))
|
|
|
|
Column(modifier = Modifier
|
|
.fillMaxWidth()
|
|
.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 = nameAndAge,
|
|
fontSize = 24.sp,
|
|
fontWeight = FontWeight.Bold,
|
|
color = textDark
|
|
)
|
|
Text(
|
|
text = "24",
|
|
fontSize = 24.sp,
|
|
fontWeight = FontWeight.Bold,
|
|
color = textDark
|
|
)
|
|
Surface(
|
|
shape = RoundedCornerShape(12.dp),
|
|
color = BadgePink
|
|
) {
|
|
Row(
|
|
modifier = Modifier.padding(horizontal = 8.dp, vertical = 3.dp),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
|
) {
|
|
Icon(
|
|
imageVector = Icons.Default.Favorite,
|
|
contentDescription = null,
|
|
tint = Color.White,
|
|
modifier = Modifier.size(10.dp)
|
|
)
|
|
Text(
|
|
text = "98% Match",
|
|
color = Color.White,
|
|
fontSize = 10.sp,
|
|
fontWeight = FontWeight.Bold
|
|
)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
Row(
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
|
) {
|
|
Icon(
|
|
imageVector = Icons.Outlined.LocationOn,
|
|
contentDescription = null,
|
|
tint = Color.Gray,
|
|
modifier = Modifier.size(14.dp)
|
|
)
|
|
Text(
|
|
text = "2 miles away",
|
|
fontSize = 12.sp,
|
|
color = Color.Gray
|
|
)
|
|
Text(
|
|
text = "•",
|
|
fontSize = 12.sp,
|
|
color = Color.Gray
|
|
)
|
|
Box(
|
|
modifier = Modifier
|
|
.size(6.dp)
|
|
.clip(CircleShape)
|
|
.background(AccentPurple)
|
|
)
|
|
Text(
|
|
text = "Active 1 hour ago",
|
|
fontSize = 12.sp,
|
|
color = Color.Gray
|
|
)
|
|
}
|
|
|
|
|
|
Surface(
|
|
onClick = {
|
|
// Handle bio click action here (e.g., expand bio or navigate)
|
|
},
|
|
modifier = Modifier.fillMaxWidth(),
|
|
shape = RoundedCornerShape(12.dp),
|
|
color = Color(0xFFF9F9F9),
|
|
shadowElevation = 1.dp
|
|
) {
|
|
val displayBio= friendProfileData.bio ?: ""
|
|
Text(
|
|
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 {
|
|
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 = {}
|
|
)
|
|
}
|
|
}
|
|
|
|
|