This is a dating app
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

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 = {}
)
}
}