Browse Source

update meet

master
osman160128 4 weeks ago
parent
commit
5b6ce5f751
  1. 68
      app/src/main/java/com/example/dattingapp/homes/MeetScreen.kt
  2. 58
      app/src/main/java/com/example/dattingapp/homes/MyProfile.kt
  3. 7
      app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeApiService.kt
  4. 21
      app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeDataClass.kt
  5. 71
      app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeViewModelClass.kt
  6. 4
      app/src/main/java/com/example/dattingapp/registration/AuthApiService.kt

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

@ -1,6 +1,8 @@
package com.example.dattingapp.homes
import android.content.Context
import android.util.Log
import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
@ -21,11 +23,15 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
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 coil.request.ImageRequest
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.homes.networkandapi.TokenManager
import com.example.dattingapp.ui.theme.BrandPink
import com.example.dattingapp.ui.theme.BrandPurple
@ -43,9 +49,10 @@ fun MeetDiscoveryScreen(
onSearchClick: () -> Unit = {},
onSuperLikeClick: (MeetUserProfile) -> Unit = {},
onRejectClick: (MeetUserProfile) -> Unit = {},
onLikeClick: (MeetUserProfile) -> Unit = {}
onLikeClick: (MeetUserProfile) -> Unit = {},
swipeViewModel: SwipeViewModel = viewModel()
) {
// Index tracking current active user profile (starts at 0)
val context = LocalContext.current
var currentIndex by remember { mutableIntStateOf(0) }
Box(
@ -121,7 +128,6 @@ fun MeetDiscoveryScreen(
}
profiles.isNotEmpty() && currentIndex < profiles.size -> {
// Current profile starting at index 0
val currentProfile = profiles[currentIndex]
ProfileCardItem(
@ -131,10 +137,12 @@ fun MeetDiscoveryScreen(
currentIndex++
},
onReject = {
handleSwipeAction(context,direction="pass", currentProfile, swipeViewModel)
onRejectClick(currentProfile)
currentIndex++
},
onLike = {
handleSwipeAction(context,direction="like", currentProfile, swipeViewModel)
onLikeClick(currentProfile)
currentIndex++
}
@ -161,20 +169,22 @@ private fun ProfileCardItem(
onReject: () -> Unit,
onLike: () -> Unit
) {
// Extract primary photo URL from media list
val context = LocalContext.current
val scope = rememberCoroutineScope()
// Reset drag offset whenever the profile changes
val offsetX = remember(profile) { Animatable(0f) }
val photoUrl = remember(profile) {
profile.media
?.firstOrNull { it.mediaType.equals("image", ignoreCase = true) || it.url != null }
?.fullMediaUrl
}
Log.d("photourl", "$photoUrl")
// Helper to calculate age from birth_date string (formatted e.g. "YYYY-MM-DD")
val age = remember(profile.birthDate) {
calculateAge(profile.birthDate)
}
// Location display formatting
val locationText = remember(profile) {
val loc = listOfNotNull(profile.city, profile.country).joinToString(", ")
val dist = profile.distanceKm?.let { "${it.toInt()} km away" }
@ -186,10 +196,8 @@ private fun ProfileCardItem(
.fillMaxSize()
.clip(RoundedCornerShape(20.dp))
) {
// BACKEND PROFILE IMAGE
val context = LocalContext.current
val token = remember(context) {
TokenManager.getInstance(context).getAccessToken()
TokenManager.getInstance(context).getAccessToken()
}
AsyncImage(
@ -197,7 +205,6 @@ private fun ProfileCardItem(
.data(photoUrl)
.apply {
if (!token.isNullOrBlank()) {
// Formatting matches "Bearer <token>" or raw token based on backend preference
val authHeader = if (token.startsWith("Bearer ", ignoreCase = true)) token else "Bearer $token"
addHeader("Authorization", authHeader)
}
@ -295,7 +302,6 @@ private fun ProfileCardItem(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
// Name & Age
val displayName = profile.fullName ?: "User"
val nameAndAge = if (age != null) "$displayName, $age" else displayName
@ -306,7 +312,6 @@ private fun ProfileCardItem(
color = Color.White
)
// Compatibility / Match Percentage Badge
profile.compatibilityPercentage?.let { match ->
Surface(
shape = RoundedCornerShape(10.dp),
@ -336,7 +341,6 @@ private fun ProfileCardItem(
Spacer(modifier = Modifier.height(4.dp))
// Location & Distance
if (locationText.isNotBlank()) {
Row(
verticalAlignment = Alignment.CenterVertically,
@ -356,7 +360,6 @@ private fun ProfileCardItem(
}
}
// Bio
profile.bio?.takeIf { it.isNotBlank() }?.let { bio ->
Spacer(modifier = Modifier.height(8.dp))
Text(
@ -370,16 +373,43 @@ private fun ProfileCardItem(
}
}
// Utility to calculate age from date string (e.g. "1999-05-15")
//Helper to calculate age from birth date string (e.g., "1999-05-15")
private fun calculateAge(birthDateStr: String?): Int? {
if (birthDateStr.isNull_or_blank()) return null
if (birthDateStr.isNullOrBlank()) return null
return try {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val birthDate = LocalDate.parse(birthDateStr?.take(10), formatter)
val birthDate = LocalDate.parse(birthDateStr.take(10), formatter)
Period.between(birthDate, LocalDate.now()).years
} catch (e: Exception) {
null
}
}
private fun String?.isNull_or_blank(): Boolean = this == null || this.trim().isEmpty()
// Helper method for reject swipe network call
private fun handleSwipeAction(
context: Context,
direction:String,
profile: MeetUserProfile,
swipeViewModel: SwipeViewModel
) {
val targetUserId = profile.id
val swipeRequest = SwipeRequest(targetUserId, direction)
swipeViewModel.sendSwipeResponse(context, swipeRequest)
}
@Preview(
name = "Discovery Screen - Active Card",
showBackground = true,
widthDp = 390,
heightDp = 844
)
@Composable
fun MeetDiscoveryScreenPreview() {
MaterialTheme {
MeetDiscoveryScreen(
profiles = emptyList(),
isLoading = false,
errorMessage = null
)
}
}

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

@ -47,7 +47,6 @@ import com.example.dattingapp.ui.theme.TextMuted
@Composable
fun MyProfileScreen(
profile: MyProfileResponse?, // Change from MyProfileData? to MyProfileResponse?
isLoading: Boolean,
errorMessage: String?,
@ -57,39 +56,25 @@ fun MyProfileScreen(
onSettingsClick: () -> Unit = {}
) {
// ========================================================
// FULL SCREEN SCROLL
// ========================================================
val scrollState = rememberScrollState()
Column(
modifier = Modifier.fillMaxSize().background(Color.White).verticalScroll(scrollState)) {
// ====================================================
// HEADER
// ====================================================
Box(
modifier = Modifier.fillMaxWidth().height(260.dp)
) {
// =================================================
// COVER IMAGE
// =================================================
Image(
painter = painterResource(
id = R.drawable.locationpermissionbg
),
painter = painterResource(id = R.drawable.locationpermissionbg),
contentDescription = "Cover Banner",
modifier = Modifier.fillMaxWidth().height(200.dp),
contentScale = ContentScale.Crop
)
@ -179,7 +164,7 @@ fun MyProfileScreen(
) {
Text(
text = "Alex, 28",
text = profile?.profile?.fullName?:"",
fontSize = 22.sp,
fontWeight = FontWeight.Bold,
color = BrandMagenta
@ -222,7 +207,7 @@ fun MyProfileScreen(
Text(
text = "Adventure seeker, dog lover, and amateur chef. Let's find a vibe!",
text = profile?.profile?.bio?:"",
fontSize = 13.sp,
color = TextMuted,
lineHeight = 18.sp
@ -230,9 +215,7 @@ fun MyProfileScreen(
}
Spacer(
modifier = Modifier.height(12.dp)
)
Spacer(modifier = Modifier.height(12.dp))
// ====================================================
// PREMIUM BANNER
// ====================================================
@ -443,3 +426,36 @@ private fun StatCard(
// ============================================================
// PREVIEW
// ============================================================
// 1. Success State Preview (With Mock User Profile Data)
@Preview(showBackground = true, showSystemUi = true)
@Composable
fun MyProfileScreenSuccessPreview() {
DattingAPPTheme {
// Create dummy profile response matching your backend structure
val mockProfileResponse = MyProfileResponse(
id = 1,
emailOrPhone = "alex.dev@example.com",
status = "active",
profile = com.example.dattingapp.homes.networkandapi.UserProfileDetails(
fullName = "Alex",
gender = "Male",
city = "New York",
country = "USA",
bio = "Adventure seeker, dog lover, and amateur chef. Let's find a vibe!",
profileImageUrl = "https://picsum.photos/300/300", // Sample remote URL
videoUrl = null
),
createdAt = "2026-01-01"
)
MyProfileScreen(
profile = mockProfileResponse,
isLoading = false,
errorMessage = null,
onRetry = {},
onEditClick = {},
onUpgradeClick = {},
onSettingsClick = {}
)
}
}

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

@ -1,7 +1,9 @@
package com.example.dattingapp.homes.networkandapi
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Query
interface HomeApiService {
@ -14,4 +16,9 @@ interface HomeApiService {
@GET("api/v1/users/me")
suspend fun getCurrentUserProfile(): Response<MyProfileResponse>
@POST("api/v1/meet/swipes")
suspend fun recordSwipe(
@Body request: SwipeRequest
): Response<SwipeResponse>
}

21
app/src/main/java/com/example/dattingapp/homes/networkandapi/HomeDataClass.kt

@ -91,3 +91,24 @@ data class MediaItem(
val fullMediaUrl: String?
get() = resolveMediaUrl(url)
}
//swife data
// Request payload
data class SwipeRequest(
@SerializedName("target_user_id")
val targetUserId: Int,
@SerializedName("direction")
val direction: String // "like", "dislike", "super_like"
)
data class SwipeResponse(
@SerializedName("direction")
val direction: String?,
@SerializedName("matched")
val matched: Boolean = false,
@SerializedName("match_id")
val matchId: Int? = null
)

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

@ -93,6 +93,8 @@ class MeetViewModel : ViewModel() {
// Myprofle ViewModel---------------------------------------------------------------------------------------
sealed interface MyProfileUiState {
object Idle : MyProfileUiState
object Loading : MyProfileUiState
@ -153,3 +155,72 @@ class MyProfileViewModel : ViewModel() {
}
}
}
// Swife ViewModel---------------------------------------------------------------------------------------
sealed interface SwipeUiState {
object Idle : SwipeUiState
object Loading : SwipeUiState
data class Success(val swipedata: SwipeResponse) : SwipeUiState // Ensure this is MyProfileData, not MyProfileResponse
data class Error(val message: String) : SwipeUiState
}
class SwipeViewModel : ViewModel() {
private val _swipeUiState = MutableStateFlow<SwipeUiState>(SwipeUiState.Idle)
val swipeUiState: StateFlow<SwipeUiState> = _swipeUiState.asStateFlow()
fun sendSwipeResponse(context: Context,swipeRequest: SwipeRequest) {
if (_swipeUiState.value is SwipeUiState.Loading) return
val appContext = context.applicationContext
viewModelScope.launch {
_swipeUiState.value = SwipeUiState.Loading
if (TokenManager.getInstance(appContext).getAccessToken().isNullOrBlank()) {
Log.e("MeetApi", "No access token in user_tokens preferences")
_swipeUiState.value = SwipeUiState.Error("No access token found. Please log in again.")
return@launch
}
try {
val response = HomeNetworkModule
.getApiService(appContext)
.recordSwipe(swipeRequest)
val statusCode = response.code()
Log.d("swipe code", "Response Code: $statusCode")
if (!response.isSuccessful) {
response.errorBody()?.close()
val message = when (response.code()) {
401 -> "Authentication failed. Please log in again."
403 -> "The server refused access to these profiles."
404 -> "Meet endpoint was not found."
else -> "Request failed: HTTP ${response.code()}."
}
_swipeUiState.value = SwipeUiState.Error(message)
return@launch
}
// FIX: Use response.body() directly as profileData
val swipeData = response.body()
if ( swipeData!= null) {
_swipeUiState.value = SwipeUiState.Success(swipeData)
} else {
_swipeUiState.value = SwipeUiState.Error("Profile data is empty.")
}
} catch (e: Exception) {
Log.e("MeetApi", "Unexpected request failure", e)
_swipeUiState.value = SwipeUiState.Error("Request failed: ${e.javaClass.simpleName}")
}
}
}
}

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

@ -9,7 +9,9 @@ import retrofit2.http.Part
interface AuthApiService {
@POST("/api/v1/auth/register")
suspend fun registerUser(@Body request: RegisterRequest): Response<RegisterResponse>
suspend fun registerUser(
@Body request: RegisterRequest
): Response<RegisterResponse>
@Multipart
@POST("api/v1/users/me/image")

Loading…
Cancel
Save